Compare commits
49 Commits
model_mana
...
v0.3.13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef85058e97 | ||
|
|
f9230bd357 | ||
|
|
537c27cbf3 | ||
|
|
6ff2e4d550 | ||
|
|
222f48c0f2 | ||
|
|
13fd4d6e45 | ||
|
|
1210d094c7 | ||
|
|
255edf2246 | ||
|
|
4f011b9a00 | ||
|
|
67feb05299 | ||
|
|
6d21740346 | ||
|
|
7fbf4b72fe | ||
|
|
14ca5f5a10 | ||
|
|
ce557cfb88 | ||
|
|
96e2a45193 | ||
|
|
dfa2b6d129 | ||
|
|
f3566f0894 | ||
|
|
ca69b41cee | ||
|
|
a058f52090 | ||
|
|
d6bbe8c40f | ||
|
|
a7fe0a94de | ||
|
|
e857dd48b8 | ||
|
|
d303cb5341 | ||
|
|
fb2ad645a3 | ||
|
|
d8a7a32779 | ||
|
|
a00e1489d2 | ||
|
|
ebf038d4fa | ||
|
|
b4de04a1c1 | ||
|
|
b1a02131c9 | ||
|
|
3a3910f91d | ||
|
|
507199d9a8 | ||
|
|
2f3ab40b62 | ||
|
|
7fc3ccdcc2 | ||
|
|
55add50220 | ||
|
|
0aa2368e46 | ||
|
|
cca96a85ae | ||
|
|
619b8cde74 | ||
|
|
31831e6ef1 | ||
|
|
88ceb28e20 | ||
|
|
23289a6a5c | ||
|
|
9d8b6c1f46 | ||
|
|
6320d05696 | ||
|
|
25683b5b02 | ||
|
|
4758fb64b9 | ||
|
|
008761166f | ||
|
|
bfd5dfd611 | ||
|
|
55ade36d01 | ||
|
|
2e20e399ea | ||
|
|
3baf92d120 |
2
.github/workflows/stable-release.yml
vendored
2
.github/workflows/stable-release.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
description: 'CUDA version'
|
||||
required: true
|
||||
type: string
|
||||
default: "124"
|
||||
default: "126"
|
||||
python_minor:
|
||||
description: 'Python minor version'
|
||||
required: true
|
||||
|
||||
4
.github/workflows/test-build.yml
vendored
4
.github/workflows/test-build.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/test-unit.yml
vendored
2
.github/workflows/test-unit.yml
vendored
@@ -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
|
||||
|
||||
@@ -17,7 +17,7 @@ on:
|
||||
description: 'cuda version'
|
||||
required: true
|
||||
type: string
|
||||
default: "124"
|
||||
default: "126"
|
||||
|
||||
python_minor:
|
||||
description: 'python minor version'
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
description: 'cuda version'
|
||||
required: true
|
||||
type: string
|
||||
default: "124"
|
||||
default: "126"
|
||||
|
||||
python_minor:
|
||||
description: 'python minor version'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
from contextlib import contextmanager
|
||||
from queue import Queue, Empty, Full
|
||||
import threading
|
||||
from app.database.updater import DatabaseUpdater
|
||||
import folder_paths
|
||||
from comfy.cli_args import args
|
||||
|
||||
|
||||
class Database:
|
||||
def __init__(self, database_path=None, pool_size=1):
|
||||
if database_path is None:
|
||||
self.exists = False
|
||||
database_path = "file::memory:?cache=shared"
|
||||
else:
|
||||
self.exists = os.path.exists(database_path)
|
||||
|
||||
self.database_path = database_path
|
||||
self.pool_size = pool_size
|
||||
# Store connections in a pool, default to 1 as normal usage is going to be from a single thread at a time
|
||||
self.connection_pool: Queue = Queue(maxsize=pool_size)
|
||||
self._db_lock = threading.Lock()
|
||||
self._initialized = False
|
||||
self._closing = False
|
||||
self._after_update_callbacks = []
|
||||
|
||||
def _setup(self):
|
||||
if self._initialized:
|
||||
return
|
||||
|
||||
with self._db_lock:
|
||||
if not self._initialized:
|
||||
self._make_db()
|
||||
self._initialized = True
|
||||
|
||||
def _create_connection(self):
|
||||
# TODO: Catch error for sqlite lib missing on linux
|
||||
logging.info(f"Creating connection to {self.database_path}")
|
||||
conn = sqlite3.connect(
|
||||
self.database_path,
|
||||
check_same_thread=False,
|
||||
uri=self.database_path.startswith("file::"),
|
||||
)
|
||||
conn.execute("PRAGMA foreign_keys = ON")
|
||||
self.exists = True
|
||||
logging.info(f"Connected!")
|
||||
return conn
|
||||
|
||||
def _make_db(self):
|
||||
with self._get_connection() as con:
|
||||
updater = DatabaseUpdater(con, self.database_path)
|
||||
result = updater.update()
|
||||
if result is not None:
|
||||
old_version, new_version = result
|
||||
|
||||
for callback in self._after_update_callbacks:
|
||||
callback(old_version, new_version)
|
||||
|
||||
def _transform(self, row, columns):
|
||||
return {col.name: value for value, col in zip(row, columns)}
|
||||
|
||||
@contextmanager
|
||||
def _get_connection(self):
|
||||
if self._closing:
|
||||
raise Exception("Database is shutting down")
|
||||
|
||||
try:
|
||||
# Try to get connection from pool
|
||||
connection = self.connection_pool.get_nowait()
|
||||
except Empty:
|
||||
# Create new connection if pool is empty
|
||||
connection = self._create_connection()
|
||||
|
||||
try:
|
||||
yield connection
|
||||
finally:
|
||||
try:
|
||||
# Try to add to pool if it's empty
|
||||
self.connection_pool.put_nowait(connection)
|
||||
except Full:
|
||||
# Pool is full, close the connection
|
||||
connection.close()
|
||||
|
||||
@contextmanager
|
||||
def get_connection(self):
|
||||
# Setup the database if it's not already initialized
|
||||
self._setup()
|
||||
with self._get_connection() as connection:
|
||||
yield connection
|
||||
|
||||
def execute(self, sql, *args):
|
||||
with self.get_connection() as connection:
|
||||
cursor = connection.execute(sql, args)
|
||||
results = cursor.fetchall()
|
||||
return results
|
||||
|
||||
def register_after_update_callback(self, callback):
|
||||
self._after_update_callbacks.append(callback)
|
||||
|
||||
def close(self):
|
||||
if self._closing:
|
||||
return
|
||||
# Drain and close all connections in the pool
|
||||
self._closing = True
|
||||
while True:
|
||||
try:
|
||||
conn = self.connection_pool.get_nowait()
|
||||
conn.close()
|
||||
except Empty:
|
||||
break
|
||||
self._closing = False
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# Create a global instance
|
||||
db_path = None
|
||||
if not args.memory_database:
|
||||
db_path = folder_paths.get_user_directory() + "/comfyui.db"
|
||||
db = Database(db_path)
|
||||
@@ -1,343 +0,0 @@
|
||||
from typing import Optional, Any, Callable
|
||||
from dataclasses import dataclass
|
||||
from functools import wraps
|
||||
from aiohttp import web
|
||||
from app.database.db import db
|
||||
|
||||
primitives = (bool, str, int, float, type(None))
|
||||
|
||||
|
||||
def is_primitive(obj):
|
||||
return isinstance(obj, primitives)
|
||||
|
||||
|
||||
class EntityError(Exception):
|
||||
def __init__(
|
||||
self, message: str, field: str = None, value: Any = None, status_code: int = 400
|
||||
):
|
||||
self.message = message
|
||||
self.field = field
|
||||
self.value = value
|
||||
self.status_code = status_code
|
||||
super().__init__(self.message)
|
||||
|
||||
def to_json(self):
|
||||
result = {"message": self.message}
|
||||
if self.field is not None:
|
||||
result["field"] = self.field
|
||||
if self.value is not None:
|
||||
result["value"] = self.value
|
||||
return result
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.message} {self.field} {self.value}"
|
||||
|
||||
|
||||
class EntityCommon(dict):
|
||||
@classmethod
|
||||
def _get_route(cls, include_key: bool):
|
||||
route = f"/db/{cls._table_name}"
|
||||
if include_key:
|
||||
route += "".join([f"/{{{k}}}" for k in cls._key_columns])
|
||||
return route
|
||||
|
||||
@classmethod
|
||||
def _register_route(cls, routes, verb: str, include_key: bool, handler: Callable):
|
||||
route = cls._get_route(include_key)
|
||||
|
||||
@getattr(routes, verb)(route)
|
||||
async def _(request):
|
||||
try:
|
||||
data = await handler(request)
|
||||
if data is None:
|
||||
return web.json_response(status=204)
|
||||
|
||||
return web.json_response(data)
|
||||
except EntityError as e:
|
||||
return web.json_response(e.to_json(), status=e.status_code)
|
||||
|
||||
@classmethod
|
||||
def _transform(cls, row: list[Any]):
|
||||
return {col: value for col, value in zip(cls._columns, row)}
|
||||
|
||||
@classmethod
|
||||
def _transform_rows(cls, rows: list[list[Any]]):
|
||||
return [cls._transform(row) for row in rows]
|
||||
|
||||
@classmethod
|
||||
def _extract_key(cls, request):
|
||||
return {key: request.match_info.get(key, None) for key in cls._key_columns}
|
||||
|
||||
@classmethod
|
||||
def _validate(cls, fields: list[str], data: dict, allow_missing: bool = False):
|
||||
result = {}
|
||||
|
||||
if not isinstance(data, dict):
|
||||
raise EntityError("Invalid data")
|
||||
|
||||
# Ensure all required fields are present
|
||||
for field in data:
|
||||
if field not in fields:
|
||||
raise EntityError("Unknown field", field)
|
||||
|
||||
for key in fields:
|
||||
col = cls._columns[key]
|
||||
if key not in data:
|
||||
if col.required and not allow_missing:
|
||||
raise EntityError("Missing field", key)
|
||||
else:
|
||||
# e.g. for updates, we allow missing fields
|
||||
continue
|
||||
elif data[key] is None and col.required:
|
||||
# Dont allow None for required fields
|
||||
raise EntityError("Required field", key)
|
||||
|
||||
# Validate data type
|
||||
value = data[key]
|
||||
|
||||
if value is not None and not is_primitive(value):
|
||||
raise EntityError("Invalid value", key, value)
|
||||
|
||||
try:
|
||||
type = col.type
|
||||
if value is not None and not isinstance(value, type):
|
||||
value = type(value)
|
||||
result[key] = value
|
||||
except Exception:
|
||||
raise EntityError("Invalid value", key, value)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _validate_id(cls, id: dict):
|
||||
return cls._validate(cls._key_columns, id)
|
||||
|
||||
@classmethod
|
||||
def _validate_data(cls, data: dict, allow_missing: bool = False):
|
||||
return cls._validate(cls._columns.keys(), data, allow_missing)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self._columns:
|
||||
self[name] = value
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self:
|
||||
return self[name]
|
||||
raise AttributeError(f"'{self.__class__.__name__}' has no attribute '{name}'")
|
||||
|
||||
|
||||
class GetEntity(EntityCommon):
|
||||
@classmethod
|
||||
def get(cls, top: Optional[int] = None, where: Optional[str] = None):
|
||||
limit = ""
|
||||
if top is not None and isinstance(top, int):
|
||||
limit = f" LIMIT {top}"
|
||||
result = db.execute(
|
||||
f"SELECT * FROM {cls._table_name}{limit}{f' WHERE {where}' if where else ''}",
|
||||
)
|
||||
|
||||
# Map each row in result to an instance of the class
|
||||
return cls._transform_rows(result)
|
||||
|
||||
@classmethod
|
||||
def register_route(cls, routes):
|
||||
async def get_handler(request):
|
||||
top = request.rel_url.query.get("top", None)
|
||||
if top is not None:
|
||||
try:
|
||||
top = int(top)
|
||||
except Exception:
|
||||
raise EntityError("Invalid top parameter", "top", top)
|
||||
return cls.get(top)
|
||||
|
||||
cls._register_route(routes, "get", False, get_handler)
|
||||
|
||||
|
||||
class GetEntityById(EntityCommon):
|
||||
@classmethod
|
||||
def get_by_id(cls, id: dict):
|
||||
id = cls._validate_id(id)
|
||||
|
||||
result = db.execute(
|
||||
f"SELECT * FROM {cls._table_name} WHERE {cls._where_clause}",
|
||||
*[id[key] for key in cls._key_columns],
|
||||
)
|
||||
|
||||
return cls._transform_rows(result)
|
||||
|
||||
@classmethod
|
||||
def register_route(cls, routes):
|
||||
async def get_by_id_handler(request):
|
||||
id = cls._extract_key(request)
|
||||
return cls.get_by_id(id)
|
||||
|
||||
cls._register_route(routes, "get", True, get_by_id_handler)
|
||||
|
||||
|
||||
class CreateEntity(EntityCommon):
|
||||
@classmethod
|
||||
def create(cls, data: dict, allow_upsert: bool = False):
|
||||
data = cls._validate_data(data)
|
||||
values = ", ".join(["?"] * len(data))
|
||||
on_conflict = ""
|
||||
|
||||
data_keys = ", ".join(list(data.keys()))
|
||||
if allow_upsert:
|
||||
# Remove key columns from data
|
||||
upsert_keys = [key for key in data if key not in cls._key_columns]
|
||||
|
||||
set_clause = ", ".join([f"{k} = excluded.{k}" for k in upsert_keys])
|
||||
on_conflict = f" ON CONFLICT ({', '.join(cls._key_columns)}) DO UPDATE SET {set_clause}"
|
||||
sql = f"INSERT INTO {cls._table_name} ({data_keys}) VALUES ({values}){on_conflict} RETURNING *"
|
||||
result = db.execute(
|
||||
sql,
|
||||
*[data[key] for key in data],
|
||||
)
|
||||
|
||||
if len(result) == 0:
|
||||
raise EntityError("Failed to create entity", status_code=500)
|
||||
|
||||
return cls._transform_rows(result)[0]
|
||||
|
||||
@classmethod
|
||||
def register_route(cls, routes):
|
||||
async def create_handler(request):
|
||||
data = await request.json()
|
||||
return cls.create(data)
|
||||
|
||||
cls._register_route(routes, "post", False, create_handler)
|
||||
|
||||
|
||||
class UpdateEntity(EntityCommon):
|
||||
@classmethod
|
||||
def update(cls, id: list, data: dict):
|
||||
id = cls._validate_id(id)
|
||||
data = cls._validate_data(data, allow_missing=True)
|
||||
|
||||
sql = f"UPDATE {cls._table_name} SET {', '.join([f'{k} = ?' for k in data])} WHERE {cls._where_clause} RETURNING *"
|
||||
result = db.execute(
|
||||
sql,
|
||||
*[data[key] for key in data],
|
||||
*[id[key] for key in cls._key_columns],
|
||||
)
|
||||
|
||||
if len(result) == 0:
|
||||
raise EntityError("Failed to update entity", status_code=404)
|
||||
|
||||
return cls._transform_rows(result)[0]
|
||||
|
||||
@classmethod
|
||||
def register_route(cls, routes):
|
||||
async def update_handler(request):
|
||||
id = cls._extract_key(request)
|
||||
data = await request.json()
|
||||
return cls.update(id, data)
|
||||
|
||||
cls._register_route(routes, "patch", True, update_handler)
|
||||
|
||||
|
||||
class UpsertEntity(CreateEntity):
|
||||
@classmethod
|
||||
def upsert(cls, data: dict):
|
||||
return cls.create(data, allow_upsert=True)
|
||||
|
||||
@classmethod
|
||||
def register_route(cls, routes):
|
||||
async def upsert_handler(request):
|
||||
data = await request.json()
|
||||
return cls.upsert(data)
|
||||
|
||||
cls._register_route(routes, "put", False, upsert_handler)
|
||||
|
||||
|
||||
class DeleteEntity(EntityCommon):
|
||||
@classmethod
|
||||
def delete(cls, id: list):
|
||||
id = cls._validate_id(id)
|
||||
db.execute(
|
||||
f"DELETE FROM {cls._table_name} WHERE {cls._where_clause}",
|
||||
*[id[key] for key in cls._key_columns],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def register_route(cls, routes):
|
||||
async def delete_handler(request):
|
||||
id = cls._extract_key(request)
|
||||
cls.delete(id)
|
||||
|
||||
cls._register_route(routes, "delete", True, delete_handler)
|
||||
|
||||
|
||||
class BaseEntity(GetEntity, CreateEntity, UpdateEntity, DeleteEntity, GetEntityById):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Column:
|
||||
type: Any
|
||||
required: bool = False
|
||||
key: bool = False
|
||||
default: Any = None
|
||||
|
||||
|
||||
def column(type_: Any, required: bool = False, key: bool = False, default: Any = None):
|
||||
return Column(type_, required, key, default)
|
||||
|
||||
|
||||
def table(table_name: str):
|
||||
def decorator(cls):
|
||||
# Store table name
|
||||
cls._table_name = table_name
|
||||
|
||||
# Process column definitions
|
||||
columns: dict[str, Column] = {}
|
||||
for attr_name, attr_value in cls.__dict__.items():
|
||||
if isinstance(attr_value, Column):
|
||||
columns[attr_name] = attr_value
|
||||
|
||||
# Store columns metadata
|
||||
cls._columns = columns
|
||||
cls._key_columns = [col for col in columns if columns[col].key]
|
||||
cls._column_csv = ", ".join([col for col in columns])
|
||||
cls._where_clause = " AND ".join([f"{col} = ?" for col in cls._key_columns])
|
||||
|
||||
# Add initialization
|
||||
original_init = cls.__init__
|
||||
|
||||
@wraps(original_init)
|
||||
def new_init(self, *args, **kwargs):
|
||||
# Initialize columns with default values
|
||||
for col_name, col_def in cls._columns.items():
|
||||
setattr(self, col_name, col_def.default)
|
||||
# Call original init
|
||||
original_init(self, *args, **kwargs)
|
||||
|
||||
cls.__init__ = new_init
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def test():
|
||||
@table("models")
|
||||
class Model(BaseEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
path: str = column(str, required=True)
|
||||
name: str = column(str, required=True)
|
||||
description: Optional[str] = column(str)
|
||||
architecture: Optional[str] = column(str)
|
||||
type: str = column(str, required=True)
|
||||
hash: Optional[str] = column(str)
|
||||
source_url: Optional[str] = column(str)
|
||||
|
||||
return Model
|
||||
|
||||
|
||||
@table("test")
|
||||
class Test(GetEntity, CreateEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
test: str = column(str, required=True)
|
||||
|
||||
|
||||
Model = test()
|
||||
@@ -1,32 +0,0 @@
|
||||
from app.database.db import db
|
||||
from aiohttp import web
|
||||
|
||||
def create_routes(
|
||||
routes, prefix, entity, get=False, get_by_id=False, post=False, delete=False
|
||||
):
|
||||
if get:
|
||||
@routes.get(f"/{prefix}/{table}")
|
||||
async def get_table(request):
|
||||
connection = db.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(f"SELECT * FROM {table}")
|
||||
rows = cursor.fetchall()
|
||||
return web.json_response(rows)
|
||||
|
||||
if get_by_id:
|
||||
@routes.get(f"/{prefix}/{table}/{id}")
|
||||
async def get_table_by_id(request):
|
||||
connection = db.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(f"SELECT * FROM {table} WHERE id = {id}")
|
||||
row = cursor.fetchone()
|
||||
return web.json_response(row)
|
||||
|
||||
if post:
|
||||
@routes.post(f"/{prefix}/{table}")
|
||||
async def post_table(request):
|
||||
data = await request.json()
|
||||
connection = db.get_connection()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(f"INSERT INTO {table} ({data}) VALUES ({data})")
|
||||
return web.json_response({"status": "success"})
|
||||
@@ -1,79 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
from app.database.versions.v1 import v1
|
||||
|
||||
|
||||
class DatabaseUpdater:
|
||||
def __init__(self, connection, database_path):
|
||||
self.connection = connection
|
||||
self.database_path = database_path
|
||||
self.current_version = self.get_db_version()
|
||||
self.version_updates = {
|
||||
1: v1,
|
||||
}
|
||||
self.max_version = max(self.version_updates.keys())
|
||||
self.update_required = self.current_version < self.max_version
|
||||
logging.info(f"Database version: {self.current_version}")
|
||||
|
||||
def get_db_version(self):
|
||||
return self.connection.execute("PRAGMA user_version").fetchone()[0]
|
||||
|
||||
def backup(self):
|
||||
bkp_path = self.database_path + ".bkp"
|
||||
if os.path.exists(bkp_path):
|
||||
# TODO: auto-rollback failed upgrades
|
||||
raise Exception(
|
||||
f"Database backup already exists, this indicates that a previous upgrade failed. Please restore this backup before continuing. Backup location: {bkp_path}"
|
||||
)
|
||||
|
||||
bkp = sqlite3.connect(bkp_path)
|
||||
self.connection.backup(bkp)
|
||||
bkp.close()
|
||||
logging.info("Database backup taken pre-upgrade.")
|
||||
return bkp_path
|
||||
|
||||
def update(self):
|
||||
if not self.update_required:
|
||||
return None
|
||||
|
||||
bkp_version = self.current_version
|
||||
bkp_path = None
|
||||
if self.current_version > 0:
|
||||
bkp_path = self.backup()
|
||||
|
||||
logging.info(f"Updating database: {self.current_version} -> {self.max_version}")
|
||||
|
||||
dirname = os.path.dirname(__file__)
|
||||
cursor = self.connection.cursor()
|
||||
for version in range(self.current_version + 1, self.max_version + 1):
|
||||
filename = os.path.join(dirname, f"versions/v{version}.sql")
|
||||
if not os.path.exists(filename):
|
||||
raise Exception(
|
||||
f"Database update script for version {version} not found"
|
||||
)
|
||||
|
||||
try:
|
||||
with open(filename, "r") as file:
|
||||
sql = file.read()
|
||||
cursor.executescript(sql)
|
||||
except Exception as e:
|
||||
raise Exception(
|
||||
f"Failed to execute update script for version {version}: {e}"
|
||||
)
|
||||
|
||||
method = self.version_updates[version]
|
||||
if method is not None:
|
||||
method(cursor)
|
||||
|
||||
cursor.execute("PRAGMA user_version = %d" % self.max_version)
|
||||
self.connection.commit()
|
||||
cursor.close()
|
||||
self.current_version = self.get_db_version()
|
||||
|
||||
if bkp_path:
|
||||
# Keep a copy of the backup in case something goes wrong and we need to rollback
|
||||
os.rename(bkp_path, self.database_path + f".v{bkp_version}.bkp")
|
||||
logging.info(f"Upgrade to successful.")
|
||||
|
||||
return (bkp_version, self.current_version)
|
||||
@@ -1,17 +0,0 @@
|
||||
from folder_paths import folder_names_and_paths, get_filename_list, get_full_path
|
||||
|
||||
|
||||
def v1(cursor):
|
||||
print("Updating to v1")
|
||||
for folder_name in folder_names_and_paths.keys():
|
||||
if folder_name == "custom_nodes":
|
||||
continue
|
||||
|
||||
files = get_filename_list(folder_name)
|
||||
for file in files:
|
||||
file_path = get_full_path(folder_name, file)
|
||||
file_without_extension = file.rsplit(".", maxsplit=1)[0]
|
||||
cursor.execute(
|
||||
"INSERT INTO models (path, name, type) VALUES (?, ?, ?)",
|
||||
(file_path, file_without_extension, folder_name),
|
||||
)
|
||||
@@ -1,41 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
models (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
path TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
architecture TEXT,
|
||||
type TEXT NOT NULL,
|
||||
hash TEXT,
|
||||
source_url TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
tags (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS
|
||||
model_tags (
|
||||
model_id INTEGER NOT NULL,
|
||||
tag_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (model_id, tag_id),
|
||||
FOREIGN KEY (model_id) REFERENCES models (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
tags (name)
|
||||
VALUES
|
||||
('character'),
|
||||
('style'),
|
||||
('concept'),
|
||||
('clothing'),
|
||||
('poses'),
|
||||
('background'),
|
||||
('vehicle'),
|
||||
('buildings'),
|
||||
('objects'),
|
||||
('animal'),
|
||||
('action');
|
||||
@@ -1,63 +0,0 @@
|
||||
import hashlib
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from comfy.cli_args import args
|
||||
|
||||
|
||||
class ModelHasher:
|
||||
|
||||
def __init__(self):
|
||||
self._thread = None
|
||||
self._lock = threading.Lock()
|
||||
self._model_entity = None
|
||||
|
||||
def start(self, model_entity):
|
||||
if args.disable_model_hashing:
|
||||
return
|
||||
|
||||
self._model_entity = model_entity
|
||||
|
||||
if self._thread is None:
|
||||
# Lock to prevent multiple threads from starting
|
||||
with self._lock:
|
||||
if self._thread is None:
|
||||
self._thread = threading.Thread(target=self._hash_models)
|
||||
self._thread.daemon = True
|
||||
self._thread.start()
|
||||
|
||||
def _get_models(self):
|
||||
models = self._model_entity.get("WHERE hash IS NULL")
|
||||
return models
|
||||
|
||||
def _hash_model(self, model_path):
|
||||
h = hashlib.sha256()
|
||||
b = bytearray(128 * 1024)
|
||||
mv = memoryview(b)
|
||||
with open(model_path, "rb", buffering=0) as f:
|
||||
while n := f.readinto(mv):
|
||||
h.update(mv[:n])
|
||||
hash = h.hexdigest()
|
||||
return hash
|
||||
|
||||
def _hash_models(self):
|
||||
while True:
|
||||
models = self._get_models()
|
||||
|
||||
if len(models) == 0:
|
||||
break
|
||||
|
||||
for model in models:
|
||||
time.sleep(0)
|
||||
now = time.time()
|
||||
logging.info(f"Hashing model {model['path']}")
|
||||
hash = self._hash_model(model["path"])
|
||||
logging.info(
|
||||
f"Hashed model {model['path']} in {time.time() - now} seconds"
|
||||
)
|
||||
self._model_entity.update((model["id"],), {"hash": hash})
|
||||
|
||||
self._thread = None
|
||||
|
||||
|
||||
model_hasher = ModelHasher()
|
||||
@@ -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.")
|
||||
@@ -143,13 +144,9 @@ parser.add_argument("--multi-user", action="store_true", help="Enables per-user
|
||||
parser.add_argument("--verbose", default='INFO', const='DEBUG', nargs="?", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], help='Set the logging level')
|
||||
parser.add_argument("--log-stdout", action="store_true", help="Send normal process output to stdout instead of stderr (default).")
|
||||
|
||||
parser.add_argument("--memory-database", default=False, action="store_true", help="Use an in-memory database instead of a file-based one.")
|
||||
parser.add_argument("--disable-model-hashing", action="store_true", help="Disable model hashing.")
|
||||
|
||||
# The default built-in provider hosted under web/
|
||||
DEFAULT_VERSION_STRING = "comfyanonymous/ComfyUI@latest"
|
||||
|
||||
|
||||
parser.add_argument(
|
||||
"--front-end-version",
|
||||
type=str,
|
||||
@@ -180,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()
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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={}):
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,)
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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])])
|
||||
|
||||
@@ -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}),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]] = {}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
3
main.py
3
main.py
@@ -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())
|
||||
|
||||
4
nodes.py
4
nodes.py
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -2,6 +2,7 @@ torch
|
||||
torchsde
|
||||
torchvision
|
||||
torchaudio
|
||||
numpy>=1.25.0
|
||||
einops
|
||||
transformers>=4.28.1
|
||||
tokenizers>=0.13.3
|
||||
|
||||
23
server.py
23
server.py
@@ -34,9 +34,6 @@ from app.model_manager import ModelFileManager
|
||||
from app.custom_node_manager import CustomNodeManager
|
||||
from typing import Optional
|
||||
from api_server.routes.internal.internal_routes import InternalRoutes
|
||||
from app.database.entities import get_entity, init_entities
|
||||
from app.database.db import db
|
||||
from app.model_hasher import model_hasher
|
||||
|
||||
class BinaryEventTypes:
|
||||
PREVIEW_IMAGE = 1
|
||||
@@ -332,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)
|
||||
@@ -373,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)
|
||||
@@ -685,25 +688,11 @@ class PromptServer():
|
||||
timeout = aiohttp.ClientTimeout(total=None) # no timeout
|
||||
self.client_session = aiohttp.ClientSession(timeout=timeout)
|
||||
|
||||
def init_db(self, routes):
|
||||
init_entities(routes)
|
||||
models = get_entity("models")
|
||||
|
||||
if db.exists:
|
||||
model_hasher.start(models)
|
||||
else:
|
||||
def on_db_update(_, __):
|
||||
model_hasher.start(models)
|
||||
|
||||
db.register_after_update_callback(on_db_update)
|
||||
|
||||
|
||||
def add_routes(self):
|
||||
self.user_manager.add_routes(self.routes)
|
||||
self.model_file_manager.add_routes(self.routes)
|
||||
self.custom_node_manager.add_routes(self.routes, self.app, nodes.LOADED_MODULE_DIRS.items())
|
||||
self.app.add_subapp('/internal', self.internal_routes.get_app())
|
||||
self.init_db(self.routes)
|
||||
|
||||
# Prefix every route with /api for easier matching for delegation.
|
||||
# This is very useful for frontend dev server, which need to forward
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,513 +0,0 @@
|
||||
from comfy.cli_args import args
|
||||
|
||||
args.memory_database = True # force in-memory database for testing
|
||||
|
||||
from typing import Callable, Optional
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from unittest.mock import patch
|
||||
from aiohttp import web
|
||||
from app.database.entities import (
|
||||
DeleteEntity,
|
||||
column,
|
||||
table,
|
||||
Column,
|
||||
GetEntity,
|
||||
GetEntityById,
|
||||
CreateEntity,
|
||||
UpsertEntity,
|
||||
UpdateEntity,
|
||||
)
|
||||
from app.database.db import db
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
def create_table(entity):
|
||||
# reset db
|
||||
db.close()
|
||||
|
||||
cols: list[Column] = entity._columns
|
||||
# Create tables as temporary so when we close the db, the tables are dropped for next test
|
||||
sql = f"CREATE TEMPORARY TABLE {entity._table_name} ( "
|
||||
for col_name, col in cols.items():
|
||||
type = None
|
||||
if col.type == int:
|
||||
type = "INTEGER"
|
||||
elif col.type == str:
|
||||
type = "TEXT"
|
||||
|
||||
sql += f"{col_name} {type}"
|
||||
if col.required:
|
||||
sql += " NOT NULL"
|
||||
sql += ", "
|
||||
|
||||
sql += f"PRIMARY KEY ({', '.join(entity._key_columns)})"
|
||||
sql += ")"
|
||||
db.execute(sql)
|
||||
|
||||
|
||||
async def wrap_db(method: Callable, expected_sql: str, expected_args: list):
|
||||
with patch.object(db, "execute", wraps=db.execute) as mock:
|
||||
response = await method()
|
||||
assert mock.call_count == 1
|
||||
assert mock.call_args[0][0] == expected_sql
|
||||
assert mock.call_args[0][1:] == expected_args
|
||||
return response
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def getable_entity():
|
||||
@table("getable_entity")
|
||||
class GetableEntity(GetEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
test: str = column(str, required=True)
|
||||
nullable: Optional[str] = column(str)
|
||||
|
||||
return GetableEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def getable_by_id_entity():
|
||||
@table("getable_by_id_entity")
|
||||
class GetableByIdEntity(GetEntityById):
|
||||
id: int = column(int, required=True, key=True)
|
||||
test: str = column(str, required=True)
|
||||
|
||||
return GetableByIdEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def getable_by_id_composite_entity():
|
||||
@table("getable_by_id_composite_entity")
|
||||
class GetableByIdCompositeEntity(GetEntityById):
|
||||
id1: str = column(str, required=True, key=True)
|
||||
id2: int = column(int, required=True, key=True)
|
||||
test: str = column(str, required=True)
|
||||
|
||||
return GetableByIdCompositeEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def creatable_entity():
|
||||
@table("creatable_entity")
|
||||
class CreatableEntity(CreateEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
test: str = column(str, required=True)
|
||||
reqd: str = column(str, required=True)
|
||||
nullable: Optional[str] = column(str)
|
||||
|
||||
return CreatableEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def upsertable_entity():
|
||||
@table("upsertable_entity")
|
||||
class UpsertableEntity(UpsertEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
test: str = column(str, required=True)
|
||||
reqd: str = column(str, required=True)
|
||||
nullable: Optional[str] = column(str)
|
||||
|
||||
return UpsertableEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def updateable_entity():
|
||||
@table("updateable_entity")
|
||||
class UpdateableEntity(UpdateEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
reqd: str = column(str, required=True)
|
||||
|
||||
return UpdateableEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def deletable_entity():
|
||||
@table("deletable_entity")
|
||||
class DeletableEntity(DeleteEntity):
|
||||
id: int = column(int, required=True, key=True)
|
||||
|
||||
return DeletableEntity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def deletable_composite_entity():
|
||||
@table("deletable_composite_entity")
|
||||
class DeletableCompositeEntity(DeleteEntity):
|
||||
id1: str = column(str, required=True, key=True)
|
||||
id2: int = column(int, required=True, key=True)
|
||||
|
||||
return DeletableCompositeEntity
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def entity(request):
|
||||
value = request.getfixturevalue(request.param)
|
||||
create_table(value)
|
||||
return value
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def client(aiohttp_client, app):
|
||||
return await aiohttp_client(app)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app(entity):
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
entity.register_route(routes)
|
||||
app.add_routes(routes)
|
||||
return app
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_entity"], indirect=True)
|
||||
async def test_get_model_empty_response(client):
|
||||
expected_sql = "SELECT * FROM getable_entity"
|
||||
expected_args = ()
|
||||
response = await wrap_db(
|
||||
lambda: client.get("/db/getable_entity"), expected_sql, expected_args
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_entity"], indirect=True)
|
||||
async def test_get_model_with_data(client):
|
||||
# seed db
|
||||
db.execute(
|
||||
"INSERT INTO getable_entity (id, test, nullable) VALUES (1, 'test1', NULL), (2, 'test2', 'test2')"
|
||||
)
|
||||
|
||||
expected_sql = "SELECT * FROM getable_entity"
|
||||
expected_args = ()
|
||||
response = await wrap_db(
|
||||
lambda: client.get("/db/getable_entity"), expected_sql, expected_args
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == [
|
||||
{"id": 1, "test": "test1", "nullable": None},
|
||||
{"id": 2, "test": "test2", "nullable": "test2"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_entity"], indirect=True)
|
||||
async def test_get_model_with_top_parameter(client):
|
||||
# seed with 3 rows
|
||||
db.execute(
|
||||
"INSERT INTO getable_entity (id, test, nullable) VALUES (1, 'test1', NULL), (2, 'test2', 'test2'), (3, 'test3', 'test3')"
|
||||
)
|
||||
|
||||
expected_sql = "SELECT * FROM getable_entity LIMIT 2"
|
||||
expected_args = ()
|
||||
response = await wrap_db(
|
||||
lambda: client.get("/db/getable_entity?top=2"),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == [
|
||||
{"id": 1, "test": "test1", "nullable": None},
|
||||
{"id": 2, "test": "test2", "nullable": "test2"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_entity"], indirect=True)
|
||||
async def test_get_model_with_invalid_top_parameter(client):
|
||||
response = await client.get("/db/getable_entity?top=hello")
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Invalid top parameter",
|
||||
"field": "top",
|
||||
"value": "hello",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_by_id_entity"], indirect=True)
|
||||
async def test_get_model_by_id_empty_response(client):
|
||||
# seed db
|
||||
db.execute("INSERT INTO getable_by_id_entity (id, test) VALUES (1, 'test1')")
|
||||
|
||||
expected_sql = "SELECT * FROM getable_by_id_entity WHERE id = ?"
|
||||
expected_args = (1,)
|
||||
response = await wrap_db(
|
||||
lambda: client.get("/db/getable_by_id_entity/1"),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == [
|
||||
{"id": 1, "test": "test1"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_by_id_entity"], indirect=True)
|
||||
async def test_get_model_by_id_with_invalid_id(client):
|
||||
response = await client.get("/db/getable_by_id_entity/hello")
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Invalid value",
|
||||
"field": "id",
|
||||
"value": "hello",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_by_id_composite_entity"], indirect=True)
|
||||
async def test_get_model_by_id_composite(client):
|
||||
# seed db
|
||||
db.execute(
|
||||
"INSERT INTO getable_by_id_composite_entity (id1, id2, test) VALUES ('one', 2, 'test')"
|
||||
)
|
||||
|
||||
expected_sql = (
|
||||
"SELECT * FROM getable_by_id_composite_entity WHERE id1 = ? AND id2 = ?"
|
||||
)
|
||||
expected_args = ("one", 2)
|
||||
response = await wrap_db(
|
||||
lambda: client.get("/db/getable_by_id_composite_entity/one/2"),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == [
|
||||
{"id1": "one", "id2": 2, "test": "test"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["getable_by_id_composite_entity"], indirect=True)
|
||||
async def test_get_model_by_id_composite_with_invalid_id(client):
|
||||
response = await client.get("/db/getable_by_id_composite_entity/hello/hello")
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Invalid value",
|
||||
"field": "id2",
|
||||
"value": "hello",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model(client):
|
||||
expected_sql = (
|
||||
"INSERT INTO creatable_entity (id, test, reqd) VALUES (?, ?, ?) RETURNING *"
|
||||
)
|
||||
expected_args = (1, "test1", "reqd1")
|
||||
response = await wrap_db(
|
||||
lambda: client.post(
|
||||
"/db/creatable_entity", json={"id": 1, "test": "test1", "reqd": "reqd1"}
|
||||
),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == {
|
||||
"id": 1,
|
||||
"test": "test1",
|
||||
"reqd": "reqd1",
|
||||
"nullable": None,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model_missing_required_field(client):
|
||||
response = await client.post(
|
||||
"/db/creatable_entity", json={"id": 1, "test": "test1"}
|
||||
)
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Missing field",
|
||||
"field": "reqd",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model_missing_key_field(client):
|
||||
response = await client.post(
|
||||
"/db/creatable_entity",
|
||||
json={"test": "test1", "reqd": "reqd1"}, # Missing 'id' which is a key
|
||||
)
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Missing field",
|
||||
"field": "id",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model_invalid_key_data(client):
|
||||
response = await client.post(
|
||||
"/db/creatable_entity",
|
||||
json={
|
||||
"id": "not_an_integer",
|
||||
"test": "test1",
|
||||
"reqd": "reqd1",
|
||||
}, # id should be int
|
||||
)
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Invalid value",
|
||||
"field": "id",
|
||||
"value": "not_an_integer",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model_invalid_field_data(client):
|
||||
response = await client.post(
|
||||
"/db/creatable_entity",
|
||||
json={"id": "aaa", "test": "123", "reqd": "reqd1"}, # id should be int
|
||||
)
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Invalid value",
|
||||
"field": "id",
|
||||
"value": "aaa",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model_invalid_field_type(client):
|
||||
response = await client.post(
|
||||
"/db/creatable_entity",
|
||||
json={
|
||||
"id": 1,
|
||||
"test": ["invalid_array"],
|
||||
"reqd": "reqd1",
|
||||
}, # test should be string
|
||||
)
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Invalid value",
|
||||
"field": "test",
|
||||
"value": ["invalid_array"],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["creatable_entity"], indirect=True)
|
||||
async def test_create_model_invalid_field_name(client):
|
||||
response = await client.post(
|
||||
"/db/creatable_entity",
|
||||
json={"id": 1, "test": "test1", "reqd": "reqd1", "nonexistent_field": "value"},
|
||||
)
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Unknown field",
|
||||
"field": "nonexistent_field",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["upsertable_entity"], indirect=True)
|
||||
async def test_upsert_model(client):
|
||||
expected_sql = (
|
||||
"INSERT INTO upsertable_entity (id, test, reqd) VALUES (?, ?, ?) "
|
||||
"ON CONFLICT (id) DO UPDATE SET test = excluded.test, reqd = excluded.reqd "
|
||||
"RETURNING *"
|
||||
)
|
||||
expected_args = (1, "test1", "reqd1")
|
||||
response = await wrap_db(
|
||||
lambda: client.put(
|
||||
"/db/upsertable_entity", json={"id": 1, "test": "test1", "reqd": "reqd1"}
|
||||
),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == {
|
||||
"id": 1,
|
||||
"test": "test1",
|
||||
"reqd": "reqd1",
|
||||
"nullable": None,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["updateable_entity"], indirect=True)
|
||||
async def test_update_model(client):
|
||||
# seed db
|
||||
db.execute("INSERT INTO updateable_entity (id, reqd) VALUES (1, 'test1')")
|
||||
|
||||
expected_sql = "UPDATE updateable_entity SET reqd = ? WHERE id = ? RETURNING *"
|
||||
expected_args = ("updated_test", 1)
|
||||
response = await wrap_db(
|
||||
lambda: client.patch("/db/updateable_entity/1", json={"reqd": "updated_test"}),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == {
|
||||
"id": 1,
|
||||
"reqd": "updated_test",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["updateable_entity"], indirect=True)
|
||||
async def test_update_model_reject_null_required_field(client):
|
||||
response = await client.patch("/db/updateable_entity/1", json={"reqd": None})
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Required field",
|
||||
"field": "reqd",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["updateable_entity"], indirect=True)
|
||||
async def test_update_model_reject_invalid_field(client):
|
||||
response = await client.patch("/db/updateable_entity/1", json={"hello": "world"})
|
||||
|
||||
assert response.status == 400
|
||||
assert await response.json() == {
|
||||
"message": "Unknown field",
|
||||
"field": "hello",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["updateable_entity"], indirect=True)
|
||||
async def test_update_model_reject_missing_record(client):
|
||||
response = await client.patch(
|
||||
"/db/updateable_entity/1", json={"reqd": "updated_test"}
|
||||
)
|
||||
|
||||
assert response.status == 404
|
||||
assert await response.json() == {
|
||||
"message": "Failed to update entity",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["deletable_entity"], indirect=True)
|
||||
async def test_delete_model(client):
|
||||
expected_sql = "DELETE FROM deletable_entity WHERE id = ?"
|
||||
expected_args = (1,)
|
||||
response = await wrap_db(
|
||||
lambda: client.delete("/db/deletable_entity/1"),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 204
|
||||
|
||||
|
||||
@pytest.mark.parametrize("entity", ["deletable_composite_entity"], indirect=True)
|
||||
async def test_delete_model_composite_key(client):
|
||||
expected_sql = "DELETE FROM deletable_composite_entity WHERE id1 = ? AND id2 = ?"
|
||||
expected_args = ("one", 2)
|
||||
response = await wrap_db(
|
||||
lambda: client.delete("/db/deletable_composite_entity/one/2"),
|
||||
expected_sql,
|
||||
expected_args,
|
||||
)
|
||||
|
||||
assert response.status == 204
|
||||
@@ -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
|
||||
|
||||
71
tests-unit/utils/json_util_test.py
Normal file
71
tests-unit/utils/json_util_test.py
Normal 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
26
utils/json_util.py
Normal 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
|
||||
23
web/assets/BaseViewTemplate-BNGF4K22.js
generated
vendored
23
web/assets/BaseViewTemplate-BNGF4K22.js
generated
vendored
@@ -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
54
web/assets/BaseViewTemplate-BhQMaVFP.js
generated
vendored
Normal 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
22
web/assets/DesktopStartView-le6AjGZr.js
generated
vendored
Normal 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
|
||||
6
web/assets/DownloadGitView-DeC7MBzG.js → web/assets/DownloadGitView-rPK_vYgU.js
generated
vendored
6
web/assets/DownloadGitView-DeC7MBzG.js → web/assets/DownloadGitView-rPK_vYgU.js
generated
vendored
@@ -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
|
||||
9
web/assets/ExtensionPanel-D4Phn0Zr.js → web/assets/ExtensionPanel-3jWrm6Zi.js
generated
vendored
9
web/assets/ExtensionPanel-D4Phn0Zr.js → web/assets/ExtensionPanel-3jWrm6Zi.js
generated
vendored
@@ -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
|
||||
1109
web/assets/GraphView-HVeNbkaW.js → web/assets/GraphView-CDDCHVO0.js
generated
vendored
1109
web/assets/GraphView-HVeNbkaW.js → web/assets/GraphView-CDDCHVO0.js
generated
vendored
File diff suppressed because it is too large
Load Diff
157
web/assets/GraphView-CIRWBKTm.css → web/assets/GraphView-CqZ3opAX.css
generated
vendored
157
web/assets/GraphView-CIRWBKTm.css → web/assets/GraphView-CqZ3opAX.css
generated
vendored
@@ -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;
|
||||
71
web/assets/InstallView-CAcYt0HL.js → web/assets/InstallView-By3hC1fC.js
generated
vendored
71
web/assets/InstallView-CAcYt0HL.js → web/assets/InstallView-By3hC1fC.js
generated
vendored
@@ -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
|
||||
36
web/assets/InstallView-CwQdoH-C.css → web/assets/InstallView-CxhfFC8Y.css
generated
vendored
36
web/assets/InstallView-CwQdoH-C.css → web/assets/InstallView-CxhfFC8Y.css
generated
vendored
@@ -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
|
||||
}
|
||||
11
web/assets/KeybindingPanel-Dc3C4lG1.js → web/assets/KeybindingPanel-D6O16W_1.js
generated
vendored
11
web/assets/KeybindingPanel-Dc3C4lG1.js → web/assets/KeybindingPanel-D6O16W_1.js
generated
vendored
@@ -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
|
||||
@@ -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)));
|
||||
}
|
||||
@@ -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
86
web/assets/MetricsConsentView-lSfLu4nr.js
generated
vendored
Normal 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
|
||||
8
web/assets/NotSupportedView-bFzHmqNj.css → web/assets/NotSupportedView-DQerxQzi.css
generated
vendored
8
web/assets/NotSupportedView-bFzHmqNj.css → web/assets/NotSupportedView-DQerxQzi.css
generated
vendored
@@ -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);
|
||||
}
|
||||
14
web/assets/NotSupportedView-Drz3x2d-.js → web/assets/NotSupportedView-Vc8_xWgH.js
generated
vendored
14
web/assets/NotSupportedView-Drz3x2d-.js → web/assets/NotSupportedView-Vc8_xWgH.js
generated
vendored
@@ -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
|
||||
6
web/assets/ServerConfigPanel-Be4StJmv.js → web/assets/ServerConfigPanel-B-w0HFlz.js
generated
vendored
6
web/assets/ServerConfigPanel-Be4StJmv.js → web/assets/ServerConfigPanel-B-w0HFlz.js
generated
vendored
@@ -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
101
web/assets/ServerStartView-48wfE1MS.js
generated
vendored
Normal 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
|
||||
98
web/assets/ServerStartView-CIDTUh4x.js
generated
vendored
98
web/assets/ServerStartView-CIDTUh4x.js
generated
vendored
@@ -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
|
||||
2
web/assets/ServerStartView-CnyN4Ib6.css → web/assets/ServerStartView-CJiwVDQY.css
generated
vendored
2
web/assets/ServerStartView-CnyN4Ib6.css → web/assets/ServerStartView-CJiwVDQY.css
generated
vendored
@@ -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;
|
||||
}
|
||||
6
web/assets/UserSelectView-B3jYchWu.js → web/assets/UserSelectView-CXmVKOeK.js
generated
vendored
6
web/assets/UserSelectView-B3jYchWu.js → web/assets/UserSelectView-CXmVKOeK.js
generated
vendored
@@ -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
|
||||
6
web/assets/WelcomeView-N0ZXLjdi.js → web/assets/WelcomeView-C8whKl15.js
generated
vendored
6
web/assets/WelcomeView-N0ZXLjdi.js → web/assets/WelcomeView-C8whKl15.js
generated
vendored
@@ -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
27
web/assets/index-5HFeZax4.js
generated
vendored
@@ -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
|
||||
146
web/assets/index-t-sFBuUC.css → web/assets/index-Cf-n7v0V.css
generated
vendored
146
web/assets/index-t-sFBuUC.css → web/assets/index-Cf-n7v0V.css
generated
vendored
@@ -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;
|
||||
}
|
||||
|
||||
7
web/assets/index-B5F0uxTQ.js → web/assets/index-DpF-ptbJ.js
generated
vendored
7
web/assets/index-B5F0uxTQ.js → web/assets/index-DpF-ptbJ.js
generated
vendored
@@ -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
|
||||
4
web/assets/index-B-aVupP5.js → web/assets/index-Q1cQr26V.js
generated
vendored
4
web/assets/index-B-aVupP5.js → web/assets/index-Q1cQr26V.js
generated
vendored
@@ -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
|
||||
68188
web/assets/index-DjNHn37O.js → web/assets/index-QvfM__ze.js
generated
vendored
68188
web/assets/index-DjNHn37O.js → web/assets/index-QvfM__ze.js
generated
vendored
File diff suppressed because one or more lines are too long
173
web/assets/index-jXPKy3pP.js
generated
vendored
173
web/assets/index-jXPKy3pP.js
generated
vendored
@@ -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
|
||||
613
web/assets/index-Bordpmzt.js → web/assets/index-je62U6DH.js
generated
vendored
613
web/assets/index-Bordpmzt.js → web/assets/index-je62U6DH.js
generated
vendored
@@ -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
|
||||
24
web/assets/keybindingService-Bx7YdkXn.js → web/assets/keybindingService-Cak1En5n.js
generated
vendored
24
web/assets/keybindingService-Bx7YdkXn.js → web/assets/keybindingService-Cak1En5n.js
generated
vendored
@@ -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
|
||||
4
web/assets/serverConfigStore-CvyKFVuP.js → web/assets/serverConfigStore-DCme3xlV.js
generated
vendored
4
web/assets/serverConfigStore-CvyKFVuP.js → web/assets/serverConfigStore-DCme3xlV.js
generated
vendored
@@ -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
4
web/index.html
vendored
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user