Compare commits

...

49 Commits

Author SHA1 Message Date
comfyanonymous
ef85058e97 Bump ComfyUI version to v0.3.13 2025-01-29 16:07:12 -05:00
comfyanonymous
f9230bd357 Update the python version in some workflows. 2025-01-29 15:54:13 -05:00
comfyanonymous
537c27cbf3 Bump default cuda version in standalone package to 126. 2025-01-29 08:13:33 -05:00
comfyanonymous
6ff2e4d550 Remove logging call added in last commit.
This is called before the logging is set up so it messes up some things.
2025-01-29 08:08:01 -05:00
filtered
222f48c0f2 Allow changing folder_paths.base_path via command line argument. (#6600)
* Reimpl. CLI arg directly inside folder_paths.

* Update tests to use CLI arg mocking.

* Revert last-minute refactor.

* Fix test state polution.
2025-01-29 08:06:28 -05:00
comfyanonymous
13fd4d6e45 More friendly error messages for corrupted safetensors files. 2025-01-28 09:41:09 -05:00
Bradley Reynolds
1210d094c7 Convert latents_ubyte to 8-bit unsigned int before converting to CPU (#6300)
* Convert latents_ubyte to 8-bit unsigned int before converting to CPU

* Only convert to unint8 if directml_enabled
2025-01-28 08:22:54 -05:00
comfyanonymous
255edf2246 Lower minimum ratio of loaded weights on Nvidia. 2025-01-27 05:26:51 -05:00
comfyanonymous
4f011b9a00 Better CLIPTextEncode error when clip input is None. 2025-01-26 06:04:57 -05:00
comfyanonymous
67feb05299 Remove redundant code. 2025-01-25 19:04:53 -05:00
comfyanonymous
6d21740346 Print ComfyUI version. 2025-01-25 15:03:57 -05:00
comfyanonymous
7fbf4b72fe Update nightly pytorch ROCm command in Readme. 2025-01-24 06:15:54 -05:00
comfyanonymous
14ca5f5a10 Remove useless code. 2025-01-24 06:15:54 -05:00
filtered
ce557cfb88 Remove redundant code (#6576) 2025-01-23 05:57:41 -05:00
comfyanonymous
96e2a45193 Remove useless code. 2025-01-23 05:56:23 -05:00
Chenlei Hu
dfa2b6d129 Remove unused function lcm in conds.py (#6572) 2025-01-23 05:54:09 -05:00
Terry Jia
f3566f0894 remove some params from load 3d node (#6436) 2025-01-22 17:23:51 -05:00
Chenlei Hu
ca69b41cee Add utils/ to web server developer codeowner (#6570) 2025-01-22 17:16:54 -05:00
Chenlei Hu
a058f52090 [i18n] Add /i18n endpoint to provide all custom node translations (#6558)
* [i18n] Add /i18n endpoint to provide all custom node translations

* Sort glob result for deterministic ordering

* Update comment
2025-01-22 17:15:45 -05:00
comfyanonymous
d6bbe8c40f Remove support for python 3.8. 2025-01-22 17:04:30 -05:00
comfyanonymous
a7fe0a94de Refactor and fixes for video latents. 2025-01-22 06:37:46 -05:00
chaObserv
e857dd48b8 Add gradient estimation sampler (#6554) 2025-01-22 05:29:40 -05:00
comfyanonymous
d303cb5341 Add missing case to CLIPLoader. 2025-01-21 08:57:04 -05:00
comfyanonymous
fb2ad645a3 Add FluxDisableGuidance node to disable using the guidance embed. 2025-01-20 14:50:24 -05:00
comfyanonymous
d8a7a32779 Cleanup old TODO. 2025-01-20 03:44:13 -05:00
comfyanonymous
a00e1489d2 LatentBatch fix for video latents 2025-01-19 06:02:14 -05:00
Sergii Dymchenko
ebf038d4fa Use torch.special.expm1 (#6388)
* Use `torch.special.expm1`

This function provides greater precision than `exp(x) - 1` for small values of `x`.

Found with TorchFix https://github.com/pytorch-labs/torchfix/

* Use non-alias
2025-01-19 04:54:32 -05:00
Comfy Org PR Bot
b4de04a1c1 Update frontend to v1.7.14 (#6522)
Co-authored-by: huchenlei <20929282+huchenlei@users.noreply.github.com>
2025-01-18 21:43:37 -05:00
catboxanon
b1a02131c9 Remove comfy.samplers self-import (#6506) 2025-01-18 17:49:51 -05:00
catboxanon
3a3910f91d PromptServer: Return 400 for empty filename param (#6504) 2025-01-18 17:47:33 -05:00
comfyanonymous
507199d9a8 Uni pc sampler now works with audio and video models. 2025-01-18 05:27:58 -05:00
comfyanonymous
2f3ab40b62 Add warning when using old pytorch versions. 2025-01-17 18:47:27 -05:00
comfyanonymous
7fc3ccdcc2 Add that nvidia cosmos is supported to the README. 2025-01-16 21:17:18 -05:00
comfyanonymous
55add50220 Bump ComfyUI version to v0.3.12 2025-01-16 18:11:57 -05:00
comfyanonymous
0aa2368e46 Fix some cosmos fp8 issues. 2025-01-16 17:45:37 -05:00
comfyanonymous
cca96a85ae Fix cosmos VAE failing with videos longer than 121 frames. 2025-01-16 16:30:06 -05:00
comfyanonymous
619b8cde74 Bump ComfyUI version to 0.3.11 2025-01-16 14:54:48 -05:00
comfyanonymous
31831e6ef1 Code refactor. 2025-01-16 07:23:54 -05:00
comfyanonymous
88ceb28e20 Tweak hunyuan memory usage factor. 2025-01-16 06:31:03 -05:00
comfyanonymous
23289a6a5c Clean up some debug lines. 2025-01-16 04:24:39 -05:00
comfyanonymous
9d8b6c1f46 More accurate memory estimation for cosmos and hunyuan video. 2025-01-16 03:48:40 -05:00
comfyanonymous
6320d05696 Slightly lower hunyuan video memory usage. 2025-01-16 00:23:01 -05:00
comfyanonymous
25683b5b02 Lower cosmos diffusion model memory usage. 2025-01-15 23:46:42 -05:00
comfyanonymous
4758fb64b9 Lower cosmos VAE memory usage by a bit. 2025-01-15 22:57:52 -05:00
comfyanonymous
008761166f Optimize first attention block in cosmos VAE. 2025-01-15 21:48:46 -05:00
comfyanonymous
bfd5dfd611 3.13 doesn't work yet. 2025-01-15 20:32:44 -05:00
comfyanonymous
55ade36d01 Remove python 3.8 from test-build workflow. 2025-01-15 20:24:55 -05:00
comfyanonymous
2e20e399ea Add minimum numpy version to requirements.txt 2025-01-15 20:19:56 -05:00
comfyanonymous
3baf92d120 CosmosImageToVideoLatent batch_size now does something. 2025-01-15 17:19:59 -05:00
78 changed files with 50488 additions and 21436 deletions

View File

@@ -12,7 +12,7 @@ on:
description: 'CUDA version'
required: true
type: string
default: "124"
default: "126"
python_minor:
description: 'Python minor version'
required: true

View File

@@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
@@ -28,4 +28,4 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements.txt

View File

@@ -18,7 +18,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.12'
- name: Install requirements
run: |
python -m pip install --upgrade pip

View File

@@ -17,7 +17,7 @@ on:
description: 'cuda version'
required: true
type: string
default: "124"
default: "126"
python_minor:
description: 'python minor version'

View File

@@ -7,7 +7,7 @@ on:
description: 'cuda version'
required: true
type: string
default: "124"
default: "126"
python_minor:
description: 'python minor version'

View File

@@ -15,6 +15,7 @@
# Python web server
/api_server/ @yoland68 @robinjhuang @huchenlei @webfiltered @pythongosssss @ltdrdata
/app/ @yoland68 @robinjhuang @huchenlei @webfiltered @pythongosssss @ltdrdata
/utils/ @yoland68 @robinjhuang @huchenlei @webfiltered @pythongosssss @ltdrdata
# Frontend assets
/web/ @huchenlei @webfiltered @pythongosssss @yoland68 @robinjhuang

View File

@@ -52,6 +52,7 @@ This ui will let you design and execute advanced stable diffusion pipelines usin
- [Mochi](https://comfyanonymous.github.io/ComfyUI_examples/mochi/)
- [LTX-Video](https://comfyanonymous.github.io/ComfyUI_examples/ltxv/)
- [Hunyuan Video](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_video/)
- [Nvidia Cosmos](https://comfyanonymous.github.io/ComfyUI_examples/cosmos/)
- [Stable Audio](https://comfyanonymous.github.io/ComfyUI_examples/audio/)
- Asynchronous Queue system
- Many optimizations: Only re-executes the parts of the workflow that changes between executions.
@@ -153,9 +154,9 @@ AMD users can install rocm and pytorch with pip if you don't have it already ins
```pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2```
This is the command to install the nightly with ROCm 6.2 which might have some performance improvements:
This is the command to install the nightly with ROCm 6.3 which might have some performance improvements:
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.2.4```
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.3```
### Intel GPUs (Windows and Linux)

View File

@@ -4,12 +4,93 @@ import os
import folder_paths
import glob
from aiohttp import web
import json
import logging
from functools import lru_cache
from utils.json_util import merge_json_recursive
# Extra locale files to load into main.json
EXTRA_LOCALE_FILES = [
"nodeDefs.json",
"commands.json",
"settings.json",
]
def safe_load_json_file(file_path: str) -> dict:
if not os.path.exists(file_path):
return {}
try:
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
except json.JSONDecodeError:
logging.error(f"Error loading {file_path}")
return {}
class CustomNodeManager:
"""
Placeholder to refactor the custom node management features from ComfyUI-Manager.
Currently it only contains the custom workflow templates feature.
"""
@lru_cache(maxsize=1)
def build_translations(self):
"""Load all custom nodes translations during initialization. Translations are
expected to be loaded from `locales/` folder.
The folder structure is expected to be the following:
- custom_nodes/
- custom_node_1/
- locales/
- en/
- main.json
- commands.json
- settings.json
returned translations are expected to be in the following format:
{
"en": {
"nodeDefs": {...},
"commands": {...},
"settings": {...},
...{other main.json keys}
}
}
"""
translations = {}
for folder in folder_paths.get_folder_paths("custom_nodes"):
# Sort glob results for deterministic ordering
for custom_node_dir in sorted(glob.glob(os.path.join(folder, "*/"))):
locales_dir = os.path.join(custom_node_dir, "locales")
if not os.path.exists(locales_dir):
continue
for lang_dir in glob.glob(os.path.join(locales_dir, "*/")):
lang_code = os.path.basename(os.path.dirname(lang_dir))
if lang_code not in translations:
translations[lang_code] = {}
# Load main.json
main_file = os.path.join(lang_dir, "main.json")
node_translations = safe_load_json_file(main_file)
# Load extra locale files
for extra_file in EXTRA_LOCALE_FILES:
extra_file_path = os.path.join(lang_dir, extra_file)
key = extra_file.split(".")[0]
json_data = safe_load_json_file(extra_file_path)
if json_data:
node_translations[key] = json_data
if node_translations:
translations[lang_code] = merge_json_recursive(
translations[lang_code], node_translations
)
return translations
def add_routes(self, routes, webapp, loadedModules):
@routes.get("/workflow_templates")
@@ -18,17 +99,36 @@ class CustomNodeManager:
files = [
file
for folder in folder_paths.get_folder_paths("custom_nodes")
for file in glob.glob(os.path.join(folder, '*/example_workflows/*.json'))
for file in glob.glob(
os.path.join(folder, "*/example_workflows/*.json")
)
]
workflow_templates_dict = {} # custom_nodes folder name -> example workflow names
workflow_templates_dict = (
{}
) # custom_nodes folder name -> example workflow names
for file in files:
custom_nodes_name = os.path.basename(os.path.dirname(os.path.dirname(file)))
custom_nodes_name = os.path.basename(
os.path.dirname(os.path.dirname(file))
)
workflow_name = os.path.splitext(os.path.basename(file))[0]
workflow_templates_dict.setdefault(custom_nodes_name, []).append(workflow_name)
workflow_templates_dict.setdefault(custom_nodes_name, []).append(
workflow_name
)
return web.json_response(workflow_templates_dict)
# Serve workflow templates from custom nodes.
for module_name, module_dir in loadedModules:
workflows_dir = os.path.join(module_dir, 'example_workflows')
workflows_dir = os.path.join(module_dir, "example_workflows")
if os.path.exists(workflows_dir):
webapp.add_routes([web.static('/api/workflow_templates/' + module_name, workflows_dir)])
webapp.add_routes(
[
web.static(
"/api/workflow_templates/" + module_name, workflows_dir
)
]
)
@routes.get("/i18n")
async def get_i18n(request):
"""Returns translations from all custom nodes' locales folders."""
return web.json_response(self.build_translations())

View File

@@ -43,10 +43,11 @@ parser.add_argument("--tls-certfile", type=str, help="Path to TLS (SSL) certific
parser.add_argument("--enable-cors-header", type=str, default=None, metavar="ORIGIN", nargs="?", const="*", help="Enable CORS (Cross-Origin Resource Sharing) with optional origin or allow all with default '*'.")
parser.add_argument("--max-upload-size", type=float, default=100, help="Set the maximum upload size in MB.")
parser.add_argument("--base-directory", type=str, default=None, help="Set the ComfyUI base directory for models, custom_nodes, input, output, temp, and user directories.")
parser.add_argument("--extra-model-paths-config", type=str, default=None, metavar="PATH", nargs='+', action='append', help="Load one or more extra_model_paths.yaml files.")
parser.add_argument("--output-directory", type=str, default=None, help="Set the ComfyUI output directory.")
parser.add_argument("--temp-directory", type=str, default=None, help="Set the ComfyUI temp directory (default is in the ComfyUI directory).")
parser.add_argument("--input-directory", type=str, default=None, help="Set the ComfyUI input directory.")
parser.add_argument("--output-directory", type=str, default=None, help="Set the ComfyUI output directory. Overrides --base-directory.")
parser.add_argument("--temp-directory", type=str, default=None, help="Set the ComfyUI temp directory (default is in the ComfyUI directory). Overrides --base-directory.")
parser.add_argument("--input-directory", type=str, default=None, help="Set the ComfyUI input directory. Overrides --base-directory.")
parser.add_argument("--auto-launch", action="store_true", help="Automatically launch ComfyUI in the default browser.")
parser.add_argument("--disable-auto-launch", action="store_true", help="Disable auto launching the browser.")
parser.add_argument("--cuda-device", type=int, default=None, metavar="DEVICE_ID", help="Set the id of the cuda device this instance will use.")
@@ -176,7 +177,7 @@ parser.add_argument(
help="The local filesystem path to the directory where the frontend is located. Overrides --front-end-version.",
)
parser.add_argument("--user-directory", type=is_valid_directory, default=None, help="Set the ComfyUI user directory with an absolute path.")
parser.add_argument("--user-directory", type=is_valid_directory, default=None, help="Set the ComfyUI user directory with an absolute path. Overrides --base-directory.")
if comfy.options.args_parsing:
args = parser.parse_args()

View File

@@ -3,9 +3,6 @@ import math
import comfy.utils
def lcm(a, b): #TODO: eventually replace by math.lcm (added in python3.9)
return abs(a*b) // math.gcd(a, b)
class CONDRegular:
def __init__(self, cond):
self.cond = cond
@@ -46,7 +43,7 @@ class CONDCrossAttn(CONDRegular):
if s1[0] != s2[0] or s1[2] != s2[2]: #these 2 cases should not happen
return False
mult_min = lcm(s1[1], s2[1])
mult_min = math.lcm(s1[1], s2[1])
diff = mult_min // min(s1[1], s2[1])
if diff > 4: #arbitrary limit on the padding because it's probably going to impact performance negatively if it's too much
return False
@@ -57,7 +54,7 @@ class CONDCrossAttn(CONDRegular):
crossattn_max_len = self.cond.shape[1]
for x in others:
c = x.cond
crossattn_max_len = lcm(crossattn_max_len, c.shape[1])
crossattn_max_len = math.lcm(crossattn_max_len, c.shape[1])
conds.append(c)
out = []

View File

@@ -4,105 +4,6 @@ import logging
# conversion code from https://github.com/huggingface/diffusers/blob/main/scripts/convert_diffusers_to_original_stable_diffusion.py
# =================#
# UNet Conversion #
# =================#
unet_conversion_map = [
# (stable-diffusion, HF Diffusers)
("time_embed.0.weight", "time_embedding.linear_1.weight"),
("time_embed.0.bias", "time_embedding.linear_1.bias"),
("time_embed.2.weight", "time_embedding.linear_2.weight"),
("time_embed.2.bias", "time_embedding.linear_2.bias"),
("input_blocks.0.0.weight", "conv_in.weight"),
("input_blocks.0.0.bias", "conv_in.bias"),
("out.0.weight", "conv_norm_out.weight"),
("out.0.bias", "conv_norm_out.bias"),
("out.2.weight", "conv_out.weight"),
("out.2.bias", "conv_out.bias"),
]
unet_conversion_map_resnet = [
# (stable-diffusion, HF Diffusers)
("in_layers.0", "norm1"),
("in_layers.2", "conv1"),
("out_layers.0", "norm2"),
("out_layers.3", "conv2"),
("emb_layers.1", "time_emb_proj"),
("skip_connection", "conv_shortcut"),
]
unet_conversion_map_layer = []
# hardcoded number of downblocks and resnets/attentions...
# would need smarter logic for other networks.
for i in range(4):
# loop over downblocks/upblocks
for j in range(2):
# loop over resnets/attentions for downblocks
hf_down_res_prefix = f"down_blocks.{i}.resnets.{j}."
sd_down_res_prefix = f"input_blocks.{3 * i + j + 1}.0."
unet_conversion_map_layer.append((sd_down_res_prefix, hf_down_res_prefix))
if i < 3:
# no attention layers in down_blocks.3
hf_down_atn_prefix = f"down_blocks.{i}.attentions.{j}."
sd_down_atn_prefix = f"input_blocks.{3 * i + j + 1}.1."
unet_conversion_map_layer.append((sd_down_atn_prefix, hf_down_atn_prefix))
for j in range(3):
# loop over resnets/attentions for upblocks
hf_up_res_prefix = f"up_blocks.{i}.resnets.{j}."
sd_up_res_prefix = f"output_blocks.{3 * i + j}.0."
unet_conversion_map_layer.append((sd_up_res_prefix, hf_up_res_prefix))
if i > 0:
# no attention layers in up_blocks.0
hf_up_atn_prefix = f"up_blocks.{i}.attentions.{j}."
sd_up_atn_prefix = f"output_blocks.{3 * i + j}.1."
unet_conversion_map_layer.append((sd_up_atn_prefix, hf_up_atn_prefix))
if i < 3:
# no downsample in down_blocks.3
hf_downsample_prefix = f"down_blocks.{i}.downsamplers.0.conv."
sd_downsample_prefix = f"input_blocks.{3 * (i + 1)}.0.op."
unet_conversion_map_layer.append((sd_downsample_prefix, hf_downsample_prefix))
# no upsample in up_blocks.3
hf_upsample_prefix = f"up_blocks.{i}.upsamplers.0."
sd_upsample_prefix = f"output_blocks.{3 * i + 2}.{1 if i == 0 else 2}."
unet_conversion_map_layer.append((sd_upsample_prefix, hf_upsample_prefix))
hf_mid_atn_prefix = "mid_block.attentions.0."
sd_mid_atn_prefix = "middle_block.1."
unet_conversion_map_layer.append((sd_mid_atn_prefix, hf_mid_atn_prefix))
for j in range(2):
hf_mid_res_prefix = f"mid_block.resnets.{j}."
sd_mid_res_prefix = f"middle_block.{2 * j}."
unet_conversion_map_layer.append((sd_mid_res_prefix, hf_mid_res_prefix))
def convert_unet_state_dict(unet_state_dict):
# buyer beware: this is a *brittle* function,
# and correct output requires that all of these pieces interact in
# the exact order in which I have arranged them.
mapping = {k: k for k in unet_state_dict.keys()}
for sd_name, hf_name in unet_conversion_map:
mapping[hf_name] = sd_name
for k, v in mapping.items():
if "resnets" in k:
for sd_part, hf_part in unet_conversion_map_resnet:
v = v.replace(hf_part, sd_part)
mapping[k] = v
for k, v in mapping.items():
for sd_part, hf_part in unet_conversion_map_layer:
v = v.replace(hf_part, sd_part)
mapping[k] = v
new_state_dict = {v: unet_state_dict[k] for k, v in mapping.items()}
return new_state_dict
# ================#
# VAE Conversion #
# ================#
@@ -213,6 +114,7 @@ textenc_pattern = re.compile("|".join(protected.keys()))
# Ordering is from https://github.com/pytorch/pytorch/blob/master/test/cpp/api/modules.cpp
code2idx = {"q": 0, "k": 1, "v": 2}
# This function exists because at the time of writing torch.cat can't do fp8 with cuda
def cat_tensors(tensors):
x = 0
@@ -229,6 +131,7 @@ def cat_tensors(tensors):
return out
def convert_text_enc_state_dict_v20(text_enc_dict, prefix=""):
new_state_dict = {}
capture_qkv_weight = {}
@@ -284,5 +187,3 @@ def convert_text_enc_state_dict_v20(text_enc_dict, prefix=""):
def convert_text_enc_state_dict(text_enc_dict):
return text_enc_dict

View File

@@ -661,7 +661,7 @@ class UniPC:
if x_t is None:
if use_predictor:
pred_res = torch.einsum('k,bkchw->bchw', rhos_p, D1s)
pred_res = torch.tensordot(D1s, rhos_p, dims=([1], [0])) # torch.einsum('k,bkchw->bchw', rhos_p, D1s)
else:
pred_res = 0
x_t = x_t_ - expand_dims(alpha_t * B_h, dims) * pred_res
@@ -669,7 +669,7 @@ class UniPC:
if use_corrector:
model_t = self.model_fn(x_t, t)
if D1s is not None:
corr_res = torch.einsum('k,bkchw->bchw', rhos_c[:-1], D1s)
corr_res = torch.tensordot(D1s, rhos_c[:-1], dims=([1], [0])) # torch.einsum('k,bkchw->bchw', rhos_c[:-1], D1s)
else:
corr_res = 0
D1_t = (model_t - model_prev_0)

View File

@@ -40,7 +40,7 @@ def get_sigmas_polyexponential(n, sigma_min, sigma_max, rho=1., device='cpu'):
def get_sigmas_vp(n, beta_d=19.9, beta_min=0.1, eps_s=1e-3, device='cpu'):
"""Constructs a continuous VP noise schedule."""
t = torch.linspace(1, eps_s, n, device=device)
sigmas = torch.sqrt(torch.exp(beta_d * t ** 2 / 2 + beta_min * t) - 1)
sigmas = torch.sqrt(torch.special.expm1(beta_d * t ** 2 / 2 + beta_min * t))
return append_zero(sigmas)
@@ -1336,3 +1336,26 @@ def sample_res_multistep(model, x, sigmas, extra_args=None, callback=None, disab
@torch.no_grad()
def sample_res_multistep_cfg_pp(model, x, sigmas, extra_args=None, callback=None, disable=None, s_churn=0., s_tmin=0., s_tmax=float('inf'), s_noise=1., noise_sampler=None):
return res_multistep(model, x, sigmas, extra_args=extra_args, callback=callback, disable=disable, s_churn=s_churn, s_tmin=s_tmin, s_tmax=s_tmax, s_noise=s_noise, noise_sampler=noise_sampler, cfg_pp=True)
@torch.no_grad()
def sample_gradient_estimation(model, x, sigmas, extra_args=None, callback=None, disable=None, ge_gamma=2.):
"""Gradient-estimation sampler. Paper: https://openreview.net/pdf?id=o2ND9v0CeK"""
extra_args = {} if extra_args is None else extra_args
s_in = x.new_ones([x.shape[0]])
old_d = None
for i in trange(len(sigmas) - 1, disable=disable):
denoised = model(x, sigmas[i] * s_in, **extra_args)
d = to_d(x, sigmas[i], denoised)
if callback is not None:
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
dt = sigmas[i + 1] - sigmas[i]
if i == 0:
# Euler method
x = x + d * dt
else:
# Gradient estimation
d_bar = ge_gamma * d + (1 - ge_gamma) * old_d
x = x + d_bar * dt
old_d = d
return x

View File

@@ -168,14 +168,18 @@ class Attention(nn.Module):
k = self.to_k[1](k)
v = self.to_v[1](v)
if self.is_selfattn and rope_emb is not None: # only apply to self-attention!
q = apply_rotary_pos_emb(q, rope_emb)
k = apply_rotary_pos_emb(k, rope_emb)
return q, k, v
# apply_rotary_pos_emb inlined
q_shape = q.shape
q = q.reshape(*q.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2)
q = rope_emb[..., 0] * q[..., 0] + rope_emb[..., 1] * q[..., 1]
q = q.movedim(-1, -2).reshape(*q_shape).to(x.dtype)
def cal_attn(self, q, k, v, mask=None):
out = optimized_attention(q, k, v, self.heads, skip_reshape=True, mask=mask, skip_output_reshape=True)
out = rearrange(out, " b n s c -> s b (n c)")
return self.to_out(out)
# apply_rotary_pos_emb inlined
k_shape = k.shape
k = k.reshape(*k.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2)
k = rope_emb[..., 0] * k[..., 0] + rope_emb[..., 1] * k[..., 1]
k = k.movedim(-1, -2).reshape(*k_shape).to(x.dtype)
return q, k, v
def forward(
self,
@@ -191,7 +195,10 @@ class Attention(nn.Module):
context (Optional[Tensor]): The key tensor of shape [B, Mk, K] or use x as context [self attention] if None
"""
q, k, v = self.cal_qkv(x, context, mask, rope_emb=rope_emb, **kwargs)
return self.cal_attn(q, k, v, mask)
out = optimized_attention(q, k, v, self.heads, skip_reshape=True, mask=mask, skip_output_reshape=True)
del q, k, v
out = rearrange(out, " b n s c -> s b (n c)")
return self.to_out(out)
class FeedForward(nn.Module):
@@ -788,10 +795,7 @@ class GeneralDITTransformerBlock(nn.Module):
crossattn_mask: Optional[torch.Tensor] = None,
rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
adaln_lora_B_3D: Optional[torch.Tensor] = None,
extra_per_block_pos_emb: Optional[torch.Tensor] = None,
) -> torch.Tensor:
if extra_per_block_pos_emb is not None:
x = x + extra_per_block_pos_emb
for block in self.blocks:
x = block(
x,

View File

@@ -30,6 +30,8 @@ import torch.nn as nn
import torch.nn.functional as F
import logging
from comfy.ldm.modules.diffusionmodules.model import vae_attention
from .patching import (
Patcher,
Patcher3D,
@@ -400,6 +402,8 @@ class CausalAttnBlock(nn.Module):
in_channels, in_channels, kernel_size=1, stride=1, padding=0
)
self.optimized_attention = vae_attention()
def forward(self, x: torch.Tensor) -> torch.Tensor:
h_ = x
h_ = self.norm(h_)
@@ -413,18 +417,7 @@ class CausalAttnBlock(nn.Module):
v, batch_size = time2batch(v)
b, c, h, w = q.shape
q = q.reshape(b, c, h * w)
q = q.permute(0, 2, 1)
k = k.reshape(b, c, h * w)
w_ = torch.bmm(q, k)
w_ = w_ * (int(c) ** (-0.5))
w_ = F.softmax(w_, dim=2)
# attend to values
v = v.reshape(b, c, h * w)
w_ = w_.permute(0, 2, 1)
h_ = torch.bmm(v, w_)
h_ = h_.reshape(b, c, h, w)
h_ = self.optimized_attention(q, k, v)
h_ = batch2time(h_, batch_size)
h_ = self.proj_out(h_)
@@ -871,18 +864,16 @@ class EncoderFactorized(nn.Module):
x = self.patcher3d(x)
# downsampling
hs = [self.conv_in(x)]
h = self.conv_in(x)
for i_level in range(self.num_resolutions):
for i_block in range(self.num_res_blocks):
h = self.down[i_level].block[i_block](hs[-1])
h = self.down[i_level].block[i_block](h)
if len(self.down[i_level].attn) > 0:
h = self.down[i_level].attn[i_block](h)
hs.append(h)
if i_level != self.num_resolutions - 1:
hs.append(self.down[i_level].downsample(hs[-1]))
h = self.down[i_level].downsample(h)
# middle
h = hs[-1]
h = self.mid.block_1(h)
h = self.mid.attn_1(h)
h = self.mid.block_2(h)

View File

@@ -281,54 +281,76 @@ class UnPatcher3D(UnPatcher):
hh = hh.to(dtype=dtype)
xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh = torch.chunk(x, 8, dim=1)
del x
# Height height transposed convolutions.
xll = F.conv_transpose3d(
xlll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xlll
xll += F.conv_transpose3d(
xllh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xllh
xlh = F.conv_transpose3d(
xlhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xlhl
xlh += F.conv_transpose3d(
xlhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xlhh
xhl = F.conv_transpose3d(
xhll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xhll
xhl += F.conv_transpose3d(
xhlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xhlh
xhh = F.conv_transpose3d(
xhhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xhhl
xhh += F.conv_transpose3d(
xhhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
)
del xhhh
# Handles width transposed convolutions.
xl = F.conv_transpose3d(
xll, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
)
del xll
xl += F.conv_transpose3d(
xlh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
)
del xlh
xh = F.conv_transpose3d(
xhl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
)
del xhl
xh += F.conv_transpose3d(
xhh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
)
del xhh
# Handles time axis transposed convolutions.
x = F.conv_transpose3d(
xl, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)
)
del xl
x += F.conv_transpose3d(
xh, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)
)

View File

@@ -168,7 +168,7 @@ class GeneralDIT(nn.Module):
operations=operations,
)
self.build_pos_embed(device=device)
self.build_pos_embed(device=device, dtype=dtype)
self.block_x_format = block_x_format
self.use_adaln_lora = use_adaln_lora
self.adaln_lora_dim = adaln_lora_dim
@@ -210,7 +210,7 @@ class GeneralDIT(nn.Module):
operations=operations,
)
def build_pos_embed(self, device=None):
def build_pos_embed(self, device=None, dtype=None):
if self.pos_emb_cls == "rope3d":
cls_type = VideoRopePosition3DEmb
else:
@@ -242,6 +242,7 @@ class GeneralDIT(nn.Module):
kwargs["w_extrapolation_ratio"] = self.extra_w_extrapolation_ratio
kwargs["t_extrapolation_ratio"] = self.extra_t_extrapolation_ratio
kwargs["device"] = device
kwargs["dtype"] = dtype
self.extra_pos_embedder = LearnablePosEmbAxis(
**kwargs,
)
@@ -292,7 +293,7 @@ class GeneralDIT(nn.Module):
x_B_T_H_W_D = self.x_embedder(x_B_C_T_H_W)
if self.extra_per_block_abs_pos_emb:
extra_pos_emb = self.extra_pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device)
extra_pos_emb = self.extra_pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device, dtype=x_B_C_T_H_W.dtype)
else:
extra_pos_emb = None
@@ -476,6 +477,8 @@ class GeneralDIT(nn.Module):
inputs["original_shape"],
)
extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = inputs["extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D"].to(x.dtype)
del inputs
if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
assert (
x.shape == extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape
@@ -486,6 +489,8 @@ class GeneralDIT(nn.Module):
self.blocks["block0"].x_format == block.x_format
), f"First block has x_format {self.blocks[0].x_format}, got {block.x_format}"
if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
x += extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D
x = block(
x,
affline_emb_B_D,
@@ -493,7 +498,6 @@ class GeneralDIT(nn.Module):
crossattn_mask,
rope_emb_L_1_1_D=rope_emb_L_1_1_D,
adaln_lora_B_3D=adaln_lora_B_3D,
extra_per_block_pos_emb=extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D,
)
x_B_T_H_W_D = rearrange(x, "T H W B D -> B T H W D")

View File

@@ -41,12 +41,12 @@ def normalize(x: torch.Tensor, dim: Optional[List[int]] = None, eps: float = 0)
class VideoPositionEmb(nn.Module):
def forward(self, x_B_T_H_W_C: torch.Tensor, fps=Optional[torch.Tensor], device=None) -> torch.Tensor:
def forward(self, x_B_T_H_W_C: torch.Tensor, fps=Optional[torch.Tensor], device=None, dtype=None) -> torch.Tensor:
"""
It delegates the embedding generation to generate_embeddings function.
"""
B_T_H_W_C = x_B_T_H_W_C.shape
embeddings = self.generate_embeddings(B_T_H_W_C, fps=fps, device=device)
embeddings = self.generate_embeddings(B_T_H_W_C, fps=fps, device=device, dtype=dtype)
return embeddings
@@ -104,6 +104,7 @@ class VideoRopePosition3DEmb(VideoPositionEmb):
w_ntk_factor: Optional[float] = None,
t_ntk_factor: Optional[float] = None,
device=None,
dtype=None,
):
"""
Generate embeddings for the given input size.
@@ -173,6 +174,7 @@ class LearnablePosEmbAxis(VideoPositionEmb):
len_w: int,
len_t: int,
device=None,
dtype=None,
**kwargs,
):
"""
@@ -184,17 +186,16 @@ class LearnablePosEmbAxis(VideoPositionEmb):
self.interpolation = interpolation
assert self.interpolation in ["crop"], f"Unknown interpolation method {self.interpolation}"
self.pos_emb_h = nn.Parameter(torch.empty(len_h, model_channels, device=device))
self.pos_emb_w = nn.Parameter(torch.empty(len_w, model_channels, device=device))
self.pos_emb_t = nn.Parameter(torch.empty(len_t, model_channels, device=device))
self.pos_emb_h = nn.Parameter(torch.empty(len_h, model_channels, device=device, dtype=dtype))
self.pos_emb_w = nn.Parameter(torch.empty(len_w, model_channels, device=device, dtype=dtype))
self.pos_emb_t = nn.Parameter(torch.empty(len_t, model_channels, device=device, dtype=dtype))
def generate_embeddings(self, B_T_H_W_C: torch.Size, fps=Optional[torch.Tensor], device=None) -> torch.Tensor:
def generate_embeddings(self, B_T_H_W_C: torch.Size, fps=Optional[torch.Tensor], device=None, dtype=None) -> torch.Tensor:
B, T, H, W, _ = B_T_H_W_C
if self.interpolation == "crop":
emb_h_H = self.pos_emb_h[:H].to(device=device)
emb_w_W = self.pos_emb_w[:W].to(device=device)
emb_t_T = self.pos_emb_t[:T].to(device=device)
emb_h_H = self.pos_emb_h[:H].to(device=device, dtype=dtype)
emb_w_W = self.pos_emb_w[:W].to(device=device, dtype=dtype)
emb_t_T = self.pos_emb_t[:T].to(device=device, dtype=dtype)
emb = (
repeat(emb_t_T, "t d-> b t h w d", b=B, h=H, w=W)
+ repeat(emb_h_H, "h d-> b t h w d", b=B, t=T, w=W)

View File

@@ -18,6 +18,7 @@ import logging
import torch
from torch import nn
from enum import Enum
import math
from .cosmos_tokenizer.layers3d import (
EncoderFactorized,
@@ -89,8 +90,8 @@ class CausalContinuousVideoTokenizer(nn.Module):
self.distribution = IdentityDistribution() # ContinuousFormulation[formulation_name].value()
num_parameters = sum(param.numel() for param in self.parameters())
logging.info(f"model={self.name}, num_parameters={num_parameters:,}")
logging.info(
logging.debug(f"model={self.name}, num_parameters={num_parameters:,}")
logging.debug(
f"z_channels={z_channels}, latent_channels={self.latent_channels}."
)
@@ -105,17 +106,23 @@ class CausalContinuousVideoTokenizer(nn.Module):
z, posteriors = self.distribution(moments)
latent_ch = z.shape[1]
latent_t = z.shape[2]
dtype = z.dtype
mean = self.latent_mean.view(latent_ch, -1)[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=dtype, device=z.device)
std = self.latent_std.view(latent_ch, -1)[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=dtype, device=z.device)
in_dtype = z.dtype
mean = self.latent_mean.view(latent_ch, -1)
std = self.latent_std.view(latent_ch, -1)
mean = mean.repeat(1, math.ceil(latent_t / mean.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
std = std.repeat(1, math.ceil(latent_t / std.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
return ((z - mean) / std) * self.sigma_data
def decode(self, z):
in_dtype = z.dtype
latent_ch = z.shape[1]
latent_t = z.shape[2]
mean = self.latent_mean.view(latent_ch, -1)[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
std = self.latent_std.view(latent_ch, -1)[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
mean = self.latent_mean.view(latent_ch, -1)
std = self.latent_std.view(latent_ch, -1)
mean = mean.repeat(1, math.ceil(latent_t / mean.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
std = std.repeat(1, math.ceil(latent_t / std.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
z = z / self.sigma_data
z = z * std + mean

View File

@@ -230,8 +230,7 @@ class SingleStreamBlock(nn.Module):
def forward(self, x: Tensor, vec: Tensor, pe: Tensor, attn_mask=None) -> Tensor:
mod, _ = self.modulation(vec)
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)
qkv, mlp = torch.split(self.linear1((1 + mod.scale) * self.pre_norm(x) + mod.shift), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
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)

View File

@@ -5,8 +5,15 @@ from torch import Tensor
from comfy.ldm.modules.attention import optimized_attention
import comfy.model_management
def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None) -> Tensor:
q, k = apply_rope(q, k, pe)
q_shape = q.shape
k_shape = k.shape
q = q.float().reshape(*q.shape[:-1], -1, 1, 2)
k = k.float().reshape(*k.shape[:-1], -1, 1, 2)
q = (pe[..., 0] * q[..., 0] + pe[..., 1] * q[..., 1]).reshape(*q_shape).type_as(v)
k = (pe[..., 0] * k[..., 0] + pe[..., 1] * k[..., 1]).reshape(*k_shape).type_as(v)
heads = q.shape[1]
x = optimized_attention(q, k, v, heads, skip_reshape=True, mask=mask)

View File

@@ -109,9 +109,8 @@ class Flux(nn.Module):
img = self.img_in(img)
vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
if self.params.guidance_embed:
if guidance is None:
raise ValueError("Didn't get guidance strength for guidance distilled model.")
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
if guidance is not None:
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
vec = vec + self.vector_in(y[:,:self.params.vec_in_dim])
txt = self.txt_in(txt)
@@ -186,7 +185,7 @@ class Flux(nn.Module):
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
return img
def forward(self, x, timestep, context, y, guidance, control=None, transformer_options={}, **kwargs):
def forward(self, x, timestep, context, y, guidance=None, control=None, transformer_options={}, **kwargs):
bs, c, h, w = x.shape
patch_size = self.patch_size
x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))

View File

@@ -240,9 +240,8 @@ class HunyuanVideo(nn.Module):
vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])
if self.params.guidance_embed:
if guidance is None:
raise ValueError("Didn't get guidance strength for guidance distilled model.")
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
if guidance is not None:
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
if txt_mask is not None and not torch.is_floating_point(txt_mask):
txt_mask = (txt_mask - 1).to(img.dtype) * torch.finfo(img.dtype).max
@@ -314,7 +313,7 @@ class HunyuanVideo(nn.Module):
img = img.reshape(initial_shape)
return img
def forward(self, x, timestep, context, y, guidance, attention_mask=None, control=None, transformer_options={}, **kwargs):
def forward(self, x, timestep, context, y, guidance=None, attention_mask=None, control=None, transformer_options={}, **kwargs):
bs, c, t, h, w = x.shape
patch_size = self.patch_size
t_len = ((t + (patch_size[0] // 2)) // patch_size[0])

View File

@@ -293,6 +293,17 @@ def pytorch_attention(q, k, v):
return out
def vae_attention():
if model_management.xformers_enabled_vae():
logging.info("Using xformers attention in VAE")
return xformers_attention
elif model_management.pytorch_attention_enabled():
logging.info("Using pytorch attention in VAE")
return pytorch_attention
else:
logging.info("Using split attention in VAE")
return normal_attention
class AttnBlock(nn.Module):
def __init__(self, in_channels, conv_op=ops.Conv2d):
super().__init__()
@@ -320,15 +331,7 @@ class AttnBlock(nn.Module):
stride=1,
padding=0)
if model_management.xformers_enabled_vae():
logging.info("Using xformers attention in VAE")
self.optimized_attention = xformers_attention
elif model_management.pytorch_attention_enabled():
logging.info("Using pytorch attention in VAE")
self.optimized_attention = pytorch_attention
else:
logging.info("Using split attention in VAE")
self.optimized_attention = normal_attention
self.optimized_attention = vae_attention()
def forward(self, x):
h_ = x
@@ -699,9 +702,6 @@ class Decoder(nn.Module):
padding=1)
def forward(self, z, **kwargs):
#assert z.shape[1:] == self.z_shape[1:]
self.last_z_shape = z.shape
# timestep embedding
temb = None

View File

@@ -148,7 +148,9 @@ class BaseModel(torch.nn.Module):
xc = xc.to(dtype)
t = self.model_sampling.timestep(t).float()
context = context.to(dtype)
if context is not None:
context = context.to(dtype)
extra_conds = {}
for o in kwargs:
extra = kwargs[o]
@@ -549,6 +551,10 @@ class SD_X4Upscaler(BaseModel):
out['c_concat'] = comfy.conds.CONDNoiseShape(image)
out['y'] = comfy.conds.CONDRegular(noise_level)
cross_attn = kwargs.get("cross_attn", None)
if cross_attn is not None:
out['c_crossattn'] = comfy.conds.CONDCrossAttn(cross_attn)
return out
class IP2P:
@@ -806,7 +812,10 @@ class Flux(BaseModel):
(h_tok, w_tok) = (math.ceil(shape[2] / self.diffusion_model.patch_size), math.ceil(shape[3] / self.diffusion_model.patch_size))
attention_mask = utils.upscale_dit_mask(attention_mask, mask_ref_size, (h_tok, w_tok))
out['attention_mask'] = comfy.conds.CONDRegular(attention_mask)
out['guidance'] = comfy.conds.CONDRegular(torch.FloatTensor([kwargs.get("guidance", 3.5)]))
guidance = kwargs.get("guidance", 3.5)
if guidance is not None:
out['guidance'] = comfy.conds.CONDRegular(torch.FloatTensor([guidance]))
return out
class GenmoMochi(BaseModel):
@@ -863,7 +872,10 @@ class HunyuanVideo(BaseModel):
cross_attn = kwargs.get("cross_attn", None)
if cross_attn is not None:
out['c_crossattn'] = comfy.conds.CONDRegular(cross_attn)
out['guidance'] = comfy.conds.CONDRegular(torch.FloatTensor([kwargs.get("guidance", 6.0)]))
guidance = kwargs.get("guidance", 6.0)
if guidance is not None:
out['guidance'] = comfy.conds.CONDRegular(torch.FloatTensor([guidance]))
return out
class CosmosVideo(BaseModel):

View File

@@ -218,7 +218,7 @@ def is_amd():
MIN_WEIGHT_MEMORY_RATIO = 0.4
if is_nvidia():
MIN_WEIGHT_MEMORY_RATIO = 0.2
MIN_WEIGHT_MEMORY_RATIO = 0.1
ENABLE_PYTORCH_ATTENTION = False
if args.use_pytorch_cross_attention:
@@ -535,14 +535,11 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu
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) and not force_full_load:
model_size = loaded_model.model_memory_required(torch_dev)
loaded_memory = loaded_model.model_loaded_memory()
current_free_mem = get_free_memory(torch_dev) + loaded_memory
lowvram_model_memory = max(64 * 1024 * 1024, (current_free_mem - minimum_memory_required), min(current_free_mem * MIN_WEIGHT_MEMORY_RATIO, current_free_mem - minimum_inference_memory()))
lowvram_model_memory = max(0.1, lowvram_model_memory - loaded_memory)
if model_size <= lowvram_model_memory: #only switch to lowvram if really necessary
lowvram_model_memory = 0
if vram_set_state == VRAMState.NO_VRAM:
lowvram_model_memory = 0.1

View File

@@ -58,7 +58,6 @@ def convert_cond(cond):
temp = c[1].copy()
model_conds = temp.get("model_conds", {})
if c[0] is not None:
model_conds["c_crossattn"] = comfy.conds.CONDCrossAttn(c[0]) #TODO: remove
temp["cross_attn"] = c[0]
temp["model_conds"] = model_conds
temp["uuid"] = uuid.uuid4()

View File

@@ -12,7 +12,6 @@ import collections
from comfy import model_management
import math
import logging
import comfy.samplers
import comfy.sampler_helpers
import comfy.model_patcher
import comfy.patcher_extension
@@ -178,7 +177,7 @@ def finalize_default_conds(model: 'BaseModel', hooked_to_run: dict[comfy.hooks.H
cond = default_conds[i]
for x in cond:
# do get_area_and_mult to get all the expected values
p = comfy.samplers.get_area_and_mult(x, x_in, timestep)
p = get_area_and_mult(x, x_in, timestep)
if p is None:
continue
# replace p's mult with calculated mult
@@ -215,7 +214,7 @@ def _calc_cond_batch(model: 'BaseModel', conds: list[list[dict]], x_in: torch.Te
default_c.append(x)
has_default_conds = True
continue
p = comfy.samplers.get_area_and_mult(x, x_in, timestep)
p = get_area_and_mult(x, x_in, timestep)
if p is None:
continue
if p.hooks is not None:
@@ -687,7 +686,7 @@ class Sampler:
KSAMPLER_NAMES = ["euler", "euler_cfg_pp", "euler_ancestral", "euler_ancestral_cfg_pp", "heun", "heunpp2","dpm_2", "dpm_2_ancestral",
"lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_2s_ancestral_cfg_pp", "dpmpp_sde", "dpmpp_sde_gpu",
"dpmpp_2m", "dpmpp_2m_cfg_pp", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm",
"ipndm", "ipndm_v", "deis", "res_multistep", "res_multistep_cfg_pp"]
"ipndm", "ipndm_v", "deis", "res_multistep", "res_multistep_cfg_pp", "gradient_estimation"]
class KSAMPLER(Sampler):
def __init__(self, sampler_function, extra_options={}, inpaint_options={}):

View File

@@ -388,8 +388,8 @@ class VAE:
ddconfig = {'z_channels': 16, 'latent_channels': self.latent_channels, 'z_factor': 1, 'resolution': 1024, 'in_channels': 3, 'out_channels': 3, 'channels': 128, 'channels_mult': [2, 4, 4], 'num_res_blocks': 2, 'attn_resolutions': [32], 'dropout': 0.0, 'patch_size': 4, 'num_groups': 1, 'temporal_compression': 8, 'spacial_compression': 8}
self.first_stage_model = comfy.ldm.cosmos.vae.CausalContinuousVideoTokenizer(**ddconfig)
#TODO: these values are a bit off because this is not a standard VAE
self.memory_used_decode = lambda shape, dtype: (220 * shape[2] * shape[3] * shape[4] * (8 * 8 * 8)) * model_management.dtype_size(dtype)
self.memory_used_encode = lambda shape, dtype: (500 * max(shape[2], 2) * shape[3] * shape[4]) * model_management.dtype_size(dtype)
self.memory_used_decode = lambda shape, dtype: (50 * shape[2] * shape[3] * shape[4] * (8 * 8 * 8)) * model_management.dtype_size(dtype)
self.memory_used_encode = lambda shape, dtype: (50 * (round((shape[2] + 7) / 8) * 8) * shape[3] * shape[4]) * model_management.dtype_size(dtype)
self.working_dtypes = [torch.bfloat16, torch.float32]
else:
logging.warning("WARNING: No VAE weights detected, VAE not initalized.")

View File

@@ -788,7 +788,7 @@ class HunyuanVideo(supported_models_base.BASE):
unet_extra_config = {}
latent_format = latent_formats.HunyuanVideo
memory_usage_factor = 2.0 #TODO
memory_usage_factor = 1.8 #TODO
supported_inference_dtypes = [torch.bfloat16, torch.float32]
@@ -839,7 +839,7 @@ class CosmosT2V(supported_models_base.BASE):
unet_extra_config = {}
latent_format = latent_formats.Cosmos1CV8x8x8
memory_usage_factor = 2.4 #TODO
memory_usage_factor = 1.6 #TODO
supported_inference_dtypes = [torch.bfloat16, torch.float16, torch.float32] #TODO

View File

@@ -43,13 +43,23 @@ if hasattr(torch.serialization, "add_safe_globals"): # TODO: this was added in
torch.serialization.add_safe_globals([ModelCheckpoint, scalar, dtype, Float64DType, encode])
ALWAYS_SAFE_LOAD = True
logging.info("Checkpoint files will always be loaded safely.")
else:
logging.info("Warning, you are using an old pytorch version and some ckpt/pt files might be loaded unsafely. Upgrading to 2.4 or above is recommended.")
def load_torch_file(ckpt, safe_load=False, device=None):
if device is None:
device = torch.device("cpu")
if ckpt.lower().endswith(".safetensors") or ckpt.lower().endswith(".sft"):
sd = safetensors.torch.load_file(ckpt, device=device.type)
try:
sd = safetensors.torch.load_file(ckpt, device=device.type)
except Exception as e:
if len(e.args) > 0:
message = e.args[0]
if "HeaderTooLarge" in message:
raise ValueError("{}\n\nFile path: {}\n\nThe safetensors file is corrupt or invalid. Make sure this is actually a safetensors file and not a ckpt or pt or other filetype.".format(message, ckpt))
if "MetadataIncompleteBuffer" in message:
raise ValueError("{}\n\nFile path: {}\n\nThe safetensors file is incomplete. Check the file size and make sure you have copied/downloaded it correctly.".format(message, ckpt))
raise e
else:
if safe_load or ALWAYS_SAFE_LOAD:
pl_sd = torch.load(ckpt, map_location=device, weights_only=True)

View File

@@ -71,8 +71,8 @@ class CosmosImageToVideoLatent:
mask[:, :, -latent_temp.shape[-3]:] *= 0.0
out_latent = {}
out_latent["samples"] = latent
out_latent["noise_mask"] = mask
out_latent["samples"] = latent.repeat((batch_size, ) + (1,) * (latent.ndim - 1))
out_latent["noise_mask"] = mask.repeat((batch_size, ) + (1,) * (mask.ndim - 1))
return (out_latent,)

View File

@@ -38,7 +38,26 @@ class FluxGuidance:
return (c, )
class FluxDisableGuidance:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"conditioning": ("CONDITIONING", ),
}}
RETURN_TYPES = ("CONDITIONING",)
FUNCTION = "append"
CATEGORY = "advanced/conditioning/flux"
DESCRIPTION = "This node completely disables the guidance embed on Flux and Flux like models"
def append(self, conditioning):
c = node_helpers.conditioning_set_values(conditioning, {"guidance": None})
return (c, )
NODE_CLASS_MAPPINGS = {
"CLIPTextEncodeFlux": CLIPTextEncodeFlux,
"FluxGuidance": FluxGuidance,
"FluxDisableGuidance": FluxDisableGuidance,
}

View File

@@ -2,10 +2,14 @@ import comfy.utils
import comfy_extras.nodes_post_processing
import torch
def reshape_latent_to(target_shape, latent):
def reshape_latent_to(target_shape, latent, repeat_batch=True):
if latent.shape[1:] != target_shape[1:]:
latent = comfy.utils.common_upscale(latent, target_shape[3], target_shape[2], "bilinear", "center")
return comfy.utils.repeat_to_batch_size(latent, target_shape[0])
latent = comfy.utils.common_upscale(latent, target_shape[-1], target_shape[-2], "bilinear", "center")
if repeat_batch:
return comfy.utils.repeat_to_batch_size(latent, target_shape[0])
else:
return latent
class LatentAdd:
@@ -116,8 +120,7 @@ class LatentBatch:
s1 = samples1["samples"]
s2 = samples2["samples"]
if s1.shape[1:] != s2.shape[1:]:
s2 = comfy.utils.common_upscale(s2, s1.shape[3], s1.shape[2], "bilinear", "center")
s2 = reshape_latent_to(s1.shape, s2, repeat_batch=False)
s = torch.cat((s1, s2), dim=0)
samples_out["samples"] = s
samples_out["batch_index"] = samples1.get("batch_index", [x for x in range(0, s1.shape[0])]) + samples2.get("batch_index", [x for x in range(0, s2.shape[0])])

View File

@@ -19,9 +19,6 @@ class Load3D():
"image": ("LOAD_3D", {}),
"width": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
"height": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
"show_grid": ([True, False],),
"camera_type": (["perspective", "orthographic"],),
"view": (["front", "right", "top", "isometric"],),
"material": (["original", "normal", "wireframe", "depth"],),
"bg_color": ("STRING", {"default": "#000000", "multiline": False}),
"light_intensity": ("INT", {"default": 10, "min": 1, "max": 20, "step": 1}),
@@ -69,9 +66,6 @@ class Load3DAnimation():
"image": ("LOAD_3D_ANIMATION", {}),
"width": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
"height": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
"show_grid": ([True, False],),
"camera_type": (["perspective", "orthographic"],),
"view": (["front", "right", "top", "isometric"],),
"material": (["original", "normal", "wireframe", "depth"],),
"bg_color": ("STRING", {"default": "#000000", "multiline": False}),
"light_intensity": ("INT", {"default": 10, "min": 1, "max": 20, "step": 1}),
@@ -109,9 +103,6 @@ class Preview3D():
def INPUT_TYPES(s):
return {"required": {
"model_file": ("STRING", {"default": "", "multiline": False}),
"show_grid": ([True, False],),
"camera_type": (["perspective", "orthographic"],),
"view": (["front", "right", "top", "isometric"],),
"material": (["original", "normal", "wireframe", "depth"],),
"bg_color": ("STRING", {"default": "#000000", "multiline": False}),
"light_intensity": ("INT", {"default": 10, "min": 1, "max": 20, "step": 1}),

View File

@@ -1,3 +1,3 @@
# This file is automatically generated by the build process when version is
# updated in pyproject.toml.
__version__ = "0.3.10"
__version__ = "0.3.13"

View File

@@ -7,11 +7,18 @@ import logging
from typing import Literal
from collections.abc import Collection
from comfy.cli_args import args
supported_pt_extensions: set[str] = {'.ckpt', '.pt', '.bin', '.pth', '.safetensors', '.pkl', '.sft'}
folder_names_and_paths: dict[str, tuple[list[str], set[str]]] = {}
base_path = os.path.dirname(os.path.realpath(__file__))
# --base-directory - Resets all default paths configured in folder_paths with a new base path
if args.base_directory:
base_path = os.path.abspath(args.base_directory)
else:
base_path = os.path.dirname(os.path.realpath(__file__))
models_dir = os.path.join(base_path, "models")
folder_names_and_paths["checkpoints"] = ([os.path.join(models_dir, "checkpoints")], supported_pt_extensions)
folder_names_and_paths["configs"] = ([os.path.join(models_dir, "configs")], [".yaml"])
@@ -39,10 +46,10 @@ folder_names_and_paths["photomaker"] = ([os.path.join(models_dir, "photomaker")]
folder_names_and_paths["classifiers"] = ([os.path.join(models_dir, "classifiers")], {""})
output_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "output")
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")
output_directory = os.path.join(base_path, "output")
temp_directory = os.path.join(base_path, "temp")
input_directory = os.path.join(base_path, "input")
user_directory = os.path.join(base_path, "user")
filename_list_cache: dict[str, tuple[list[str], dict[str, float], float]] = {}

View File

@@ -12,7 +12,10 @@ MAX_PREVIEW_RESOLUTION = args.preview_size
def preview_to_image(latent_image):
latents_ubyte = (((latent_image + 1.0) / 2.0).clamp(0, 1) # change scale from -1..1 to 0..1
.mul(0xFF) # to 0..255
).to(device="cpu", dtype=torch.uint8, non_blocking=comfy.model_management.device_supports_non_blocking(latent_image.device))
)
if comfy.model_management.directml_enabled:
latents_ubyte = latents_ubyte.to(dtype=torch.uint8)
latents_ubyte = latents_ubyte.to(device="cpu", dtype=torch.uint8, non_blocking=comfy.model_management.device_supports_non_blocking(latent_image.device))
return Image.fromarray(latents_ubyte.numpy())

View File

@@ -138,6 +138,8 @@ import server
from server import BinaryEventTypes
import nodes
import comfy.model_management
import comfyui_version
def cuda_malloc_warning():
device = comfy.model_management.get_torch_device()
@@ -292,6 +294,7 @@ def start_comfyui(asyncio_loop=None):
if __name__ == "__main__":
# Running directly, just start ComfyUI.
logging.info("ComfyUI version: {}".format(comfyui_version.__version__))
event_loop, _, start_all_func = start_comfyui()
try:
event_loop.run_until_complete(start_all_func())

View File

@@ -63,6 +63,8 @@ class CLIPTextEncode(ComfyNodeABC):
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):
if clip is None:
raise RuntimeError("ERROR: clip input is invalid: None\n\nIf the clip is from a checkpoint loader node your checkpoint does not contain a valid clip or text encoder model.")
tokens = clip.tokenize(text)
return (clip.encode_from_tokens_scheduled(tokens), )
@@ -937,6 +939,8 @@ class CLIPLoader:
clip_type = comfy.sd.CLIPType.LTXV
elif type == "pixart":
clip_type = comfy.sd.CLIPType.PIXART
elif type == "cosmos":
clip_type = comfy.sd.CLIPType.COSMOS
else:
clip_type = comfy.sd.CLIPType.STABLE_DIFFUSION

View File

@@ -1,6 +1,6 @@
[project]
name = "ComfyUI"
version = "0.3.10"
version = "0.3.13"
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.9"

View File

@@ -2,6 +2,7 @@ torch
torchsde
torchvision
torchaudio
numpy>=1.25.0
einops
transformers>=4.28.1
tokenizers>=0.13.3

View File

@@ -329,6 +329,9 @@ class PromptServer():
original_ref = json.loads(post.get("original_ref"))
filename, output_dir = folder_paths.annotated_filepath(original_ref['filename'])
if not filename:
return web.Response(status=400)
# validation for security: prevent accessing arbitrary path
if filename[0] == '/' or '..' in filename:
return web.Response(status=400)
@@ -370,6 +373,9 @@ class PromptServer():
filename = request.rel_url.query["filename"]
filename,output_dir = folder_paths.annotated_filepath(filename)
if not filename:
return web.Response(status=400)
# validation for security: prevent accessing arbitrary path
if filename[0] == '/' or '..' in filename:
return web.Response(status=400)

View File

@@ -2,39 +2,146 @@ import pytest
from aiohttp import web
from unittest.mock import patch
from app.custom_node_manager import CustomNodeManager
import json
pytestmark = (
pytest.mark.asyncio
) # This applies the asyncio mark to all test functions in the module
@pytest.fixture
def custom_node_manager():
return CustomNodeManager()
@pytest.fixture
def app(custom_node_manager):
app = web.Application()
routes = web.RouteTableDef()
custom_node_manager.add_routes(routes, app, [("ComfyUI-TestExtension1", "ComfyUI-TestExtension1")])
custom_node_manager.add_routes(
routes, app, [("ComfyUI-TestExtension1", "ComfyUI-TestExtension1")]
)
app.add_routes(routes)
return app
async def test_get_workflow_templates(aiohttp_client, app, tmp_path):
client = await aiohttp_client(app)
# Setup temporary custom nodes file structure with 1 workflow file
custom_nodes_dir = tmp_path / "custom_nodes"
example_workflows_dir = custom_nodes_dir / "ComfyUI-TestExtension1" / "example_workflows"
example_workflows_dir = (
custom_nodes_dir / "ComfyUI-TestExtension1" / "example_workflows"
)
example_workflows_dir.mkdir(parents=True)
template_file = example_workflows_dir / "workflow1.json"
template_file.write_text('')
template_file.write_text("")
with patch('folder_paths.folder_names_and_paths', {
'custom_nodes': ([str(custom_nodes_dir)], None)
}):
response = await client.get('/workflow_templates')
with patch(
"folder_paths.folder_names_and_paths",
{"custom_nodes": ([str(custom_nodes_dir)], None)},
):
response = await client.get("/workflow_templates")
assert response.status == 200
workflows_dict = await response.json()
assert isinstance(workflows_dict, dict)
assert "ComfyUI-TestExtension1" in workflows_dict
assert isinstance(workflows_dict["ComfyUI-TestExtension1"], list)
assert workflows_dict["ComfyUI-TestExtension1"][0] == "workflow1"
async def test_build_translations_empty_when_no_locales(custom_node_manager, tmp_path):
custom_nodes_dir = tmp_path / "custom_nodes"
custom_nodes_dir.mkdir(parents=True)
with patch("folder_paths.get_folder_paths", return_value=[str(custom_nodes_dir)]):
translations = custom_node_manager.build_translations()
assert translations == {}
async def test_build_translations_loads_all_files(custom_node_manager, tmp_path):
# Setup test directory structure
custom_nodes_dir = tmp_path / "custom_nodes" / "test-extension"
locales_dir = custom_nodes_dir / "locales" / "en"
locales_dir.mkdir(parents=True)
# Create test translation files
main_content = {"title": "Test Extension"}
(locales_dir / "main.json").write_text(json.dumps(main_content))
node_defs = {"node1": "Node 1"}
(locales_dir / "nodeDefs.json").write_text(json.dumps(node_defs))
commands = {"cmd1": "Command 1"}
(locales_dir / "commands.json").write_text(json.dumps(commands))
settings = {"setting1": "Setting 1"}
(locales_dir / "settings.json").write_text(json.dumps(settings))
with patch(
"folder_paths.get_folder_paths", return_value=[tmp_path / "custom_nodes"]
):
translations = custom_node_manager.build_translations()
assert translations == {
"en": {
"title": "Test Extension",
"nodeDefs": {"node1": "Node 1"},
"commands": {"cmd1": "Command 1"},
"settings": {"setting1": "Setting 1"},
}
}
async def test_build_translations_handles_invalid_json(custom_node_manager, tmp_path):
# Setup test directory structure
custom_nodes_dir = tmp_path / "custom_nodes" / "test-extension"
locales_dir = custom_nodes_dir / "locales" / "en"
locales_dir.mkdir(parents=True)
# Create valid main.json
main_content = {"title": "Test Extension"}
(locales_dir / "main.json").write_text(json.dumps(main_content))
# Create invalid JSON file
(locales_dir / "nodeDefs.json").write_text("invalid json{")
with patch(
"folder_paths.get_folder_paths", return_value=[tmp_path / "custom_nodes"]
):
translations = custom_node_manager.build_translations()
assert translations == {
"en": {
"title": "Test Extension",
}
}
async def test_build_translations_merges_multiple_extensions(
custom_node_manager, tmp_path
):
# Setup test directory structure for two extensions
custom_nodes_dir = tmp_path / "custom_nodes"
ext1_dir = custom_nodes_dir / "extension1" / "locales" / "en"
ext2_dir = custom_nodes_dir / "extension2" / "locales" / "en"
ext1_dir.mkdir(parents=True)
ext2_dir.mkdir(parents=True)
# Create translation files for extension 1
ext1_main = {"title": "Extension 1", "shared": "Original"}
(ext1_dir / "main.json").write_text(json.dumps(ext1_main))
# Create translation files for extension 2
ext2_main = {"description": "Extension 2", "shared": "Override"}
(ext2_dir / "main.json").write_text(json.dumps(ext2_main))
with patch("folder_paths.get_folder_paths", return_value=[str(custom_nodes_dir)]):
translations = custom_node_manager.build_translations()
assert translations == {
"en": {
"title": "Extension 1",
"description": "Extension 2",
"shared": "Override", # Second extension should override first
}
}

View File

@@ -1,19 +1,23 @@
### 🗻 This file is created through the spirit of Mount Fuji at its peak
# TODO(yoland): clean up this after I get back down
import sys
import pytest
import os
import tempfile
from unittest.mock import patch
from importlib import reload
import folder_paths
import comfy.cli_args
from comfy.options import enable_args_parsing
enable_args_parsing()
@pytest.fixture()
def clear_folder_paths():
# Clear the global dictionary before each test to ensure isolation
original = folder_paths.folder_names_and_paths.copy()
folder_paths.folder_names_and_paths.clear()
# Reload the module after each test to ensure isolation
yield
folder_paths.folder_names_and_paths = original
reload(folder_paths)
@pytest.fixture
def temp_dir():
@@ -21,7 +25,21 @@ def temp_dir():
yield tmpdirname
def test_get_directory_by_type():
@pytest.fixture
def set_base_dir():
def _set_base_dir(base_dir):
# Mock CLI args
with patch.object(sys, 'argv', ["main.py", "--base-directory", base_dir]):
reload(comfy.cli_args)
reload(folder_paths)
yield _set_base_dir
# Reload the modules after each test to ensure isolation
with patch.object(sys, 'argv', ["main.py"]):
reload(comfy.cli_args)
reload(folder_paths)
def test_get_directory_by_type(clear_folder_paths):
test_dir = "/test/dir"
folder_paths.set_output_directory(test_dir)
assert folder_paths.get_directory_by_type("output") == test_dir
@@ -96,3 +114,49 @@ def test_get_save_image_path(temp_dir):
assert counter == 1
assert subfolder == ""
assert filename_prefix == "test"
def test_base_path_changes(set_base_dir):
test_dir = os.path.abspath("/test/dir")
set_base_dir(test_dir)
assert folder_paths.base_path == test_dir
assert folder_paths.models_dir == os.path.join(test_dir, "models")
assert folder_paths.input_directory == os.path.join(test_dir, "input")
assert folder_paths.output_directory == os.path.join(test_dir, "output")
assert folder_paths.temp_directory == os.path.join(test_dir, "temp")
assert folder_paths.user_directory == os.path.join(test_dir, "user")
assert os.path.join(test_dir, "custom_nodes") in folder_paths.get_folder_paths("custom_nodes")
for name in ["checkpoints", "loras", "vae", "configs", "embeddings", "controlnet", "classifiers"]:
assert folder_paths.get_folder_paths(name)[0] == os.path.join(test_dir, "models", name)
def test_base_path_change_clears_old(set_base_dir):
test_dir = os.path.abspath("/test/dir")
set_base_dir(test_dir)
assert len(folder_paths.get_folder_paths("custom_nodes")) == 1
single_model_paths = [
"checkpoints",
"loras",
"vae",
"configs",
"clip_vision",
"style_models",
"diffusers",
"vae_approx",
"gligen",
"upscale_models",
"embeddings",
"hypernetworks",
"photomaker",
"classifiers",
]
for name in single_model_paths:
assert len(folder_paths.get_folder_paths(name)) == 1
for name in ["controlnet", "diffusion_models", "text_encoders"]:
assert len(folder_paths.get_folder_paths(name)) == 2

View File

@@ -0,0 +1,71 @@
from utils.json_util import merge_json_recursive
def test_merge_simple_dicts():
base = {"a": 1, "b": 2}
update = {"b": 3, "c": 4}
expected = {"a": 1, "b": 3, "c": 4}
assert merge_json_recursive(base, update) == expected
def test_merge_nested_dicts():
base = {"a": {"x": 1, "y": 2}, "b": 3}
update = {"a": {"y": 4, "z": 5}}
expected = {"a": {"x": 1, "y": 4, "z": 5}, "b": 3}
assert merge_json_recursive(base, update) == expected
def test_merge_lists():
base = {"a": [1, 2], "b": 3}
update = {"a": [3, 4]}
expected = {"a": [1, 2, 3, 4], "b": 3}
assert merge_json_recursive(base, update) == expected
def test_merge_nested_lists():
base = {"a": {"x": [1, 2]}}
update = {"a": {"x": [3, 4]}}
expected = {"a": {"x": [1, 2, 3, 4]}}
assert merge_json_recursive(base, update) == expected
def test_merge_mixed_types():
base = {"a": [1, 2], "b": {"x": 1}}
update = {"a": [3], "b": {"y": 2}}
expected = {"a": [1, 2, 3], "b": {"x": 1, "y": 2}}
assert merge_json_recursive(base, update) == expected
def test_merge_overwrite_non_dict():
base = {"a": 1}
update = {"a": {"x": 2}}
expected = {"a": {"x": 2}}
assert merge_json_recursive(base, update) == expected
def test_merge_empty_dicts():
base = {}
update = {"a": 1}
expected = {"a": 1}
assert merge_json_recursive(base, update) == expected
def test_merge_none_values():
base = {"a": None}
update = {"a": {"x": 1}}
expected = {"a": {"x": 1}}
assert merge_json_recursive(base, update) == expected
def test_merge_different_types():
base = {"a": [1, 2]}
update = {"a": "string"}
expected = {"a": "string"}
assert merge_json_recursive(base, update) == expected
def test_merge_complex_nested():
base = {"a": [1, 2], "b": {"x": [3, 4], "y": {"p": 1}}}
update = {"a": [5], "b": {"x": [6], "y": {"q": 2}}}
expected = {"a": [1, 2, 5], "b": {"x": [3, 4, 6], "y": {"p": 1, "q": 2}}}
assert merge_json_recursive(base, update) == expected

26
utils/json_util.py Normal file
View File

@@ -0,0 +1,26 @@
def merge_json_recursive(base, update):
"""Recursively merge two JSON-like objects.
- Dictionaries are merged recursively
- Lists are concatenated
- Other types are overwritten by the update value
Args:
base: Base JSON-like object
update: Update JSON-like object to merge into base
Returns:
Merged JSON-like object
"""
if not isinstance(base, dict) or not isinstance(update, dict):
if isinstance(base, list) and isinstance(update, list):
return base + update
return update
merged = base.copy()
for key, value in update.items():
if key in merged:
merged[key] = merge_json_recursive(merged[key], value)
else:
merged[key] = value
return merged

View File

@@ -1,23 +0,0 @@
import { d as defineComponent, o as openBlock, f as createElementBlock, J as renderSlot, T as normalizeClass } from "./index-DjNHn37O.js";
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "BaseViewTemplate",
props: {
dark: { type: Boolean, default: false }
},
setup(__props) {
const props = __props;
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: normalizeClass(["font-sans w-screen h-screen flex items-center justify-center pointer-events-auto overflow-auto", [
props.dark ? "text-neutral-300 bg-neutral-900 dark-theme" : "text-neutral-900 bg-neutral-300"
]])
}, [
renderSlot(_ctx.$slots, "default")
], 2);
};
}
});
export {
_sfc_main as _
};
//# sourceMappingURL=BaseViewTemplate-BNGF4K22.js.map

54
web/assets/BaseViewTemplate-BhQMaVFP.js generated vendored Normal file
View File

@@ -0,0 +1,54 @@
import { d as defineComponent, ad as ref, t as onMounted, bT as isElectron, bV as electronAPI, af as nextTick, o as openBlock, f as createElementBlock, i as withDirectives, v as vShow, m as createBaseVNode, M as renderSlot, V as normalizeClass } from "./index-QvfM__ze.js";
const _hoisted_1 = { class: "flex-grow w-full flex items-center justify-center overflow-auto" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "BaseViewTemplate",
props: {
dark: { type: Boolean, default: false }
},
setup(__props) {
const props = __props;
const darkTheme = {
color: "rgba(0, 0, 0, 0)",
symbolColor: "#d4d4d4"
};
const lightTheme = {
color: "rgba(0, 0, 0, 0)",
symbolColor: "#171717"
};
const topMenuRef = ref(null);
const isNativeWindow = ref(false);
onMounted(async () => {
if (isElectron()) {
const windowStyle = await electronAPI().Config.getWindowStyle();
isNativeWindow.value = windowStyle === "custom";
await nextTick();
electronAPI().changeTheme({
...props.dark ? darkTheme : lightTheme,
height: topMenuRef.value.getBoundingClientRect().height
});
}
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: normalizeClass(["font-sans w-screen h-screen flex flex-col pointer-events-auto", [
props.dark ? "text-neutral-300 bg-neutral-900 dark-theme" : "text-neutral-900 bg-neutral-300"
]])
}, [
withDirectives(createBaseVNode("div", {
ref_key: "topMenuRef",
ref: topMenuRef,
class: "app-drag w-full h-[var(--comfy-topbar-height)]"
}, null, 512), [
[vShow, isNativeWindow.value]
]),
createBaseVNode("div", _hoisted_1, [
renderSlot(_ctx.$slots, "default")
])
], 2);
};
}
});
export {
_sfc_main as _
};
//# sourceMappingURL=BaseViewTemplate-BhQMaVFP.js.map

22
web/assets/DesktopStartView-le6AjGZr.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
import { d as defineComponent, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, k as createVNode, j as unref, ch as script } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _hoisted_1 = { class: "max-w-screen-sm w-screen p-8" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "DesktopStartView",
setup(__props) {
return (_ctx, _cache) => {
return openBlock(), createBlock(_sfc_main$1, { dark: "" }, {
default: withCtx(() => [
createBaseVNode("div", _hoisted_1, [
createVNode(unref(script), { mode: "indeterminate" })
])
]),
_: 1
});
};
}
});
export {
_sfc_main as default
};
//# sourceMappingURL=DesktopStartView-le6AjGZr.js.map

View File

@@ -1,7 +1,7 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, o as openBlock, k as createBlock, M as withCtx, H as createBaseVNode, X as toDisplayString, N as createVNode, j as unref, l as script, bW as useRouter } from "./index-DjNHn37O.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BNGF4K22.js";
import { d as defineComponent, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, Z as toDisplayString, k as createVNode, j as unref, l as script, c2 as useRouter } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _hoisted_1 = { class: "max-w-screen-sm flex flex-col gap-8 p-8 bg-[url('/assets/images/Git-Logo-White.svg')] bg-no-repeat bg-right-top bg-origin-padding" };
const _hoisted_2 = { class: "mt-24 text-4xl font-bold text-red-500" };
const _hoisted_3 = { class: "space-y-4" };
@@ -55,4 +55,4 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
export {
_sfc_main as default
};
//# sourceMappingURL=DownloadGitView-DeC7MBzG.js.map
//# sourceMappingURL=DownloadGitView-rPK_vYgU.js.map

View File

@@ -1,9 +1,8 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, ab as ref, cn as FilterMatchMode, cs as useExtensionStore, a as useSettingStore, m as onMounted, c as computed, o as openBlock, k as createBlock, M as withCtx, N as createVNode, co as SearchBox, j as unref, bZ as script, H as createBaseVNode, f as createElementBlock, E as renderList, X as toDisplayString, aE as createTextVNode, F as Fragment, l as script$1, I as createCommentVNode, aI as script$3, bO as script$4, c4 as script$5, cp as _sfc_main$1 } from "./index-DjNHn37O.js";
import { s as script$2, a as script$6 } from "./index-B5F0uxTQ.js";
import "./index-B-aVupP5.js";
import "./index-5HFeZax4.js";
import { d as defineComponent, ad as ref, cu as FilterMatchMode, cz as useExtensionStore, a as useSettingStore, t as onMounted, c as computed, o as openBlock, J as createBlock, P as withCtx, k as createVNode, cv as SearchBox, j as unref, c6 as script, m as createBaseVNode, f as createElementBlock, I as renderList, Z as toDisplayString, aG as createTextVNode, H as Fragment, l as script$1, L as createCommentVNode, aK as script$3, b8 as script$4, cc as script$5, cw as _sfc_main$1 } from "./index-QvfM__ze.js";
import { s as script$2, a as script$6 } from "./index-DpF-ptbJ.js";
import "./index-Q1cQr26V.js";
const _hoisted_1 = { class: "flex justify-end" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "ExtensionPanel",
@@ -180,4 +179,4 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
export {
_sfc_main as default
};
//# sourceMappingURL=ExtensionPanel-D4Phn0Zr.js.map
//# sourceMappingURL=ExtensionPanel-3jWrm6Zi.js.map

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,10 @@
.comfy-menu-hamburger[data-v-5661bed0] {
pointer-events: auto;
position: fixed;
z-index: 9999;
.comfy-menu-hamburger[data-v-7ed57d1a] {
pointer-events: auto;
position: fixed;
z-index: 9999;
display: flex;
flex-direction: row
}
[data-v-e50caa15] .p-splitter-gutter {
@@ -39,14 +41,14 @@
z-index: 999;
}
.p-buttongroup-vertical[data-v-cf40dd39] {
.p-buttongroup-vertical[data-v-cb8f9a1a] {
display: flex;
flex-direction: column;
border-radius: var(--p-button-border-radius);
overflow: hidden;
border: 1px solid var(--p-panel-border-color);
}
.p-buttongroup-vertical .p-button[data-v-cf40dd39] {
.p-buttongroup-vertical .p-button[data-v-cb8f9a1a] {
margin: 0;
border-radius: 0;
}
@@ -82,7 +84,7 @@
font-size: inherit;
}
[data-v-5741c9ae] .highlight {
[data-v-fd0a74bd] .highlight {
background-color: var(--p-primary-color);
color: var(--p-primary-contrast-color);
font-weight: bold;
@@ -131,16 +133,7 @@
border-right: 4px solid var(--p-button-text-primary-color);
}
: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-37d8d7b4] {
.side-tool-bar-container[data-v-33cac83a] {
display: flex;
flex-direction: column;
align-items: center;
@@ -153,18 +146,91 @@
background-color: var(--comfy-menu-secondary-bg);
color: var(--fg-color);
box-shadow: var(--bar-shadow);
--sidebar-width: 4rem;
--sidebar-icon-size: 1.5rem;
}
.side-tool-bar-end[data-v-37d8d7b4] {
.side-tool-bar-container.small-sidebar[data-v-33cac83a] {
--sidebar-width: 2.5rem;
--sidebar-icon-size: 1rem;
}
.side-tool-bar-end[data-v-33cac83a] {
align-self: flex-end;
margin-top: auto;
}
[data-v-b9328350] .p-inputtext {
.status-indicator[data-v-8d011a31] {
position: absolute;
font-weight: 700;
font-size: 1.5rem;
top: 50%;
left: 50%;
transform: translate(-50%, -50%)
}
[data-v-54fadc45] .p-togglebutton {
position: relative;
flex-shrink: 0;
border-radius: 0px;
border-width: 0px;
border-right-width: 1px;
border-style: solid;
background-color: transparent;
padding: 0px;
border-right-color: var(--border-color)
}
[data-v-54fadc45] .p-togglebutton::before {
display: none
}
[data-v-54fadc45] .p-togglebutton:first-child {
border-left-width: 1px;
border-style: solid;
border-left-color: var(--border-color)
}
[data-v-54fadc45] .p-togglebutton:not(:first-child) {
border-left-width: 0px
}
[data-v-54fadc45] .p-togglebutton.p-togglebutton-checked {
height: 100%;
border-bottom-width: 1px;
border-style: solid;
border-bottom-color: var(--p-button-text-primary-color)
}
[data-v-54fadc45] .p-togglebutton:not(.p-togglebutton-checked) {
opacity: 0.75
}
[data-v-54fadc45] .p-togglebutton-checked .close-button,[data-v-54fadc45] .p-togglebutton:hover .close-button {
visibility: visible
}
[data-v-54fadc45] .p-togglebutton:hover .status-indicator {
display: none
}
[data-v-54fadc45] .p-togglebutton .close-button {
visibility: hidden
}
[data-v-54fadc45] .p-scrollpanel-content {
height: 100%
}
/* Scrollbar half opacity to avoid blocking the active tab bottom border */
[data-v-54fadc45] .p-scrollpanel:hover .p-scrollpanel-bar,[data-v-54fadc45] .p-scrollpanel:active .p-scrollpanel-bar {
opacity: 0.5
}
[data-v-54fadc45] .p-selectbutton {
height: 100%;
border-radius: 0px
}
[data-v-38831d8e] .workflow-tabs {
background-color: var(--comfy-menu-bg);
}
[data-v-26957f1f] .p-inputtext {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.comfyui-queue-button[data-v-7f4f551b] .p-splitbutton-dropdown {
.comfyui-queue-button[data-v-e9044686] .p-splitbutton-dropdown {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
@@ -195,55 +261,23 @@
display: none;
}
.top-menubar[data-v-6fecd137] .p-menubar-item-link svg {
.top-menubar[data-v-56df69d2] .p-menubar-item-link svg {
display: none;
}
[data-v-6fecd137] .p-menubar-submenu.dropdown-direction-up {
[data-v-56df69d2] .p-menubar-submenu.dropdown-direction-up {
top: auto;
bottom: 100%;
flex-direction: column-reverse;
}
.keybinding-tag[data-v-6fecd137] {
.keybinding-tag[data-v-56df69d2] {
background: var(--p-content-hover-background);
border-color: var(--p-content-border-color);
border-style: solid;
}
.status-indicator[data-v-8d011a31] {
position: absolute;
font-weight: 700;
font-size: 1.5rem;
top: 50%;
left: 50%;
transform: translate(-50%, -50%)
}
[data-v-d485c044] .p-togglebutton::before {
display: none
}
[data-v-d485c044] .p-togglebutton {
position: relative;
flex-shrink: 0;
border-radius: 0px;
background-color: transparent;
padding: 0px
}
[data-v-d485c044] .p-togglebutton.p-togglebutton-checked {
border-bottom-width: 2px;
border-bottom-color: var(--p-button-text-primary-color)
}
[data-v-d485c044] .p-togglebutton-checked .close-button,[data-v-d485c044] .p-togglebutton:hover .close-button {
visibility: visible
}
[data-v-d485c044] .p-togglebutton:hover .status-indicator {
display: none
}
[data-v-d485c044] .p-togglebutton .close-button {
visibility: hidden
}
.comfyui-menu[data-v-878b63b8] {
.comfyui-menu[data-v-6e35440f] {
width: 100vw;
height: var(--comfy-topbar-height);
background: var(--comfy-menu-bg);
color: var(--fg-color);
box-shadow: var(--bar-shadow);
@@ -253,18 +287,17 @@
z-index: 1000;
order: 0;
grid-column: 1/-1;
max-height: 90vh;
}
.comfyui-menu.dropzone[data-v-878b63b8] {
.comfyui-menu.dropzone[data-v-6e35440f] {
background: var(--p-highlight-background);
}
.comfyui-menu.dropzone-active[data-v-878b63b8] {
.comfyui-menu.dropzone-active[data-v-6e35440f] {
background: var(--p-highlight-background-focus);
}
[data-v-878b63b8] .p-menubar-item-label {
[data-v-6e35440f] .p-menubar-item-label {
line-height: revert;
}
.comfyui-logo[data-v-878b63b8] {
.comfyui-logo[data-v-6e35440f] {
font-size: 1.2em;
-webkit-user-select: none;
-moz-user-select: none;

View File

@@ -1,7 +1,7 @@
var __defProp = Object.defineProperty;
var __name = (target, value2) => __defProp(target, "name", { value: value2, configurable: true });
import { B as BaseStyle, q as script$6, o as openBlock, f as createElementBlock, D as mergeProps, c1 as findIndexInList, c2 as find, aB as resolveComponent, k as createBlock, G as resolveDynamicComponent, M as withCtx, H as createBaseVNode, X as toDisplayString, J as renderSlot, I as createCommentVNode, T as normalizeClass, P as findSingle, F as Fragment, aC as Transition, i as withDirectives, v as vShow, ak as UniqueComponentId, d as defineComponent, ab as ref, c3 as useModel, N as createVNode, j as unref, c4 as script$7, bQ as script$8, bM as withModifiers, aP as script$9, a1 as useI18n, c as computed, aI as script$a, aE as createTextVNode, c0 as electronAPI, m as onMounted, r as resolveDirective, av as script$b, c5 as script$c, c6 as script$d, l as script$e, bZ as script$f, c7 as MigrationItems, w as watchEffect, E as renderList, c8 as script$g, bW as useRouter, aL as pushScopeId, aM as popScopeId, aU as toRaw, _ as _export_sfc } from "./index-DjNHn37O.js";
import { _ as _sfc_main$5 } from "./BaseViewTemplate-BNGF4K22.js";
import { B as BaseStyle, y as script$6, o as openBlock, f as createElementBlock, G as mergeProps, c9 as findIndexInList, ca as find, aD as resolveComponent, J as createBlock, K as resolveDynamicComponent, P as withCtx, m as createBaseVNode, Z as toDisplayString, M as renderSlot, L as createCommentVNode, V as normalizeClass, R as findSingle, H as Fragment, aE as Transition, i as withDirectives, v as vShow, am as UniqueComponentId, d as defineComponent, ad as ref, cb as useModel, k as createVNode, j as unref, cc as script$7, c4 as script$8, b3 as withModifiers, aP as script$9, a3 as useI18n, c as computed, aK as script$a, aG as createTextVNode, p as pushScopeId, q as popScopeId, bV as electronAPI, _ as _export_sfc, t as onMounted, r as resolveDirective, ax as script$b, cd as script$c, ce as script$d, l as script$e, c6 as script$f, cf as MigrationItems, w as watchEffect, I as renderList, cg as script$g, c2 as useRouter, aU as toRaw } from "./index-QvfM__ze.js";
import { _ as _sfc_main$5 } from "./BaseViewTemplate-BhQMaVFP.js";
var classes$4 = {
root: /* @__PURE__ */ __name(function root(_ref) {
var instance = _ref.instance;
@@ -548,6 +548,12 @@ const _hoisted_15$2 = { class: "font-medium mb-2" };
const _hoisted_16$2 = { class: "list-disc pl-6 space-y-1" };
const _hoisted_17$2 = { class: "font-medium mt-4 mb-2" };
const _hoisted_18$2 = { class: "list-disc pl-6 space-y-1" };
const _hoisted_19 = { class: "mt-4" };
const _hoisted_20 = {
href: "https://comfy.org/privacy",
target: "_blank",
class: "text-blue-400 hover:text-blue-300 underline"
};
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
__name: "DesktopSettingsConfiguration",
props: {
@@ -608,17 +614,29 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
createBaseVNode("div", _hoisted_14$2, [
createBaseVNode("h4", _hoisted_15$2, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.whatWeCollect")), 1),
createBaseVNode("ul", _hoisted_16$2, [
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.errorReports")), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.systemInfo")), 1)
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.collect.errorReports")), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.collect.systemInfo")), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t(
"install.settings.dataCollectionDialog.collect.userJourneyEvents"
)), 1)
]),
createBaseVNode("h4", _hoisted_17$2, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.whatWeDoNotCollect")), 1),
createBaseVNode("ul", _hoisted_18$2, [
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.personalInformation")), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.workflowContents")), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.fileSystemInformation")), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t(
"install.settings.dataCollectionDialog.customNodeConfigurations"
"install.settings.dataCollectionDialog.doNotCollect.personalInformation"
)), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t(
"install.settings.dataCollectionDialog.doNotCollect.workflowContents"
)), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t(
"install.settings.dataCollectionDialog.doNotCollect.fileSystemInformation"
)), 1),
createBaseVNode("li", null, toDisplayString(_ctx.$t(
"install.settings.dataCollectionDialog.doNotCollect.customNodeConfigurations"
)), 1)
]),
createBaseVNode("div", _hoisted_19, [
createBaseVNode("a", _hoisted_20, toDisplayString(_ctx.$t("install.settings.dataCollectionDialog.viewFullPolicy")), 1)
])
])
]),
@@ -631,36 +649,37 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
const _imports_0 = "" + new URL("images/nvidia-logo.svg", import.meta.url).href;
const _imports_1 = "" + new URL("images/apple-mps-logo.png", import.meta.url).href;
const _imports_2 = "" + new URL("images/manual-configuration.svg", import.meta.url).href;
const _withScopeId$1 = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-79125ff6"), n = n(), popScopeId(), n), "_withScopeId$1");
const _hoisted_1$3 = { class: "flex flex-col gap-6 w-[600px] h-[30rem] select-none" };
const _hoisted_2$3 = { class: "grow flex flex-col gap-4 text-neutral-300" };
const _hoisted_3$3 = { class: "text-2xl font-semibold text-neutral-100" };
const _hoisted_4$3 = { class: "m-1 text-neutral-400" };
const _hoisted_5$2 = /* @__PURE__ */ createBaseVNode("img", {
const _hoisted_5$2 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("img", {
class: "m-12",
alt: "NVIDIA logo",
width: "196",
height: "32",
src: _imports_0
}, null, -1);
}, null, -1));
const _hoisted_6$2 = [
_hoisted_5$2
];
const _hoisted_7$2 = /* @__PURE__ */ createBaseVNode("img", {
const _hoisted_7$2 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("img", {
class: "rounded-lg hover-brighten",
alt: "Apple Metal Performance Shaders Logo",
width: "292",
ratio: "",
src: _imports_1
}, null, -1);
}, null, -1));
const _hoisted_8$2 = [
_hoisted_7$2
];
const _hoisted_9$2 = /* @__PURE__ */ createBaseVNode("img", {
const _hoisted_9$2 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("img", {
class: "m-12",
alt: "Manual configuration",
width: "196",
src: _imports_2
}, null, -1);
}, null, -1));
const _hoisted_10$2 = [
_hoisted_9$2
];
@@ -797,6 +816,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
};
}
});
const GpuPicker = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-79125ff6"]]);
const _hoisted_1$2 = { class: "flex flex-col gap-6 w-[600px]" };
const _hoisted_2$2 = { class: "flex flex-col gap-4" };
const _hoisted_3$2 = { class: "text-2xl font-semibold text-neutral-100" };
@@ -1082,7 +1102,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
};
}
});
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-de33872d"), n = n(), popScopeId(), n), "_withScopeId");
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-0a97b0ae"), n = n(), popScopeId(), n), "_withScopeId");
const _hoisted_1 = { class: "flex pt-6 justify-end" };
const _hoisted_2 = { class: "flex pt-6 justify-between" };
const _hoisted_3 = { class: "flex pt-6 justify-between" };
@@ -1098,6 +1118,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
const autoUpdate = ref(true);
const allowMetrics = ref(true);
const highestStep = ref(0);
const handleStepChange = /* @__PURE__ */ __name((value2) => {
setHighestStep(value2);
electronAPI().Events.trackEvent("install_stepper_change", {
step: value2
});
}, "handleStepChange");
const setHighestStep = /* @__PURE__ */ __name((value2) => {
const int = typeof value2 === "number" ? value2 : parseInt(value2, 10);
if (!isNaN(int) && int > highestStep.value) highestStep.value = int;
@@ -1122,8 +1148,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
onMounted(async () => {
if (!electron) return;
const detectedGpu = await electron.Config.getDetectedGpu();
if (detectedGpu === "mps" || detectedGpu === "nvidia")
if (detectedGpu === "mps" || detectedGpu === "nvidia") {
device.value = detectedGpu;
}
electronAPI().Events.trackEvent("install_stepper_change", {
step: "0",
gpu: detectedGpu
});
});
return (_ctx, _cache) => {
return openBlock(), createBlock(_sfc_main$5, { dark: "" }, {
@@ -1131,7 +1162,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
createVNode(unref(script), {
class: "h-full p-8 2xl:p-16",
value: "0",
"onUpdate:value": setHighestStep
"onUpdate:value": handleStepChange
}, {
default: withCtx(() => [
createVNode(unref(script$4), { class: "select-none" }, {
@@ -1176,7 +1207,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
default: withCtx(() => [
createVNode(unref(script$3), { value: "0" }, {
default: withCtx(({ activateCallback }) => [
createVNode(_sfc_main$3, {
createVNode(GpuPicker, {
device: device.value,
"onUpdate:device": _cache[0] || (_cache[0] = ($event) => device.value = $event)
}, null, 8, ["device"]),
@@ -1281,8 +1312,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
};
}
});
const InstallView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-de33872d"]]);
const InstallView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-0a97b0ae"]]);
export {
InstallView as default
};
//# sourceMappingURL=InstallView-CAcYt0HL.js.map
//# sourceMappingURL=InstallView-By3hC1fC.js.map

View File

@@ -1,18 +1,18 @@
:root {
.p-tag[data-v-79125ff6] {
--p-tag-gap: 0.5rem;
}
.hover-brighten {
.hover-brighten[data-v-79125ff6] {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
transition-property: filter, box-shadow;
&:hover {
&[data-v-79125ff6]:hover {
filter: brightness(107%) contrast(105%);
box-shadow: 0 0 0.25rem #ffffff79;
}
}
.p-accordioncontent-content {
.p-accordioncontent-content[data-v-79125ff6] {
border-radius: 0.5rem;
--tw-bg-opacity: 1;
background-color: rgb(23 23 23 / var(--tw-bg-opacity));
@@ -20,15 +20,15 @@
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
div.selected {
.gpu-button:not(.selected) {
div.selected[data-v-79125ff6] {
.gpu-button[data-v-79125ff6]:not(.selected) {
opacity: 0.5;
}
.gpu-button:not(.selected):hover {
.gpu-button[data-v-79125ff6]:not(.selected):hover {
opacity: 1;
}
}
.gpu-button {
.gpu-button[data-v-79125ff6] {
margin: 0px;
display: flex;
width: 50%;
@@ -43,37 +43,37 @@ div.selected {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.gpu-button:hover {
.gpu-button[data-v-79125ff6]:hover {
--tw-bg-opacity: 0.75;
}
.gpu-button {
&.selected {
.gpu-button[data-v-79125ff6] {
&.selected[data-v-79125ff6] {
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity));
}
&.selected {
&.selected[data-v-79125ff6] {
--tw-bg-opacity: 0.5;
}
&.selected {
&.selected[data-v-79125ff6] {
opacity: 1;
}
&.selected:hover {
&.selected[data-v-79125ff6]:hover {
--tw-bg-opacity: 0.6;
}
}
.disabled {
.disabled[data-v-79125ff6] {
pointer-events: none;
opacity: 0.4;
}
.p-card-header {
.p-card-header[data-v-79125ff6] {
flex-grow: 1;
text-align: center;
}
.p-card-body {
.p-card-body[data-v-79125ff6] {
padding-top: 0px;
text-align: center;
}
[data-v-de33872d] .p-steppanel {
[data-v-0a97b0ae] .p-steppanel {
background-color: transparent
}

View File

@@ -1,10 +1,9 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, c as computed, o as openBlock, f as createElementBlock, F as Fragment, E as renderList, N as createVNode, M as withCtx, aE as createTextVNode, X as toDisplayString, j as unref, aI as script, I as createCommentVNode, ab as ref, cn as FilterMatchMode, a$ as useKeybindingStore, a2 as useCommandStore, a1 as useI18n, af as normalizeI18nKey, w as watchEffect, bs as useToast, r as resolveDirective, k as createBlock, co as SearchBox, H as createBaseVNode, l as script$2, av as script$4, bM as withModifiers, bZ as script$5, aP as script$6, i as withDirectives, cp as _sfc_main$2, aL as pushScopeId, aM as popScopeId, cq as KeyComboImpl, cr as KeybindingImpl, _ as _export_sfc } from "./index-DjNHn37O.js";
import { s as script$1, a as script$3 } from "./index-B5F0uxTQ.js";
import { u as useKeybindingService } from "./keybindingService-Bx7YdkXn.js";
import "./index-B-aVupP5.js";
import "./index-5HFeZax4.js";
import { d as defineComponent, c as computed, o as openBlock, f as createElementBlock, H as Fragment, I as renderList, k as createVNode, P as withCtx, aG as createTextVNode, Z as toDisplayString, j as unref, aK as script, L as createCommentVNode, ad as ref, cu as FilterMatchMode, a$ as useKeybindingStore, a4 as useCommandStore, a3 as useI18n, ah as normalizeI18nKey, w as watchEffect, bz as useToast, r as resolveDirective, J as createBlock, cv as SearchBox, m as createBaseVNode, l as script$2, ax as script$4, b3 as withModifiers, c6 as script$5, aP as script$6, i as withDirectives, cw as _sfc_main$2, p as pushScopeId, q as popScopeId, cx as KeyComboImpl, cy as KeybindingImpl, _ as _export_sfc } from "./index-QvfM__ze.js";
import { s as script$1, a as script$3 } from "./index-DpF-ptbJ.js";
import { u as useKeybindingService } from "./keybindingService-Cak1En5n.js";
import "./index-Q1cQr26V.js";
const _hoisted_1$1 = {
key: 0,
class: "px-2"
@@ -281,4 +280,4 @@ const KeybindingPanel = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "d
export {
KeybindingPanel as default
};
//# sourceMappingURL=KeybindingPanel-Dc3C4lG1.js.map
//# sourceMappingURL=KeybindingPanel-D6O16W_1.js.map

View File

@@ -1,7 +1,7 @@
:root {
.p-tag[data-v-dc169863] {
--p-tag-gap: 0.5rem;
}
.comfy-installer {
.comfy-installer[data-v-dc169863] {
margin-top: max(1rem, max(0px, calc((100vh - 42rem) * 0.5)));
}

View File

@@ -1,9 +1,8 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, a1 as useI18n, ab as ref, m as onMounted, o as openBlock, k as createBlock, M as withCtx, H as createBaseVNode, X as toDisplayString, N as createVNode, j as unref, aI as script, l as script$2, c0 as electronAPI } from "./index-DjNHn37O.js";
import { s as script$1 } from "./index-jXPKy3pP.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BNGF4K22.js";
import "./index-5HFeZax4.js";
import { d as defineComponent, a3 as useI18n, ad as ref, t as onMounted, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, Z as toDisplayString, k as createVNode, j as unref, aK as script, bN as script$1, l as script$2, p as pushScopeId, q as popScopeId, bV as electronAPI, _ as _export_sfc } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-dc169863"), n = n(), popScopeId(), n), "_withScopeId");
const _hoisted_1 = { class: "comfy-installer grow flex flex-col gap-4 text-neutral-300 max-w-110" };
const _hoisted_2 = { class: "text-2xl font-semibold text-neutral-100" };
const _hoisted_3 = { class: "m-1 text-neutral-300" };
@@ -69,7 +68,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
};
}
});
const ManualConfigurationView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-dc169863"]]);
export {
_sfc_main as default
ManualConfigurationView as default
};
//# sourceMappingURL=ManualConfigurationView-Bi_qHE-n.js.map
//# sourceMappingURL=ManualConfigurationView-enyqGo0M.js.map

86
web/assets/MetricsConsentView-lSfLu4nr.js generated vendored Normal file
View File

@@ -0,0 +1,86 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
import { d as defineComponent, bz as useToast, a3 as useI18n, ad as ref, c2 as useRouter, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, Z as toDisplayString, aG as createTextVNode, k as createVNode, j as unref, cc as script, l as script$1, bV as electronAPI } from "./index-QvfM__ze.js";
const _hoisted_1 = { class: "h-full p-8 2xl:p-16 flex flex-col items-center justify-center" };
const _hoisted_2 = { class: "bg-neutral-800 rounded-lg shadow-lg p-6 w-full max-w-[600px] flex flex-col gap-6" };
const _hoisted_3 = { class: "text-3xl font-semibold text-neutral-100" };
const _hoisted_4 = { class: "text-neutral-400" };
const _hoisted_5 = { class: "text-neutral-400" };
const _hoisted_6 = {
href: "https://comfy.org/privacy",
target: "_blank",
class: "text-blue-400 hover:text-blue-300 underline"
};
const _hoisted_7 = { class: "flex items-center gap-4" };
const _hoisted_8 = {
id: "metricsDescription",
class: "text-neutral-100"
};
const _hoisted_9 = { class: "flex pt-6 justify-end" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "MetricsConsentView",
setup(__props) {
const toast = useToast();
const { t } = useI18n();
const allowMetrics = ref(true);
const router = useRouter();
const isUpdating = ref(false);
const updateConsent = /* @__PURE__ */ __name(async () => {
isUpdating.value = true;
try {
await electronAPI().setMetricsConsent(allowMetrics.value);
} catch (error) {
toast.add({
severity: "error",
summary: t("install.errorUpdatingConsent"),
detail: t("install.errorUpdatingConsentDetail"),
life: 3e3
});
} finally {
isUpdating.value = false;
}
router.push("/");
}, "updateConsent");
return (_ctx, _cache) => {
const _component_BaseViewTemplate = _sfc_main$1;
return openBlock(), createBlock(_component_BaseViewTemplate, { dark: "" }, {
default: withCtx(() => [
createBaseVNode("div", _hoisted_1, [
createBaseVNode("div", _hoisted_2, [
createBaseVNode("h2", _hoisted_3, toDisplayString(_ctx.$t("install.helpImprove")), 1),
createBaseVNode("p", _hoisted_4, toDisplayString(_ctx.$t("install.updateConsent")), 1),
createBaseVNode("p", _hoisted_5, [
createTextVNode(toDisplayString(_ctx.$t("install.moreInfo")) + " ", 1),
createBaseVNode("a", _hoisted_6, toDisplayString(_ctx.$t("install.privacyPolicy")), 1),
createTextVNode(". ")
]),
createBaseVNode("div", _hoisted_7, [
createVNode(unref(script), {
modelValue: allowMetrics.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => allowMetrics.value = $event),
"aria-describedby": "metricsDescription"
}, null, 8, ["modelValue"]),
createBaseVNode("span", _hoisted_8, toDisplayString(allowMetrics.value ? _ctx.$t("install.metricsEnabled") : _ctx.$t("install.metricsDisabled")), 1)
]),
createBaseVNode("div", _hoisted_9, [
createVNode(unref(script$1), {
label: _ctx.$t("g.ok"),
icon: "pi pi-check",
loading: isUpdating.value,
iconPos: "right",
onClick: updateConsent
}, null, 8, ["label", "loading"])
])
])
])
]),
_: 1
});
};
}
});
export {
_sfc_main as default
};
//# sourceMappingURL=MetricsConsentView-lSfLu4nr.js.map

View File

@@ -1,17 +1,17 @@
.sad-container {
.sad-container[data-v-ebb20958] {
display: grid;
align-items: center;
justify-content: space-evenly;
grid-template-columns: 25rem 1fr;
& > * {
&[data-v-ebb20958] > * {
grid-row: 1;
}
}
.sad-text {
.sad-text[data-v-ebb20958] {
grid-column: 1/3;
}
.sad-girl {
.sad-girl[data-v-ebb20958] {
grid-column: 2/3;
width: min(75vw, 100vh);
}

View File

@@ -1,14 +1,15 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, bW as useRouter, r as resolveDirective, o as openBlock, k as createBlock, M as withCtx, H as createBaseVNode, X as toDisplayString, N as createVNode, j as unref, l as script, i as withDirectives } from "./index-DjNHn37O.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BNGF4K22.js";
import { d as defineComponent, c2 as useRouter, r as resolveDirective, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, Z as toDisplayString, k as createVNode, j as unref, l as script, i as withDirectives, p as pushScopeId, q as popScopeId, _ as _export_sfc } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _imports_0 = "" + new URL("images/sad_girl.png", import.meta.url).href;
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-ebb20958"), n = n(), popScopeId(), n), "_withScopeId");
const _hoisted_1 = { class: "sad-container" };
const _hoisted_2 = /* @__PURE__ */ createBaseVNode("img", {
const _hoisted_2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("img", {
class: "sad-girl",
src: _imports_0,
alt: "Sad girl illustration"
}, null, -1);
}, null, -1));
const _hoisted_3 = { class: "no-drag sad-text flex items-center" };
const _hoisted_4 = { class: "flex flex-col gap-8 p-8 min-w-110" };
const _hoisted_5 = { class: "text-4xl font-bold text-red-500" };
@@ -80,7 +81,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
};
}
});
const NotSupportedView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-ebb20958"]]);
export {
_sfc_main as default
NotSupportedView as default
};
//# sourceMappingURL=NotSupportedView-Drz3x2d-.js.map
//# sourceMappingURL=NotSupportedView-Vc8_xWgH.js.map

View File

@@ -1,7 +1,7 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { H as createBaseVNode, o as openBlock, f as createElementBlock, Z as markRaw, d as defineComponent, a as useSettingStore, aS as storeToRefs, a5 as watch, cO as useCopyToClipboard, a1 as useI18n, k as createBlock, M as withCtx, j as unref, bZ as script, X as toDisplayString, E as renderList, F as Fragment, N as createVNode, l as script$1, I as createCommentVNode, bQ as script$2, cP as FormItem, cp as _sfc_main$1, c0 as electronAPI } from "./index-DjNHn37O.js";
import { u as useServerConfigStore } from "./serverConfigStore-CvyKFVuP.js";
import { m as createBaseVNode, o as openBlock, f as createElementBlock, a0 as markRaw, d as defineComponent, a as useSettingStore, aS as storeToRefs, a7 as watch, cW as useCopyToClipboard, a3 as useI18n, J as createBlock, P as withCtx, j as unref, c6 as script, Z as toDisplayString, I as renderList, H as Fragment, k as createVNode, l as script$1, L as createCommentVNode, c4 as script$2, cX as FormItem, cw as _sfc_main$1, bV as electronAPI } from "./index-QvfM__ze.js";
import { u as useServerConfigStore } from "./serverConfigStore-DCme3xlV.js";
const _hoisted_1$1 = {
viewBox: "0 0 24 24",
width: "1.2em",
@@ -155,4 +155,4 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
export {
_sfc_main as default
};
//# sourceMappingURL=ServerConfigPanel-Be4StJmv.js.map
//# sourceMappingURL=ServerConfigPanel-B-w0HFlz.js.map

101
web/assets/ServerStartView-48wfE1MS.js generated vendored Normal file
View File

@@ -0,0 +1,101 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, a3 as useI18n, ad as ref, c7 as ProgressStatus, t as onMounted, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, aG as createTextVNode, Z as toDisplayString, j as unref, f as createElementBlock, L as createCommentVNode, k as createVNode, l as script, i as withDirectives, v as vShow, c8 as BaseTerminal, p as pushScopeId, q as popScopeId, bV as electronAPI, _ as _export_sfc } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-4140d62b"), n = n(), popScopeId(), n), "_withScopeId");
const _hoisted_1 = { class: "flex flex-col w-full h-full items-center" };
const _hoisted_2 = { class: "text-2xl font-bold" };
const _hoisted_3 = { key: 0 };
const _hoisted_4 = {
key: 0,
class: "flex flex-col items-center gap-4"
};
const _hoisted_5 = { class: "flex items-center my-4 gap-2" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "ServerStartView",
setup(__props) {
const electron = electronAPI();
const { t } = useI18n();
const status = ref(ProgressStatus.INITIAL_STATE);
const electronVersion = ref("");
let xterm;
const terminalVisible = ref(true);
const updateProgress = /* @__PURE__ */ __name(({ status: newStatus }) => {
status.value = newStatus;
if (newStatus === ProgressStatus.ERROR) terminalVisible.value = false;
else xterm?.clear();
}, "updateProgress");
const terminalCreated = /* @__PURE__ */ __name(({ terminal, useAutoSize }, root) => {
xterm = terminal;
useAutoSize({ root, autoRows: true, autoCols: true });
electron.onLogMessage((message) => {
terminal.write(message);
});
terminal.options.cursorBlink = false;
terminal.options.disableStdin = true;
terminal.options.cursorInactiveStyle = "block";
}, "terminalCreated");
const reinstall = /* @__PURE__ */ __name(() => electron.reinstall(), "reinstall");
const reportIssue = /* @__PURE__ */ __name(() => {
window.open("https://forum.comfy.org/c/v1-feedback/", "_blank");
}, "reportIssue");
const openLogs = /* @__PURE__ */ __name(() => electron.openLogsFolder(), "openLogs");
onMounted(async () => {
electron.sendReady();
electron.onProgressUpdate(updateProgress);
electronVersion.value = await electron.getElectronVersion();
});
return (_ctx, _cache) => {
return openBlock(), createBlock(_sfc_main$1, {
dark: "",
class: "flex-col"
}, {
default: withCtx(() => [
createBaseVNode("div", _hoisted_1, [
createBaseVNode("h2", _hoisted_2, [
createTextVNode(toDisplayString(unref(t)(`serverStart.process.${status.value}`)) + " ", 1),
status.value === unref(ProgressStatus).ERROR ? (openBlock(), createElementBlock("span", _hoisted_3, " v" + toDisplayString(electronVersion.value), 1)) : createCommentVNode("", true)
]),
status.value === unref(ProgressStatus).ERROR ? (openBlock(), createElementBlock("div", _hoisted_4, [
createBaseVNode("div", _hoisted_5, [
createVNode(unref(script), {
icon: "pi pi-flag",
severity: "secondary",
label: unref(t)("serverStart.reportIssue"),
onClick: reportIssue
}, null, 8, ["label"]),
createVNode(unref(script), {
icon: "pi pi-file",
severity: "secondary",
label: unref(t)("serverStart.openLogs"),
onClick: openLogs
}, null, 8, ["label"]),
createVNode(unref(script), {
icon: "pi pi-refresh",
label: unref(t)("serverStart.reinstall"),
onClick: reinstall
}, null, 8, ["label"])
]),
!terminalVisible.value ? (openBlock(), createBlock(unref(script), {
key: 0,
icon: "pi pi-search",
severity: "secondary",
label: unref(t)("serverStart.showTerminal"),
onClick: _cache[0] || (_cache[0] = ($event) => terminalVisible.value = true)
}, null, 8, ["label"])) : createCommentVNode("", true)
])) : createCommentVNode("", true),
withDirectives(createVNode(BaseTerminal, { onCreated: terminalCreated }, null, 512), [
[vShow, terminalVisible.value]
])
])
]),
_: 1
});
};
}
});
const ServerStartView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4140d62b"]]);
export {
ServerStartView as default
};
//# sourceMappingURL=ServerStartView-48wfE1MS.js.map

View File

@@ -1,98 +0,0 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, a1 as useI18n, ab as ref, b_ as ProgressStatus, m as onMounted, o as openBlock, k as createBlock, M as withCtx, H as createBaseVNode, aE as createTextVNode, X as toDisplayString, j as unref, f as createElementBlock, I as createCommentVNode, N as createVNode, l as script, i as withDirectives, v as vShow, b$ as BaseTerminal, aL as pushScopeId, aM as popScopeId, c0 as electronAPI, _ as _export_sfc } from "./index-DjNHn37O.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BNGF4K22.js";
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-42c1131d"), n = n(), popScopeId(), n), "_withScopeId");
const _hoisted_1 = { class: "text-2xl font-bold" };
const _hoisted_2 = { key: 0 };
const _hoisted_3 = {
key: 0,
class: "flex flex-col items-center gap-4"
};
const _hoisted_4 = { class: "flex items-center my-4 gap-2" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "ServerStartView",
setup(__props) {
const electron = electronAPI();
const { t } = useI18n();
const status = ref(ProgressStatus.INITIAL_STATE);
const electronVersion = ref("");
let xterm;
const terminalVisible = ref(true);
const updateProgress = /* @__PURE__ */ __name(({ status: newStatus }) => {
status.value = newStatus;
if (newStatus === ProgressStatus.ERROR) terminalVisible.value = false;
else xterm?.clear();
}, "updateProgress");
const terminalCreated = /* @__PURE__ */ __name(({ terminal, useAutoSize }, root) => {
xterm = terminal;
useAutoSize(root, true, true);
electron.onLogMessage((message) => {
terminal.write(message);
});
terminal.options.cursorBlink = false;
terminal.options.disableStdin = true;
terminal.options.cursorInactiveStyle = "block";
}, "terminalCreated");
const reinstall = /* @__PURE__ */ __name(() => electron.reinstall(), "reinstall");
const reportIssue = /* @__PURE__ */ __name(() => {
window.open("https://forum.comfy.org/c/v1-feedback/", "_blank");
}, "reportIssue");
const openLogs = /* @__PURE__ */ __name(() => electron.openLogsFolder(), "openLogs");
onMounted(async () => {
electron.sendReady();
electron.onProgressUpdate(updateProgress);
electronVersion.value = await electron.getElectronVersion();
});
return (_ctx, _cache) => {
return openBlock(), createBlock(_sfc_main$1, {
dark: "",
class: "flex-col"
}, {
default: withCtx(() => [
createBaseVNode("h2", _hoisted_1, [
createTextVNode(toDisplayString(unref(t)(`serverStart.process.${status.value}`)) + " ", 1),
status.value === unref(ProgressStatus).ERROR ? (openBlock(), createElementBlock("span", _hoisted_2, " v" + toDisplayString(electronVersion.value), 1)) : createCommentVNode("", true)
]),
status.value === unref(ProgressStatus).ERROR ? (openBlock(), createElementBlock("div", _hoisted_3, [
createBaseVNode("div", _hoisted_4, [
createVNode(unref(script), {
icon: "pi pi-flag",
severity: "secondary",
label: unref(t)("serverStart.reportIssue"),
onClick: reportIssue
}, null, 8, ["label"]),
createVNode(unref(script), {
icon: "pi pi-file",
severity: "secondary",
label: unref(t)("serverStart.openLogs"),
onClick: openLogs
}, null, 8, ["label"]),
createVNode(unref(script), {
icon: "pi pi-refresh",
label: unref(t)("serverStart.reinstall"),
onClick: reinstall
}, null, 8, ["label"])
]),
!terminalVisible.value ? (openBlock(), createBlock(unref(script), {
key: 0,
icon: "pi pi-search",
severity: "secondary",
label: unref(t)("serverStart.showTerminal"),
onClick: _cache[0] || (_cache[0] = ($event) => terminalVisible.value = true)
}, null, 8, ["label"])) : createCommentVNode("", true)
])) : createCommentVNode("", true),
withDirectives(createVNode(BaseTerminal, { onCreated: terminalCreated }, null, 512), [
[vShow, terminalVisible.value]
])
]),
_: 1
});
};
}
});
const ServerStartView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-42c1131d"]]);
export {
ServerStartView as default
};
//# sourceMappingURL=ServerStartView-CIDTUh4x.js.map

View File

@@ -1,5 +1,5 @@
[data-v-42c1131d] .xterm-helper-textarea {
[data-v-4140d62b] .xterm-helper-textarea {
/* Hide this as it moves all over when uv is running */
display: none;
}

View File

@@ -1,7 +1,7 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, aX as useUserStore, bW as useRouter, ab as ref, c as computed, m as onMounted, o as openBlock, k as createBlock, M as withCtx, H as createBaseVNode, X as toDisplayString, N as createVNode, bX as withKeys, j as unref, av as script, bQ as script$1, bY as script$2, bZ as script$3, aE as createTextVNode, I as createCommentVNode, l as script$4 } from "./index-DjNHn37O.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BNGF4K22.js";
import { d as defineComponent, aX as useUserStore, c2 as useRouter, ad as ref, c as computed, t as onMounted, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, Z as toDisplayString, k as createVNode, c3 as withKeys, j as unref, ax as script, c4 as script$1, c5 as script$2, c6 as script$3, aG as createTextVNode, L as createCommentVNode, l as script$4 } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _hoisted_1 = {
id: "comfy-user-selection",
class: "min-w-84 relative rounded-lg bg-[var(--comfy-menu-bg)] p-5 px-10 shadow-lg"
@@ -99,4 +99,4 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
export {
_sfc_main as default
};
//# sourceMappingURL=UserSelectView-B3jYchWu.js.map
//# sourceMappingURL=UserSelectView-CXmVKOeK.js.map

View File

@@ -1,7 +1,7 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { d as defineComponent, bW as useRouter, o as openBlock, k as createBlock, M as withCtx, H as createBaseVNode, X as toDisplayString, N as createVNode, j as unref, l as script, aL as pushScopeId, aM as popScopeId, _ as _export_sfc } from "./index-DjNHn37O.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BNGF4K22.js";
import { d as defineComponent, c2 as useRouter, o as openBlock, J as createBlock, P as withCtx, m as createBaseVNode, Z as toDisplayString, k as createVNode, j as unref, l as script, p as pushScopeId, q as popScopeId, _ as _export_sfc } from "./index-QvfM__ze.js";
import { _ as _sfc_main$1 } from "./BaseViewTemplate-BhQMaVFP.js";
const _withScopeId = /* @__PURE__ */ __name((n) => (pushScopeId("data-v-7dfaf74c"), n = n(), popScopeId(), n), "_withScopeId");
const _hoisted_1 = { class: "flex flex-col items-center justify-center gap-8 p-8" };
const _hoisted_2 = { class: "animated-gradient-text text-glow select-none" };
@@ -37,4 +37,4 @@ const WelcomeView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-
export {
WelcomeView as default
};
//# sourceMappingURL=WelcomeView-N0ZXLjdi.js.map
//# sourceMappingURL=WelcomeView-C8whKl15.js.map

27
web/assets/index-5HFeZax4.js generated vendored
View File

@@ -1,27 +0,0 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { ct as script$1, H as createBaseVNode, o as openBlock, f as createElementBlock, D as mergeProps } from "./index-DjNHn37O.js";
var script = {
name: "PlusIcon",
"extends": script$1
};
var _hoisted_1 = /* @__PURE__ */ createBaseVNode("path", {
d: "M7.67742 6.32258V0.677419C7.67742 0.497757 7.60605 0.325452 7.47901 0.198411C7.35197 0.0713707 7.17966 0 7 0C6.82034 0 6.64803 0.0713707 6.52099 0.198411C6.39395 0.325452 6.32258 0.497757 6.32258 0.677419V6.32258H0.677419C0.497757 6.32258 0.325452 6.39395 0.198411 6.52099C0.0713707 6.64803 0 6.82034 0 7C0 7.17966 0.0713707 7.35197 0.198411 7.47901C0.325452 7.60605 0.497757 7.67742 0.677419 7.67742H6.32258V13.3226C6.32492 13.5015 6.39704 13.6725 6.52358 13.799C6.65012 13.9255 6.82106 13.9977 7 14C7.17966 14 7.35197 13.9286 7.47901 13.8016C7.60605 13.6745 7.67742 13.5022 7.67742 13.3226V7.67742H13.3226C13.5022 7.67742 13.6745 7.60605 13.8016 7.47901C13.9286 7.35197 14 7.17966 14 7C13.9977 6.82106 13.9255 6.65012 13.799 6.52358C13.6725 6.39704 13.5015 6.32492 13.3226 6.32258H7.67742Z",
fill: "currentColor"
}, null, -1);
var _hoisted_2 = [_hoisted_1];
function render(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("svg", mergeProps({
width: "14",
height: "14",
viewBox: "0 0 14 14",
fill: "none",
xmlns: "http://www.w3.org/2000/svg"
}, _ctx.pti()), _hoisted_2, 16);
}
__name(render, "render");
script.render = render;
export {
script as s
};
//# sourceMappingURL=index-5HFeZax4.js.map

View File

@@ -2131,6 +2131,9 @@
.z-\[1000\]{
z-index: 1000;
}
.z-\[9999\]{
z-index: 9999;
}
.m-0{
margin: 0px;
}
@@ -2253,6 +2256,9 @@
.h-0{
height: 0px;
}
.h-1{
height: 0.25rem;
}
.h-16{
height: 4rem;
}
@@ -2271,6 +2277,9 @@
.h-\[30rem\]{
height: 30rem;
}
.h-\[var\(--comfy-topbar-height\)\]{
height: var(--comfy-topbar-height);
}
.h-full{
height: 100%;
}
@@ -2341,6 +2350,9 @@
.w-screen{
width: 100vw;
}
.min-w-0{
min-width: 0px;
}
.min-w-110{
min-width: 32rem;
}
@@ -2359,6 +2371,9 @@
.max-w-\[150px\]{
max-width: 150px;
}
.max-w-\[600px\]{
max-width: 600px;
}
.max-w-full{
max-width: 100%;
}
@@ -2519,6 +2534,9 @@
.text-wrap{
text-wrap: wrap;
}
.text-nowrap{
text-wrap: nowrap;
}
.rounded{
border-radius: 0.25rem;
}
@@ -2528,16 +2546,35 @@
.rounded-none{
border-radius: 0px;
}
.rounded-t-lg{
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
}
.border{
border-width: 1px;
}
.border-0{
border-width: 0px;
}
.border-x-0{
border-left-width: 0px;
border-right-width: 0px;
}
.border-b{
border-bottom-width: 1px;
}
.border-l{
border-left-width: 1px;
}
.border-r{
border-right-width: 1px;
}
.border-t-0{
border-top-width: 0px;
}
.border-solid{
border-style: solid;
}
.border-none{
border-style: none;
}
@@ -2635,6 +2672,9 @@
.p-5{
padding: 1.25rem;
}
.p-6{
padding: 1.5rem;
}
.p-8{
padding: 2rem;
}
@@ -2701,6 +2741,9 @@
.text-2xl{
font-size: 1.5rem;
}
.text-3xl{
font-size: 1.875rem;
}
.text-4xl{
font-size: 2.25rem;
}
@@ -2783,6 +2826,9 @@
--tw-text-opacity: 1;
color: rgb(239 68 68 / var(--tw-text-opacity));
}
.underline{
text-decoration-line: underline;
}
.no-underline{
text-decoration-line: none;
}
@@ -2868,6 +2914,7 @@
--bg-color: #fff;
--comfy-menu-bg: #353535;
--comfy-menu-secondary-bg: #292929;
--comfy-topbar-height: 2.5rem;
--comfy-input-bg: #222;
--input-text: #ddd;
--descrip-text: #999;
@@ -3625,24 +3672,33 @@ audio.comfy-audio.empty-audio-widget {
padding: var(--comfy-tree-explorer-item-padding) !important;
}
/* [Desktop] Electron window specific styles */
.app-drag {
app-region: drag;
}
.no-drag {
app-region: no-drag;
}
.window-actions-spacer {
width: calc(100vw - env(titlebar-area-width, 100vw));
}
/* End of [Desktop] Electron window specific styles */
.hover\:bg-neutral-700:hover{
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity));
}
.hover\:bg-opacity-75:hover{
--tw-bg-opacity: 0.75;
}
.hover\:text-blue-300:hover{
--tw-text-opacity: 1;
color: rgb(144 205 244 / var(--tw-text-opacity));
}
.hover\:opacity-100:hover{
opacity: 1;
}
@media (min-width: 768px){
.md\:flex{
@@ -3653,7 +3709,6 @@ audio.comfy-audio.empty-audio-widget {
display: none;
}
}
@media (min-width: 1536px){
.\32xl\:mx-4{
@@ -3689,8 +3744,11 @@ audio.comfy-audio.empty-audio-widget {
padding-left: 1rem;
padding-right: 1rem;
}
}
.\32xl\:text-sm{
font-size: 0.875rem;
}
}
@media (prefers-color-scheme: dark){
.dark\:bg-gray-800{
@@ -3740,17 +3798,17 @@ audio.comfy-audio.empty-audio-widget {
margin-bottom: 1rem;
}
.comfy-error-report[data-v-ddf3e2da] {
.comfy-error-report[data-v-09b72a20] {
display: flex;
flex-direction: column;
gap: 1rem;
}
.action-container[data-v-ddf3e2da] {
.action-container[data-v-09b72a20] {
display: flex;
gap: 1rem;
justify-content: flex-end;
}
.wrapper-pre[data-v-ddf3e2da] {
.wrapper-pre[data-v-09b72a20] {
white-space: pre-wrap;
word-wrap: break-word;
}
@@ -3834,7 +3892,7 @@ audio.comfy-audio.empty-audio-widget {
padding-top: 0px !important;
}
.settings-container[data-v-67f71ae9] {
.settings-container[data-v-2e21278f] {
display: flex;
height: 70vh;
width: 60vw;
@@ -3842,25 +3900,25 @@ audio.comfy-audio.empty-audio-widget {
overflow: hidden;
}
@media (max-width: 768px) {
.settings-container[data-v-67f71ae9] {
.settings-container[data-v-2e21278f] {
flex-direction: column;
height: auto;
width: 80vw;
}
.settings-sidebar[data-v-67f71ae9] {
.settings-sidebar[data-v-2e21278f] {
width: 100%;
}
.settings-content[data-v-67f71ae9] {
.settings-content[data-v-2e21278f] {
height: 350px;
}
}
/* Show a separator line above the Keybinding tab */
/* This indicates the start of custom setting panels */
.settings-sidebar[data-v-67f71ae9] .p-listbox-option[aria-label='Keybinding'] {
.settings-sidebar[data-v-2e21278f] .p-listbox-option[aria-label='Keybinding'] {
position: relative;
}
.settings-sidebar[data-v-67f71ae9] .p-listbox-option[aria-label='Keybinding']::before {
.settings-sidebar[data-v-2e21278f] .p-listbox-option[aria-label='Keybinding']::before {
position: absolute;
top: 0px;
left: 0px;
@@ -3878,15 +3936,15 @@ audio.comfy-audio.empty-audio-widget {
margin-left: 0.5rem;
}
.p-card[data-v-d65acb9a] {
.p-card[data-v-ffc83afa] {
--p-card-body-padding: 10px 0 0 0;
overflow: hidden;
}
[data-v-d65acb9a] .p-card-subtitle {
[data-v-ffc83afa] .p-card-subtitle {
text-align: center;
}
.carousel[data-v-fc26284b] {
.carousel[data-v-d9962275] {
width: 66vw;
}
/**
@@ -4123,18 +4181,18 @@ audio.comfy-audio.empty-audio-widget {
overflow-y: hidden;
}
[data-v-6187144a] .p-terminal .xterm {
[data-v-90a7f075] .p-terminal .xterm {
overflow-x: auto;
}
[data-v-6187144a] .p-terminal .xterm-screen {
[data-v-90a7f075] .p-terminal .xterm-screen {
background-color: black;
overflow-y: hidden;
}
[data-v-b27b58f4] .p-terminal .xterm {
[data-v-03daf1c8] .p-terminal .xterm {
overflow-x: auto;
}
[data-v-b27b58f4] .p-terminal .xterm-screen {
[data-v-03daf1c8] .p-terminal .xterm-screen {
background-color: black;
overflow-y: hidden;
}
@@ -4494,16 +4552,28 @@ audio.comfy-audio.empty-audio-widget {
pointer-events: none;
}
[data-v-9159c070] .p-toolbar-end .p-button {
[data-v-5e759e25] .p-toolbar-end .p-button {
padding-top: 0.25rem;
padding-bottom: 0.25rem
}
@media (min-width: 1536px) {
[data-v-9159c070] .p-toolbar-end .p-button {
[data-v-5e759e25] .p-toolbar-end .p-button {
padding-top: 0.5rem;
padding-bottom: 0.5rem
}
}
[data-v-5e759e25] .p-toolbar-start {
min-width: 0px;
flex: 1 1 0%;
overflow: hidden
}
.model_preview[data-v-32e6c4d9] {
background-color: var(--comfy-menu-bg);
@@ -4736,18 +4806,18 @@ audio.comfy-audio.empty-audio-widget {
width: 100%
}
.p-selectbutton .p-button[data-v-4b8adc78] {
.p-selectbutton .p-button[data-v-05364174] {
padding: 0.5rem;
}
.p-selectbutton .p-button .pi[data-v-4b8adc78] {
.p-selectbutton .p-button .pi[data-v-05364174] {
font-size: 1.5rem;
}
.field[data-v-4b8adc78] {
.field[data-v-05364174] {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.color-picker-container[data-v-4b8adc78] {
.color-picker-container[data-v-05364174] {
display: flex;
align-items: center;
gap: 0.5rem;
@@ -4767,10 +4837,10 @@ audio.comfy-audio.empty-audio-widget {
}
}
.comfy-image-wrap[data-v-ffe66146] {
.comfy-image-wrap[data-v-a748ccd8] {
display: contents;
}
.comfy-image-blur[data-v-ffe66146] {
.comfy-image-blur[data-v-a748ccd8] {
position: absolute;
top: 0;
left: 0;
@@ -4779,7 +4849,7 @@ audio.comfy-audio.empty-audio-widget {
-o-object-fit: cover;
object-fit: cover;
}
.comfy-image-main[data-v-ffe66146] {
.comfy-image-main[data-v-a748ccd8] {
width: 100%;
height: 100%;
-o-object-fit: cover;
@@ -4788,19 +4858,19 @@ audio.comfy-audio.empty-audio-widget {
object-position: center;
z-index: 1;
}
.contain .comfy-image-wrap[data-v-ffe66146] {
.contain .comfy-image-wrap[data-v-a748ccd8] {
position: relative;
width: 100%;
height: 100%;
}
.contain .comfy-image-main[data-v-ffe66146] {
.contain .comfy-image-main[data-v-a748ccd8] {
-o-object-fit: contain;
object-fit: contain;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
position: absolute;
}
.broken-image-placeholder[data-v-ffe66146] {
.broken-image-placeholder[data-v-a748ccd8] {
display: flex;
flex-direction: column;
align-items: center;
@@ -4809,7 +4879,7 @@ audio.comfy-audio.empty-audio-widget {
height: 100%;
margin: 2rem;
}
.broken-image-placeholder i[data-v-ffe66146] {
.broken-image-placeholder i[data-v-a748ccd8] {
font-size: 3rem;
margin-bottom: 0.5rem;
}
@@ -4827,7 +4897,7 @@ img.galleria-image {
z-index: 1;
}
.result-container[data-v-61515e14] {
.result-container[data-v-2403edc6] {
width: 100%;
height: 100%;
aspect-ratio: 1 / 1;
@@ -4837,7 +4907,7 @@ img.galleria-image {
justify-content: center;
align-items: center;
}
.preview-mask[data-v-61515e14] {
.preview-mask[data-v-2403edc6] {
position: absolute;
left: 50%;
top: 50%;
@@ -4849,7 +4919,7 @@ img.galleria-image {
transition: opacity 0.3s ease;
z-index: 1;
}
.result-container:hover .preview-mask[data-v-61515e14] {
.result-container:hover .preview-mask[data-v-2403edc6] {
opacity: 1;
}

View File

@@ -1,8 +1,7 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { B as BaseStyle, q as script$s, ct as script$t, H as createBaseVNode, o as openBlock, f as createElementBlock, D as mergeProps, X as toDisplayString, S as Ripple, r as resolveDirective, i as withDirectives, k as createBlock, G as resolveDynamicComponent, bY as script$u, aB as resolveComponent, T as normalizeClass, aD as createSlots, M as withCtx, bz as script$v, bw as script$w, F as Fragment, E as renderList, aE as createTextVNode, bq as setAttribute, ak as UniqueComponentId, bo as normalizeProps, J as renderSlot, I as createCommentVNode, R as equals, bk as script$x, c8 as script$y, cu as getFirstFocusableElement, an as OverlayEventBus, A as getVNodeProp, am as resolveFieldData, cv as invokeElementMethod, O as getAttribute, cw as getNextElementSibling, y as getOuterWidth, cx as getPreviousElementSibling, l as script$z, ay as script$A, W as script$B, bn as script$D, aj as isNotEmpty, bM as withModifiers, z as getOuterHeight, cy as _default, al as ZIndex, Q as focus, ap as addStyle, ar as absolutePosition, as as ConnectedOverlayScrollHandler, at as isTouchDevice, cz as FilterOperator, ax as script$E, cA as FocusTrap, N as createVNode, aC as Transition, bX as withKeys, cB as getIndex, aW as script$G, cC as isClickable, cD as clearSelection, cE as localeComparator, cF as sort, cG as FilterService, cn as FilterMatchMode, P as findSingle, c1 as findIndexInList, c2 as find, cH as exportCSV, U as getOffset, cI as getHiddenElementOuterWidth, cJ as getHiddenElementOuterHeight, cK as reorderArray, cL as getWindowScrollTop, cM as removeClass, cN as addClass, ao as isEmpty, aw as script$H, az as script$I } from "./index-DjNHn37O.js";
import { s as script$C } from "./index-B-aVupP5.js";
import { s as script$F } from "./index-5HFeZax4.js";
import { B as BaseStyle, y as script$s, cA as script$t, m as createBaseVNode, o as openBlock, f as createElementBlock, G as mergeProps, Z as toDisplayString, U as Ripple, r as resolveDirective, i as withDirectives, J as createBlock, K as resolveDynamicComponent, c5 as script$u, aD as resolveComponent, V as normalizeClass, aF as createSlots, P as withCtx, bG as script$v, bD as script$w, H as Fragment, I as renderList, aG as createTextVNode, bx as setAttribute, am as UniqueComponentId, bv as normalizeProps, M as renderSlot, L as createCommentVNode, T as equals, br as script$x, cg as script$y, cB as getFirstFocusableElement, ap as OverlayEventBus, E as getVNodeProp, ao as resolveFieldData, cC as invokeElementMethod, Q as getAttribute, cD as getNextElementSibling, C as getOuterWidth, cE as getPreviousElementSibling, l as script$z, aA as script$A, Y as script$B, bu as script$D, al as isNotEmpty, b3 as withModifiers, D as getOuterHeight, cF as _default, an as ZIndex, S as focus, ar as addStyle, at as absolutePosition, au as ConnectedOverlayScrollHandler, av as isTouchDevice, cG as FilterOperator, az as script$E, cH as script$F, cI as FocusTrap, k as createVNode, aE as Transition, c3 as withKeys, cJ as getIndex, aW as script$G, cK as isClickable, cL as clearSelection, cM as localeComparator, cN as sort, cO as FilterService, cu as FilterMatchMode, R as findSingle, c9 as findIndexInList, ca as find, cP as exportCSV, W as getOffset, cQ as getHiddenElementOuterWidth, cR as getHiddenElementOuterHeight, cS as reorderArray, cT as getWindowScrollTop, cU as removeClass, cV as addClass, aq as isEmpty, ay as script$H, aB as script$I } from "./index-QvfM__ze.js";
import { s as script$C } from "./index-Q1cQr26V.js";
var ColumnStyle = BaseStyle.extend({
name: "column"
});
@@ -8783,4 +8782,4 @@ export {
script as a,
script$r as s
};
//# sourceMappingURL=index-B5F0uxTQ.js.map
//# sourceMappingURL=index-DpF-ptbJ.js.map

View File

@@ -1,6 +1,6 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { ct as script$1, H as createBaseVNode, o as openBlock, f as createElementBlock, D as mergeProps } from "./index-DjNHn37O.js";
import { cA as script$1, m as createBaseVNode, o as openBlock, f as createElementBlock, G as mergeProps } from "./index-QvfM__ze.js";
var script = {
name: "BarsIcon",
"extends": script$1
@@ -26,4 +26,4 @@ script.render = render;
export {
script as s
};
//# sourceMappingURL=index-B-aVupP5.js.map
//# sourceMappingURL=index-Q1cQr26V.js.map

File diff suppressed because one or more lines are too long

173
web/assets/index-jXPKy3pP.js generated vendored
View File

@@ -1,173 +0,0 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { B as BaseStyle, q as script$2, ak as UniqueComponentId, c9 as script$4, l as script$5, S as Ripple, aB as resolveComponent, o as openBlock, f as createElementBlock, D as mergeProps, H as createBaseVNode, J as renderSlot, T as normalizeClass, X as toDisplayString, I as createCommentVNode, k as createBlock, M as withCtx, G as resolveDynamicComponent, N as createVNode, aC as Transition, i as withDirectives, v as vShow } from "./index-DjNHn37O.js";
import { s as script$3 } from "./index-5HFeZax4.js";
var theme = /* @__PURE__ */ __name(function theme2(_ref) {
var dt = _ref.dt;
return "\n.p-panel {\n border: 1px solid ".concat(dt("panel.border.color"), ";\n border-radius: ").concat(dt("panel.border.radius"), ";\n background: ").concat(dt("panel.background"), ";\n color: ").concat(dt("panel.color"), ";\n}\n\n.p-panel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: ").concat(dt("panel.header.padding"), ";\n background: ").concat(dt("panel.header.background"), ";\n color: ").concat(dt("panel.header.color"), ";\n border-style: solid;\n border-width: ").concat(dt("panel.header.border.width"), ";\n border-color: ").concat(dt("panel.header.border.color"), ";\n border-radius: ").concat(dt("panel.header.border.radius"), ";\n}\n\n.p-panel-toggleable .p-panel-header {\n padding: ").concat(dt("panel.toggleable.header.padding"), ";\n}\n\n.p-panel-title {\n line-height: 1;\n font-weight: ").concat(dt("panel.title.font.weight"), ";\n}\n\n.p-panel-content {\n padding: ").concat(dt("panel.content.padding"), ";\n}\n\n.p-panel-footer {\n padding: ").concat(dt("panel.footer.padding"), ";\n}\n");
}, "theme");
var classes = {
root: /* @__PURE__ */ __name(function root(_ref2) {
var props = _ref2.props;
return ["p-panel p-component", {
"p-panel-toggleable": props.toggleable
}];
}, "root"),
header: "p-panel-header",
title: "p-panel-title",
headerActions: "p-panel-header-actions",
pcToggleButton: "p-panel-toggle-button",
contentContainer: "p-panel-content-container",
content: "p-panel-content",
footer: "p-panel-footer"
};
var PanelStyle = BaseStyle.extend({
name: "panel",
theme,
classes
});
var script$1 = {
name: "BasePanel",
"extends": script$2,
props: {
header: String,
toggleable: Boolean,
collapsed: Boolean,
toggleButtonProps: {
type: Object,
"default": /* @__PURE__ */ __name(function _default() {
return {
severity: "secondary",
text: true,
rounded: true
};
}, "_default")
}
},
style: PanelStyle,
provide: /* @__PURE__ */ __name(function provide() {
return {
$pcPanel: this,
$parentInstance: this
};
}, "provide")
};
var script = {
name: "Panel",
"extends": script$1,
inheritAttrs: false,
emits: ["update:collapsed", "toggle"],
data: /* @__PURE__ */ __name(function data() {
return {
id: this.$attrs.id,
d_collapsed: this.collapsed
};
}, "data"),
watch: {
"$attrs.id": /* @__PURE__ */ __name(function $attrsId(newValue) {
this.id = newValue || UniqueComponentId();
}, "$attrsId"),
collapsed: /* @__PURE__ */ __name(function collapsed(newValue) {
this.d_collapsed = newValue;
}, "collapsed")
},
mounted: /* @__PURE__ */ __name(function mounted() {
this.id = this.id || UniqueComponentId();
}, "mounted"),
methods: {
toggle: /* @__PURE__ */ __name(function toggle(event) {
this.d_collapsed = !this.d_collapsed;
this.$emit("update:collapsed", this.d_collapsed);
this.$emit("toggle", {
originalEvent: event,
value: this.d_collapsed
});
}, "toggle"),
onKeyDown: /* @__PURE__ */ __name(function onKeyDown(event) {
if (event.code === "Enter" || event.code === "NumpadEnter" || event.code === "Space") {
this.toggle(event);
event.preventDefault();
}
}, "onKeyDown")
},
computed: {
buttonAriaLabel: /* @__PURE__ */ __name(function buttonAriaLabel() {
return this.toggleButtonProps && this.toggleButtonProps.ariaLabel ? this.toggleButtonProps.ariaLabel : this.header;
}, "buttonAriaLabel")
},
components: {
PlusIcon: script$3,
MinusIcon: script$4,
Button: script$5
},
directives: {
ripple: Ripple
}
};
var _hoisted_1 = ["id"];
var _hoisted_2 = ["id", "aria-labelledby"];
function render(_ctx, _cache, $props, $setup, $data, $options) {
var _component_Button = resolveComponent("Button");
return openBlock(), createElementBlock("div", mergeProps({
"class": _ctx.cx("root")
}, _ctx.ptmi("root")), [createBaseVNode("div", mergeProps({
"class": _ctx.cx("header")
}, _ctx.ptm("header")), [renderSlot(_ctx.$slots, "header", {
id: $data.id + "_header",
"class": normalizeClass(_ctx.cx("title"))
}, function() {
return [_ctx.header ? (openBlock(), createElementBlock("span", mergeProps({
key: 0,
id: $data.id + "_header",
"class": _ctx.cx("title")
}, _ctx.ptm("title")), toDisplayString(_ctx.header), 17, _hoisted_1)) : createCommentVNode("", true)];
}), createBaseVNode("div", mergeProps({
"class": _ctx.cx("headerActions")
}, _ctx.ptm("headerActions")), [renderSlot(_ctx.$slots, "icons"), _ctx.toggleable ? (openBlock(), createBlock(_component_Button, mergeProps({
key: 0,
id: $data.id + "_header",
"class": _ctx.cx("pcToggleButton"),
"aria-label": $options.buttonAriaLabel,
"aria-controls": $data.id + "_content",
"aria-expanded": !$data.d_collapsed,
unstyled: _ctx.unstyled,
onClick: $options.toggle,
onKeydown: $options.onKeyDown
}, _ctx.toggleButtonProps, {
pt: _ctx.ptm("pcToggleButton")
}), {
icon: withCtx(function(slotProps) {
return [renderSlot(_ctx.$slots, _ctx.$slots.toggleicon ? "toggleicon" : "togglericon", {
collapsed: $data.d_collapsed
}, function() {
return [(openBlock(), createBlock(resolveDynamicComponent($data.d_collapsed ? "PlusIcon" : "MinusIcon"), mergeProps({
"class": slotProps["class"]
}, _ctx.ptm("pcToggleButton")["icon"]), null, 16, ["class"]))];
})];
}),
_: 3
}, 16, ["id", "class", "aria-label", "aria-controls", "aria-expanded", "unstyled", "onClick", "onKeydown", "pt"])) : createCommentVNode("", true)], 16)], 16), createVNode(Transition, mergeProps({
name: "p-toggleable-content"
}, _ctx.ptm("transition")), {
"default": withCtx(function() {
return [withDirectives(createBaseVNode("div", mergeProps({
id: $data.id + "_content",
"class": _ctx.cx("contentContainer"),
role: "region",
"aria-labelledby": $data.id + "_header"
}, _ctx.ptm("contentContainer")), [createBaseVNode("div", mergeProps({
"class": _ctx.cx("content")
}, _ctx.ptm("content")), [renderSlot(_ctx.$slots, "default")], 16), _ctx.$slots.footer ? (openBlock(), createElementBlock("div", mergeProps({
key: 0,
"class": _ctx.cx("footer")
}, _ctx.ptm("footer")), [renderSlot(_ctx.$slots, "footer")], 16)) : createCommentVNode("", true)], 16, _hoisted_2), [[vShow, !$data.d_collapsed]])];
}),
_: 3
}, 16)], 16);
}
__name(render, "render");
script.render = render;
export {
script as s
};
//# sourceMappingURL=index-jXPKy3pP.js.map

View File

@@ -1,6 +1,6 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { ca as ComfyDialog, cb as $el, cc as ComfyApp, h as app, a3 as LiteGraph, bd as LGraphCanvas, cd as useExtensionService, ce as processDynamicPrompt, cf as isElectron, c0 as electronAPI, bR as useDialogService, cg as t, ch as DraggableList, bt as useToastStore, ah as LGraphNode, ci as applyTextReplacements, cj as ComfyWidgets, ck as addValueControlWidgets, a6 as useNodeDefStore, cl as serialise, cm as deserialiseAndCreate, b8 as api, a as useSettingStore, ag as LGraphGroup, ad as nextTick } from "./index-DjNHn37O.js";
import { ci as ComfyDialog, cj as $el, ck as ComfyApp, h as app, a5 as LiteGraph, bl as LGraphCanvas, cl as useExtensionService, cm as processDynamicPrompt, bT as isElectron, bV as electronAPI, bW as useDialogService, cn as t, co as DraggableList, bA as useToastStore, aj as LGraphNode, cp as applyTextReplacements, cq as ComfyWidgets, cr as addValueControlWidgets, a8 as useNodeDefStore, cs as serialise, ct as deserialiseAndCreate, bh as api, a as useSettingStore, ai as LGraphGroup, af as nextTick, bO as lodashExports, bg as setStorageValue, bb as getStorageValue } from "./index-QvfM__ze.js";
class ClipspaceDialog extends ComfyDialog {
static {
__name(this, "ClipspaceDialog");
@@ -441,10 +441,24 @@ app.registerExtension({
{
id: "Comfy-Desktop.SendStatistics",
category: ["Comfy-Desktop", "General", "Send Statistics"],
name: "Send anonymous crash reports",
name: "Send anonymous usage metrics",
type: "boolean",
defaultValue: true,
onChange: onChangeRestartApp
},
{
id: "Comfy-Desktop.WindowStyle",
category: ["Comfy-Desktop", "General", "Window Style"],
name: "Window Style",
tooltip: "Choose custom option to hide the system title bar",
type: "combo",
experimental: true,
defaultValue: "default",
options: ["default", "custom"],
onChange: /* @__PURE__ */ __name((newValue, oldValue) => {
electronAPI$1.Config.setWindowStyle(newValue);
onChangeRestartApp(newValue, oldValue);
}, "onChange")
}
],
commands: [
@@ -2968,10 +2982,10 @@ function manageGroupNodes(type) {
new ManageGroupDialog(app).show(type);
}
__name(manageGroupNodes, "manageGroupNodes");
const id$2 = "Comfy.GroupNode";
const id$1 = "Comfy.GroupNode";
let globalDefs;
const ext = {
name: id$2,
name: id$1,
commands: [
{
id: "Comfy.GroupNode.ConvertSelectedNodesToGroupNode",
@@ -3234,39 +3248,6 @@ app.registerExtension({
};
}
});
const id$1 = "Comfy.InvertMenuScrolling";
app.registerExtension({
name: id$1,
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$1,
category: ["LiteGraph", "Menu", "InvertMenuScrolling"],
name: "Invert Context Menu Scrolling",
type: "boolean",
defaultValue: false,
onChange(value) {
if (value) {
replace();
} else {
LiteGraph.ContextMenu = ctxMenu;
}
}
});
}
});
/**
* @license
* Copyright 2010-2024 Three.js Authors
@@ -36361,6 +36342,229 @@ function interceptControlUp(event) {
}
}
__name(interceptControlUp, "interceptControlUp");
class ViewHelper extends Object3D {
static {
__name(this, "ViewHelper");
}
constructor(camera, domElement) {
super();
this.isViewHelper = true;
this.animating = false;
this.center = new Vector3();
const color1 = new Color("#ff4466");
const color2 = new Color("#88ff44");
const color3 = new Color("#4488ff");
const color4 = new Color("#000000");
const options = {};
const interactiveObjects = [];
const raycaster = new Raycaster();
const mouse = new Vector2();
const dummy = new Object3D();
const orthoCamera = new OrthographicCamera(-2, 2, 2, -2, 0, 4);
orthoCamera.position.set(0, 0, 2);
const geometry = new CylinderGeometry(0.04, 0.04, 0.8, 5).rotateZ(-Math.PI / 2).translate(0.4, 0, 0);
const xAxis = new Mesh(geometry, getAxisMaterial(color1));
const yAxis = new Mesh(geometry, getAxisMaterial(color2));
const zAxis = new Mesh(geometry, getAxisMaterial(color3));
yAxis.rotation.z = Math.PI / 2;
zAxis.rotation.y = -Math.PI / 2;
this.add(xAxis);
this.add(zAxis);
this.add(yAxis);
const spriteMaterial1 = getSpriteMaterial(color1);
const spriteMaterial2 = getSpriteMaterial(color2);
const spriteMaterial3 = getSpriteMaterial(color3);
const spriteMaterial4 = getSpriteMaterial(color4);
const posXAxisHelper = new Sprite(spriteMaterial1);
const posYAxisHelper = new Sprite(spriteMaterial2);
const posZAxisHelper = new Sprite(spriteMaterial3);
const negXAxisHelper = new Sprite(spriteMaterial4);
const negYAxisHelper = new Sprite(spriteMaterial4);
const negZAxisHelper = new Sprite(spriteMaterial4);
posXAxisHelper.position.x = 1;
posYAxisHelper.position.y = 1;
posZAxisHelper.position.z = 1;
negXAxisHelper.position.x = -1;
negYAxisHelper.position.y = -1;
negZAxisHelper.position.z = -1;
negXAxisHelper.material.opacity = 0.2;
negYAxisHelper.material.opacity = 0.2;
negZAxisHelper.material.opacity = 0.2;
posXAxisHelper.userData.type = "posX";
posYAxisHelper.userData.type = "posY";
posZAxisHelper.userData.type = "posZ";
negXAxisHelper.userData.type = "negX";
negYAxisHelper.userData.type = "negY";
negZAxisHelper.userData.type = "negZ";
this.add(posXAxisHelper);
this.add(posYAxisHelper);
this.add(posZAxisHelper);
this.add(negXAxisHelper);
this.add(negYAxisHelper);
this.add(negZAxisHelper);
interactiveObjects.push(posXAxisHelper);
interactiveObjects.push(posYAxisHelper);
interactiveObjects.push(posZAxisHelper);
interactiveObjects.push(negXAxisHelper);
interactiveObjects.push(negYAxisHelper);
interactiveObjects.push(negZAxisHelper);
const point = new Vector3();
const dim = 128;
const turnRate = 2 * Math.PI;
this.render = function(renderer) {
this.quaternion.copy(camera.quaternion).invert();
this.updateMatrixWorld();
point.set(0, 0, 1);
point.applyQuaternion(camera.quaternion);
const x = domElement.offsetWidth - dim;
renderer.clearDepth();
renderer.getViewport(viewport);
renderer.setViewport(x, 0, dim, dim);
renderer.render(this, orthoCamera);
renderer.setViewport(viewport.x, viewport.y, viewport.z, viewport.w);
};
const targetPosition = new Vector3();
const targetQuaternion = new Quaternion();
const q1 = new Quaternion();
const q2 = new Quaternion();
const viewport = new Vector4();
let radius = 0;
this.handleClick = function(event) {
if (this.animating === true) return false;
const rect = domElement.getBoundingClientRect();
const offsetX = rect.left + (domElement.offsetWidth - dim);
const offsetY = rect.top + (domElement.offsetHeight - dim);
mouse.x = (event.clientX - offsetX) / (rect.right - offsetX) * 2 - 1;
mouse.y = -((event.clientY - offsetY) / (rect.bottom - offsetY)) * 2 + 1;
raycaster.setFromCamera(mouse, orthoCamera);
const intersects2 = raycaster.intersectObjects(interactiveObjects);
if (intersects2.length > 0) {
const intersection = intersects2[0];
const object = intersection.object;
prepareAnimationData(object, this.center);
this.animating = true;
return true;
} else {
return false;
}
};
this.setLabels = function(labelX, labelY, labelZ) {
options.labelX = labelX;
options.labelY = labelY;
options.labelZ = labelZ;
updateLabels();
};
this.setLabelStyle = function(font, color, radius2) {
options.font = font;
options.color = color;
options.radius = radius2;
updateLabels();
};
this.update = function(delta) {
const step = delta * turnRate;
q1.rotateTowards(q2, step);
camera.position.set(0, 0, 1).applyQuaternion(q1).multiplyScalar(radius).add(this.center);
camera.quaternion.rotateTowards(targetQuaternion, step);
if (q1.angleTo(q2) === 0) {
this.animating = false;
}
};
this.dispose = function() {
geometry.dispose();
xAxis.material.dispose();
yAxis.material.dispose();
zAxis.material.dispose();
posXAxisHelper.material.map.dispose();
posYAxisHelper.material.map.dispose();
posZAxisHelper.material.map.dispose();
negXAxisHelper.material.map.dispose();
negYAxisHelper.material.map.dispose();
negZAxisHelper.material.map.dispose();
posXAxisHelper.material.dispose();
posYAxisHelper.material.dispose();
posZAxisHelper.material.dispose();
negXAxisHelper.material.dispose();
negYAxisHelper.material.dispose();
negZAxisHelper.material.dispose();
};
function prepareAnimationData(object, focusPoint) {
switch (object.userData.type) {
case "posX":
targetPosition.set(1, 0, 0);
targetQuaternion.setFromEuler(new Euler(0, Math.PI * 0.5, 0));
break;
case "posY":
targetPosition.set(0, 1, 0);
targetQuaternion.setFromEuler(new Euler(-Math.PI * 0.5, 0, 0));
break;
case "posZ":
targetPosition.set(0, 0, 1);
targetQuaternion.setFromEuler(new Euler());
break;
case "negX":
targetPosition.set(-1, 0, 0);
targetQuaternion.setFromEuler(new Euler(0, -Math.PI * 0.5, 0));
break;
case "negY":
targetPosition.set(0, -1, 0);
targetQuaternion.setFromEuler(new Euler(Math.PI * 0.5, 0, 0));
break;
case "negZ":
targetPosition.set(0, 0, -1);
targetQuaternion.setFromEuler(new Euler(0, Math.PI, 0));
break;
default:
console.error("ViewHelper: Invalid axis.");
}
radius = camera.position.distanceTo(focusPoint);
targetPosition.multiplyScalar(radius).add(focusPoint);
dummy.position.copy(focusPoint);
dummy.lookAt(camera.position);
q1.copy(dummy.quaternion);
dummy.lookAt(targetPosition);
q2.copy(dummy.quaternion);
}
__name(prepareAnimationData, "prepareAnimationData");
function getAxisMaterial(color) {
return new MeshBasicMaterial({ color, toneMapped: false });
}
__name(getAxisMaterial, "getAxisMaterial");
function getSpriteMaterial(color, text) {
const { font = "24px Arial", color: labelColor = "#000000", radius: radius2 = 14 } = options;
const canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 64;
const context = canvas.getContext("2d");
context.beginPath();
context.arc(32, 32, radius2, 0, 2 * Math.PI);
context.closePath();
context.fillStyle = color.getStyle();
context.fill();
if (text) {
context.font = font;
context.textAlign = "center";
context.fillStyle = labelColor;
context.fillText(text, 32, 41);
}
const texture = new CanvasTexture(canvas);
texture.colorSpace = SRGBColorSpace;
return new SpriteMaterial({ map: texture, toneMapped: false });
}
__name(getSpriteMaterial, "getSpriteMaterial");
function updateLabels() {
posXAxisHelper.material.map.dispose();
posYAxisHelper.material.map.dispose();
posZAxisHelper.material.map.dispose();
posXAxisHelper.material.dispose();
posYAxisHelper.material.dispose();
posZAxisHelper.material.dispose();
posXAxisHelper.material = getSpriteMaterial(color1, options.labelX);
posYAxisHelper.material = getSpriteMaterial(color2, options.labelY);
posZAxisHelper.material = getSpriteMaterial(color3, options.labelZ);
}
__name(updateLabels, "updateLabels");
}
}
/*!
fflate - fast JavaScript compression/decompression
<https://101arrowz.github.io/fflate>
@@ -45833,7 +46037,6 @@ class Load3d {
stlLoader;
currentModel = null;
originalModel = null;
node;
animationFrameId = null;
gridHelper;
lights = [];
@@ -45846,6 +46049,10 @@ class Load3d {
materialMode = "original";
currentUpDirection = "original";
originalRotation = null;
viewHelper;
viewHelperContainer;
cameraSwitcherContainer;
gridSwitcherContainer;
constructor(container) {
this.scene = new Scene();
this.perspectiveCamera = new PerspectiveCamera(75, 1, 0.1, 1e3);
@@ -45866,6 +46073,7 @@ class Load3d {
this.renderer = new WebGLRenderer({ alpha: true, antialias: true });
this.renderer.setSize(300, 300);
this.renderer.setClearColor(2631720);
this.renderer.autoClear = false;
const rendererDomElement = this.renderer.domElement;
container.appendChild(rendererDomElement);
this.controls = new OrbitControls(
@@ -45901,10 +46109,113 @@ class Load3d {
side: DoubleSide
});
this.standardMaterial = this.createSTLMaterial();
this.animate();
this.createViewHelper(container);
this.createGridSwitcher(container);
this.createCameraSwitcher(container);
this.handleResize();
this.startAnimation();
}
createViewHelper(container) {
this.viewHelperContainer = document.createElement("div");
this.viewHelperContainer.style.position = "absolute";
this.viewHelperContainer.style.bottom = "0";
this.viewHelperContainer.style.left = "0";
this.viewHelperContainer.style.width = "128px";
this.viewHelperContainer.style.height = "128px";
this.viewHelperContainer.addEventListener("pointerup", (event) => {
event.stopPropagation();
this.viewHelper.handleClick(event);
});
this.viewHelperContainer.addEventListener("pointerdown", (event) => {
event.stopPropagation();
});
container.appendChild(this.viewHelperContainer);
this.viewHelper = new ViewHelper(
this.activeCamera,
this.viewHelperContainer
);
this.viewHelper.center = this.controls.target;
}
createGridSwitcher(container) {
this.gridSwitcherContainer = document.createElement("div");
this.gridSwitcherContainer.style.position = "absolute";
this.gridSwitcherContainer.style.top = "28px";
this.gridSwitcherContainer.style.left = "3px";
this.gridSwitcherContainer.style.width = "20px";
this.gridSwitcherContainer.style.height = "20px";
this.gridSwitcherContainer.style.cursor = "pointer";
this.gridSwitcherContainer.style.alignItems = "center";
this.gridSwitcherContainer.style.justifyContent = "center";
this.gridSwitcherContainer.style.transition = "background-color 0.2s";
const gridIcon = document.createElement("div");
gridIcon.innerHTML = `
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<path d="M3 3h18v18H3z"/>
<path d="M3 9h18"/>
<path d="M3 15h18"/>
<path d="M9 3v18"/>
<path d="M15 3v18"/>
</svg>
`;
const updateButtonState = /* @__PURE__ */ __name(() => {
if (this.gridHelper.visible) {
this.gridSwitcherContainer.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
} else {
this.gridSwitcherContainer.style.backgroundColor = "transparent";
}
}, "updateButtonState");
updateButtonState();
this.gridSwitcherContainer.addEventListener("mouseenter", () => {
if (!this.gridHelper.visible) {
this.gridSwitcherContainer.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
}
});
this.gridSwitcherContainer.addEventListener("mouseleave", () => {
if (!this.gridHelper.visible) {
this.gridSwitcherContainer.style.backgroundColor = "transparent";
}
});
this.gridSwitcherContainer.title = "Toggle Grid";
this.gridSwitcherContainer.addEventListener("click", (event) => {
event.stopPropagation();
this.toggleGrid(!this.gridHelper.visible);
updateButtonState();
});
this.gridSwitcherContainer.appendChild(gridIcon);
container.appendChild(this.gridSwitcherContainer);
}
createCameraSwitcher(container) {
this.cameraSwitcherContainer = document.createElement("div");
this.cameraSwitcherContainer.style.position = "absolute";
this.cameraSwitcherContainer.style.top = "3px";
this.cameraSwitcherContainer.style.left = "3px";
this.cameraSwitcherContainer.style.width = "20px";
this.cameraSwitcherContainer.style.height = "20px";
this.cameraSwitcherContainer.style.cursor = "pointer";
this.cameraSwitcherContainer.style.alignItems = "center";
this.cameraSwitcherContainer.style.justifyContent = "center";
const cameraIcon = document.createElement("div");
cameraIcon.innerHTML = `
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<path d="M18 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2Z"/>
<path d="m12 12 4-2.4"/>
<circle cx="12" cy="12" r="3"/>
</svg>
`;
this.cameraSwitcherContainer.addEventListener("mouseenter", () => {
this.cameraSwitcherContainer.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
});
this.cameraSwitcherContainer.addEventListener("mouseleave", () => {
this.cameraSwitcherContainer.style.backgroundColor = "rgba(0, 0, 0, 0.3)";
});
this.cameraSwitcherContainer.title = "Switch Camera (Perspective/Orthographic)";
this.cameraSwitcherContainer.addEventListener("click", (event) => {
event.stopPropagation();
this.toggleCamera();
});
this.cameraSwitcherContainer.appendChild(cameraIcon);
container.appendChild(this.cameraSwitcherContainer);
}
setFOV(fov2) {
if (this.activeCamera === this.perspectiveCamera) {
this.perspectiveCamera.fov = fov2;
@@ -46097,6 +46408,12 @@ class Load3d {
this.controls.object = this.activeCamera;
this.controls.target.copy(target);
this.controls.update();
this.viewHelper.dispose();
this.viewHelper = new ViewHelper(
this.activeCamera,
this.viewHelperContainer
);
this.viewHelper.center = this.controls.target;
this.handleResize();
}
getCurrentCameraType() {
@@ -46127,8 +46444,14 @@ class Load3d {
startAnimation() {
const animate = /* @__PURE__ */ __name(() => {
this.animationFrameId = requestAnimationFrame(animate);
const delta = this.clock.getDelta();
if (this.viewHelper.animating) {
this.viewHelper.update(delta);
}
this.renderer.clear();
this.controls.update();
this.renderer.render(this.scene, this.activeCamera);
this.viewHelper.render(this.renderer);
}, "animate");
animate();
}
@@ -46193,6 +46516,7 @@ class Load3d {
cancelAnimationFrame(this.animationFrameId);
}
this.controls.dispose();
this.viewHelper.dispose();
this.renderer.dispose();
this.renderer.domElement.remove();
this.scene.clear();
@@ -46372,9 +46696,11 @@ class Load3d {
this.orthographicCamera.bottom = -frustumSize / 2;
this.orthographicCamera.updateProjectionMatrix();
}
this.renderer.clear();
this.renderer.render(this.scene, this.activeCamera);
const sceneData = this.renderer.domElement.toDataURL("image/png");
this.renderer.setClearColor(0, 0);
this.renderer.clear();
this.renderer.render(this.scene, this.activeCamera);
const maskData = this.renderer.domElement.toDataURL("image/png");
this.renderer.setClearColor(originalClearColor, originalClearAlpha);
@@ -46395,38 +46721,6 @@ class Load3d {
side: DoubleSide
});
}
setViewPosition(position) {
if (!this.currentModel) {
return;
}
const box = new Box3();
let center = new Vector3();
let size = new Vector3();
if (this.currentModel) {
box.setFromObject(this.currentModel);
box.getCenter(center);
box.getSize(size);
}
const maxDim = Math.max(size.x, size.y, size.z);
const distance = maxDim * 2;
switch (position) {
case "front":
this.activeCamera.position.set(0, 0, distance);
break;
case "top":
this.activeCamera.position.set(0, distance, 0);
break;
case "right":
this.activeCamera.position.set(distance, 0, 0);
break;
case "isometric":
this.activeCamera.position.set(distance, distance, distance);
break;
}
this.activeCamera.lookAt(center);
this.controls.target.copy(center);
this.controls.update();
}
setBackgroundColor(color) {
this.renderer.setClearColor(new Color(color));
this.renderer.render(this.scene, this.activeCamera);
@@ -46536,15 +46830,23 @@ class Load3dAnimation extends Load3d {
}
});
}
animate = /* @__PURE__ */ __name(() => {
requestAnimationFrame(this.animate);
if (this.currentAnimation && this.isAnimationPlaying) {
startAnimation() {
const animate = /* @__PURE__ */ __name(() => {
this.animationFrameId = requestAnimationFrame(animate);
const delta = this.clock.getDelta();
this.currentAnimation.update(delta);
}
this.controls.update();
this.renderer.render(this.scene, this.activeCamera);
}, "animate");
if (this.currentAnimation && this.isAnimationPlaying) {
this.currentAnimation.update(delta);
}
this.controls.update();
this.renderer.clear();
this.renderer.render(this.scene, this.activeCamera);
if (this.viewHelper.animating) {
this.viewHelper.update(delta);
}
this.viewHelper.render(this.renderer);
}, "animate");
animate();
}
}
function splitFilePath$1(path) {
const folder_separator = path.lastIndexOf("/");
@@ -46577,7 +46879,7 @@ const load3dCanvasCSSCLASS = `display: flex;
width: 100% !important;
height: 100% !important;`;
const containerToLoad3D = /* @__PURE__ */ new Map();
function configureLoad3D(load3d, loadFolder, modelWidget, showGrid, cameraType, view, material, bgColor, lightIntensity, upDirection, fov2, cameraState, postModelUpdateFunc) {
function configureLoad3D(load3d, loadFolder, modelWidget, material, bgColor, lightIntensity, upDirection, fov2, cameraState, postModelUpdateFunc) {
const createModelUpdateHandler = /* @__PURE__ */ __name(() => {
let isFirstLoad = true;
return async (value) => {
@@ -46611,17 +46913,6 @@ function configureLoad3D(load3d, loadFolder, modelWidget, showGrid, cameraType,
onModelWidgetUpdate(modelWidget.value);
}
modelWidget.callback = onModelWidgetUpdate;
load3d.toggleGrid(showGrid.value);
showGrid.callback = (value) => {
load3d.toggleGrid(value);
};
load3d.toggleCamera(cameraType.value);
cameraType.callback = (value) => {
load3d.toggleCamera(value);
};
view.callback = (value) => {
load3d.setViewPosition(value);
};
material.callback = (value) => {
load3d.setMaterialMode(value);
};
@@ -46741,11 +47032,6 @@ app.registerExtension({
const modelWidget = node.widgets.find(
(w2) => w2.name === "model_file"
);
const showGrid = node.widgets.find((w2) => w2.name === "show_grid");
const cameraType = node.widgets.find(
(w2) => w2.name === "camera_type"
);
const view = node.widgets.find((w2) => w2.name === "view");
const material = node.widgets.find((w2) => w2.name === "material");
const bgColor = node.widgets.find((w2) => w2.name === "bg_color");
const lightIntensity = node.widgets.find(
@@ -46769,9 +47055,6 @@ app.registerExtension({
load3d,
"input",
modelWidget,
showGrid,
cameraType,
view,
material,
bgColor,
lightIntensity,
@@ -46939,11 +47222,6 @@ app.registerExtension({
const modelWidget = node.widgets.find(
(w2) => w2.name === "model_file"
);
const showGrid = node.widgets.find((w2) => w2.name === "show_grid");
const cameraType = node.widgets.find(
(w2) => w2.name === "camera_type"
);
const view = node.widgets.find((w2) => w2.name === "view");
const material = node.widgets.find((w2) => w2.name === "material");
const bgColor = node.widgets.find((w2) => w2.name === "bg_color");
const lightIntensity = node.widgets.find(
@@ -46976,9 +47254,6 @@ app.registerExtension({
load3d,
"input",
modelWidget,
showGrid,
cameraType,
view,
material,
bgColor,
lightIntensity,
@@ -47001,6 +47276,7 @@ app.registerExtension({
const h = node.widgets.find((w2) => w2.name === "height");
sceneWidget.serializeValue = async () => {
node.properties["Camera Info"] = JSON.stringify(load3d.getCameraState());
load3d.toggleAnimation(false);
const { scene: imageData, mask: maskData } = await load3d.captureScene(
w.value,
h.value
@@ -47081,11 +47357,6 @@ app.registerExtension({
const modelWidget = node.widgets.find(
(w) => w.name === "model_file"
);
const showGrid = node.widgets.find((w) => w.name === "show_grid");
const cameraType = node.widgets.find(
(w) => w.name === "camera_type"
);
const view = node.widgets.find((w) => w.name === "view");
const material = node.widgets.find((w) => w.name === "material");
const bgColor = node.widgets.find((w) => w.name === "bg_color");
const lightIntensity = node.widgets.find(
@@ -47109,9 +47380,6 @@ app.registerExtension({
load3d,
"output",
modelWidget,
showGrid,
cameraType,
view,
material,
bgColor,
lightIntensity,
@@ -48299,15 +48567,15 @@ var styles = `
}
#maskEditor_toolPanel {
height: 100%;
width: var(--sidebar-width);
width: 4rem;
z-index: 8888;
background: var(--comfy-menu-bg);
display: flex;
flex-direction: column;
}
.maskEditor_toolPanelContainer {
width: var(--sidebar-width);
height: var(--sidebar-width);
width: 4rem;
height: 4rem;
display: flex;
justify-content: center;
align-items: center;
@@ -48368,7 +48636,7 @@ var styles = `
margin-bottom: 5px;
}
#maskEditor_pointerZone {
width: calc(100% - var(--sidebar-width) - 220px);
width: calc(100% - 4rem - 220px);
height: 100%;
}
#maskEditor_uiContainer {
@@ -48740,8 +49008,8 @@ var styles = `
}
.maskEditor_toolPanelZoomIndicator {
width: var(--sidebar-width);
height: var(--sidebar-width);
width: 4rem;
height: 4rem;
display: flex;
flex-direction: column;
justify-content: center;
@@ -48800,6 +49068,31 @@ var ColorComparisonMethod = /* @__PURE__ */ ((ColorComparisonMethod2) => {
ColorComparisonMethod2["LAB"] = "lab";
return ColorComparisonMethod2;
})(ColorComparisonMethod || {});
const saveBrushToCache = lodashExports.debounce(function(key, brush) {
try {
const brushString = JSON.stringify(brush);
setStorageValue(key, brushString);
} catch (error) {
console.error("Failed to save brush to cache:", error);
}
}, 300);
function loadBrushFromCache(key) {
try {
const brushString = getStorageValue(key);
if (brushString) {
const brush = JSON.parse(brushString);
console.log("Loaded brush from cache:", brush);
return brush;
} else {
console.log("No brush found in cache.");
return null;
}
} catch (error) {
console.error("Failed to load brush from cache:", error);
return null;
}
}
__name(loadBrushFromCache, "loadBrushFromCache");
class MaskEditorDialog extends ComfyDialog {
static {
__name(this, "MaskEditorDialog");
@@ -49065,7 +49358,7 @@ class MaskEditorDialog extends ComfyDialog {
}).then((response) => {
if (!response.ok) {
console.log("Failed to upload mask:", response);
this.uploadMask(filepath, formData, 2);
this.uploadMask(filepath, formData, retries - 1);
}
}).catch((error) => {
console.error("Error:", error);
@@ -49664,13 +49957,18 @@ class BrushTool {
this.brushAdjustmentSpeed = app.extensionManager.setting.get(
"Comfy.MaskEditor.BrushAdjustmentSpeed"
);
this.brushSettings = {
size: 10,
opacity: 100,
hardness: 1,
type: "arc"
/* Arc */
};
const cachedBrushSettings = loadBrushFromCache("maskeditor_brush_settings");
if (cachedBrushSettings) {
this.brushSettings = cachedBrushSettings;
} else {
this.brushSettings = {
type: "arc",
size: 10,
opacity: 0.7,
hardness: 1,
smoothingPrecision: 10
};
}
this.maskBlendMode = "black";
}
createListeners() {
@@ -49732,6 +50030,10 @@ class BrushTool {
"brushType",
async () => this.brushSettings.type
);
this.messageBroker.createPullTopic(
"brushSmoothingPrecision",
async () => this.brushSettings.smoothingPrecision
);
this.messageBroker.createPullTopic(
"maskBlendMode",
async () => this.maskBlendMode
@@ -49840,7 +50142,7 @@ class BrushTool {
dy = points[i + 1].y - points[i].y;
totalLength += Math.sqrt(dx * dx + dy * dy);
}
const distanceBetweenPoints = this.brushSettings.size / this.smoothingPrecision * 6;
const distanceBetweenPoints = this.brushSettings.size / this.brushSettings.smoothingPrecision * 6;
const stepNr = Math.ceil(totalLength / distanceBetweenPoints);
let interpolatedPoints = points;
if (stepNr > 0) {
@@ -49871,7 +50173,7 @@ class BrushTool {
const brush_size = await this.messageBroker.pull("brushSize");
const distance = Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
const steps = Math.ceil(
distance / (brush_size / this.smoothingPrecision * 4)
distance / (brush_size / this.brushSettings.smoothingPrecision * 4)
);
const interpolatedOpacity = 1 / (1 + Math.exp(-6 * (this.brushSettings.opacity - 0.5))) - 1 / (1 + Math.exp(3));
this.init_shape(compositionOp);
@@ -50123,18 +50425,23 @@ class BrushTool {
}
setBrushSize(size) {
this.brushSettings.size = size;
saveBrushToCache("maskeditor_brush_settings", this.brushSettings);
}
setBrushOpacity(opacity) {
this.brushSettings.opacity = opacity;
saveBrushToCache("maskeditor_brush_settings", this.brushSettings);
}
setBrushHardness(hardness) {
this.brushSettings.hardness = hardness;
saveBrushToCache("maskeditor_brush_settings", this.brushSettings);
}
setBrushType(type) {
this.brushSettings.type = type;
saveBrushToCache("maskeditor_brush_settings", this.brushSettings);
}
setBrushSmoothingPrecision(precision) {
this.smoothingPrecision = precision;
this.brushSettings.smoothingPrecision = precision;
saveBrushToCache("maskeditor_brush_settings", this.brushSettings);
}
}
class UIManager {
@@ -50340,7 +50647,6 @@ class UIManager {
const circle_shape = document.createElement("div");
circle_shape.id = "maskEditor_sidePanelBrushShapeCircle";
circle_shape.classList.add(shapeColor);
circle_shape.style.background = "var(--p-button-text-primary-color)";
circle_shape.addEventListener("click", () => {
this.messageBroker.publish(
"setBrushShape",
@@ -50354,7 +50660,6 @@ class UIManager {
const square_shape = document.createElement("div");
square_shape.id = "maskEditor_sidePanelBrushShapeSquare";
square_shape.classList.add(shapeColor);
square_shape.style.background = "";
square_shape.addEventListener("click", () => {
this.messageBroker.publish(
"setBrushShape",
@@ -50365,6 +50670,13 @@ class UIManager {
square_shape.style.background = "var(--p-button-text-primary-color)";
circle_shape.style.background = "";
});
if ((await this.messageBroker.pull("brushSettings")).type === "arc") {
circle_shape.style.background = "var(--p-button-text-primary-color)";
square_shape.style.background = "";
} else {
circle_shape.style.background = "";
square_shape.style.background = "var(--p-button-text-primary-color)";
}
brush_shape_container.appendChild(circle_shape);
brush_shape_container.appendChild(square_shape);
brush_shape_outer_container.appendChild(brush_shape_title);
@@ -50374,7 +50686,7 @@ class UIManager {
1,
100,
1,
10,
(await this.messageBroker.pull("brushSettings")).size,
(event, value) => {
this.messageBroker.publish("setBrushSize", parseInt(value));
this.updateBrushPreview();
@@ -50386,7 +50698,7 @@ class UIManager {
0,
1,
0.01,
0.7,
(await this.messageBroker.pull("brushSettings")).opacity,
(event, value) => {
this.messageBroker.publish("setBrushOpacity", parseFloat(value));
this.updateBrushPreview();
@@ -50398,7 +50710,7 @@ class UIManager {
0,
1,
0.01,
1,
(await this.messageBroker.pull("brushSettings")).hardness,
(event, value) => {
this.messageBroker.publish("setBrushHardness", parseFloat(value));
this.updateBrushPreview();
@@ -50410,7 +50722,7 @@ class UIManager {
1,
100,
1,
10,
(await this.messageBroker.pull("brushSettings")).smoothingPrecision,
(event, value) => {
this.messageBroker.publish(
"setBrushSmoothingPrecision",
@@ -50418,7 +50730,30 @@ class UIManager {
);
}
);
const resetBrushSettingsButton = document.createElement("button");
resetBrushSettingsButton.id = "resetBrushSettingsButton";
resetBrushSettingsButton.innerText = "Reset to Default";
resetBrushSettingsButton.addEventListener("click", () => {
this.messageBroker.publish(
"setBrushShape",
"arc"
/* Arc */
);
this.messageBroker.publish("setBrushSize", 10);
this.messageBroker.publish("setBrushOpacity", 0.7);
this.messageBroker.publish("setBrushHardness", 1);
this.messageBroker.publish("setBrushSmoothingPrecision", 10);
circle_shape.style.background = "var(--p-button-text-primary-color)";
square_shape.style.background = "";
thicknesSliderObj.slider.value = "10";
opacitySliderObj.slider.value = "0.7";
hardnessSliderObj.slider.value = "1";
brushSmoothingPrecisionSliderObj.slider.value = "10";
this.setBrushBorderRadius();
this.updateBrushPreview();
});
brush_settings_container.appendChild(brush_settings_title);
brush_settings_container.appendChild(resetBrushSettingsButton);
brush_settings_container.appendChild(brush_shape_outer_container);
brush_settings_container.appendChild(thicknesSliderObj.container);
brush_settings_container.appendChild(opacitySliderObj.container);
@@ -53211,4 +53546,4 @@ app.registerExtension({
});
}
});
//# sourceMappingURL=index-Bordpmzt.js.map
//# sourceMappingURL=index-je62U6DH.js.map

View File

@@ -1,6 +1,6 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { a$ as useKeybindingStore, a2 as useCommandStore, a as useSettingStore, cq as KeyComboImpl, cr as KeybindingImpl } from "./index-DjNHn37O.js";
import { a$ as useKeybindingStore, a4 as useCommandStore, a as useSettingStore, cx as KeyComboImpl, cy as KeybindingImpl } from "./index-QvfM__ze.js";
const CORE_KEYBINDINGS = [
{
combo: {
@@ -96,7 +96,7 @@ const CORE_KEYBINDINGS = [
alt: true
},
commandId: "Comfy.Canvas.ZoomIn",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
@@ -105,7 +105,7 @@ const CORE_KEYBINDINGS = [
shift: true
},
commandId: "Comfy.Canvas.ZoomIn",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
// For number pad '+'
{
@@ -114,7 +114,7 @@ const CORE_KEYBINDINGS = [
alt: true
},
commandId: "Comfy.Canvas.ZoomIn",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
@@ -122,21 +122,21 @@ const CORE_KEYBINDINGS = [
alt: true
},
commandId: "Comfy.Canvas.ZoomOut",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
key: "."
},
commandId: "Comfy.Canvas.FitView",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
key: "p"
},
commandId: "Comfy.Canvas.ToggleSelected.Pin",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
@@ -144,7 +144,7 @@ const CORE_KEYBINDINGS = [
alt: true
},
commandId: "Comfy.Canvas.ToggleSelectedNodes.Collapse",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
@@ -152,7 +152,7 @@ const CORE_KEYBINDINGS = [
ctrl: true
},
commandId: "Comfy.Canvas.ToggleSelectedNodes.Bypass",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
@@ -160,7 +160,7 @@ const CORE_KEYBINDINGS = [
ctrl: true
},
commandId: "Comfy.Canvas.ToggleSelectedNodes.Mute",
targetSelector: "#graph-canvas"
targetElementId: "graph-canvas"
},
{
combo: {
@@ -190,7 +190,7 @@ const useKeybindingService = /* @__PURE__ */ __name(() => {
return;
}
const keybinding = keybindingStore.getKeybinding(keyCombo);
if (keybinding && keybinding.targetSelector !== "#graph-canvas") {
if (keybinding && keybinding.targetElementId !== "graph-canvas") {
event.preventDefault();
await commandStore.execute(keybinding.commandId);
return;
@@ -247,4 +247,4 @@ const useKeybindingService = /* @__PURE__ */ __name(() => {
export {
useKeybindingService as u
};
//# sourceMappingURL=keybindingService-Bx7YdkXn.js.map
//# sourceMappingURL=keybindingService-Cak1En5n.js.map

View File

@@ -1,6 +1,6 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
import { $ as defineStore, ab as ref, c as computed } from "./index-DjNHn37O.js";
import { a1 as defineStore, ad as ref, c as computed } from "./index-QvfM__ze.js";
const useServerConfigStore = defineStore("serverConfig", () => {
const serverConfigById = ref({});
const serverConfigs = computed(() => {
@@ -87,4 +87,4 @@ const useServerConfigStore = defineStore("serverConfig", () => {
export {
useServerConfigStore as u
};
//# sourceMappingURL=serverConfigStore-CvyKFVuP.js.map
//# sourceMappingURL=serverConfigStore-DCme3xlV.js.map

4
web/index.html vendored
View File

@@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="stylesheet" type="text/css" href="user.css" />
<link rel="stylesheet" type="text/css" href="materialdesignicons.min.css" />
<script type="module" crossorigin src="./assets/index-DjNHn37O.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-t-sFBuUC.css">
<script type="module" crossorigin src="./assets/index-QvfM__ze.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-Cf-n7v0V.css">
</head>
<body class="litegraph grid">
<div id="vue-app"></div>