Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
982a60e85f |
8
.github/workflows/publish.yml
vendored
8
.github/workflows/publish.yml
vendored
@@ -7,19 +7,15 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- "pyproject.toml"
|
- "pyproject.toml"
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-node:
|
publish-node:
|
||||||
name: Publish Custom Node to registry
|
name: Publish Custom Node to registry
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'ltdrdata' }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Publish Custom Node
|
- name: Publish Custom Node
|
||||||
uses: Comfy-Org/publish-node-action@v1
|
uses: Comfy-Org/publish-node-action@main
|
||||||
with:
|
with:
|
||||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||||
@@ -314,6 +314,9 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
* Use `aria2` as downloader
|
* Use `aria2` as downloader
|
||||||
* [howto](docs/en/use_aria2.md)
|
* [howto](docs/en/use_aria2.md)
|
||||||
|
|
||||||
|
* If you add the item `skip_migration_check = True` to `config.ini`, it will not check whether there are nodes that can be migrated at startup.
|
||||||
|
* This option can be used if performance issues occur in a Colab+GDrive environment.
|
||||||
|
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
|
|||||||
38
cm-cli.py
38
cm-cli.py
@@ -43,8 +43,8 @@ import cnr_utils
|
|||||||
|
|
||||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||||
|
|
||||||
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")):
|
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")):
|
||||||
@@ -1047,16 +1047,18 @@ def save_snapshot(
|
|||||||
):
|
):
|
||||||
cmd_ctx.set_user_directory(user_directory)
|
cmd_ctx.set_user_directory(user_directory)
|
||||||
|
|
||||||
if output is not None:
|
if output is None:
|
||||||
if(not output.endswith('.json') and not output.endswith('.yaml')):
|
print("[bold red]ERROR: missing output path[/bold red]")
|
||||||
print("[bold red]ERROR: output path should be either '.json' or '.yaml' file.[/bold red]")
|
raise typer.Exit(code=1)
|
||||||
raise typer.Exit(code=1)
|
|
||||||
|
|
||||||
dir_path = os.path.dirname(output)
|
|
||||||
|
|
||||||
if(dir_path != '' and not os.path.exists(dir_path)):
|
if(not output.endswith('.json') and not output.endswith('.yaml')):
|
||||||
print(f"[bold red]ERROR: {output} path not exists.[/bold red]")
|
print("[bold red]ERROR: output path should be either '.json' or '.yaml' file.[/bold red]")
|
||||||
raise typer.Exit(code=1)
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
dir_path = os.path.dirname(output)
|
||||||
|
if(dir_path != '' and not os.path.exists(dir_path)):
|
||||||
|
print(f"[bold red]ERROR: {output} path not exists.[/bold red]")
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
path = asyncio.run(core.save_snapshot_with_postfix('snapshot', output, not full_snapshot))
|
path = asyncio.run(core.save_snapshot_with_postfix('snapshot', output, not full_snapshot))
|
||||||
print(f"Current snapshot is saved as `{path}`")
|
print(f"Current snapshot is saved as `{path}`")
|
||||||
@@ -1269,6 +1271,20 @@ def export_custom_node_ids(
|
|||||||
print(f"{x['id']}@unknown", file=output_file)
|
print(f"{x['id']}@unknown", file=output_file)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command(
|
||||||
|
"migrate",
|
||||||
|
help="Migrate legacy node system to new node system",
|
||||||
|
)
|
||||||
|
def migrate(
|
||||||
|
user_directory: str = typer.Option(
|
||||||
|
None,
|
||||||
|
help="user directory"
|
||||||
|
)
|
||||||
|
):
|
||||||
|
cmd_ctx.set_user_directory(user_directory)
|
||||||
|
asyncio.run(unified_manager.migrate_unmanaged_nodes())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
sys.exit(app())
|
sys.exit(app())
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
9120
github-stats.json
9120
github-stats.json
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ import manager_downloader
|
|||||||
from node_package import InstalledNodePackage
|
from node_package import InstalledNodePackage
|
||||||
|
|
||||||
|
|
||||||
version_code = [3, 31, 13]
|
version_code = [3, 30, 3]
|
||||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||||
|
|
||||||
|
|
||||||
@@ -53,11 +53,6 @@ DEFAULT_CHANNEL = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/ma
|
|||||||
default_custom_nodes_path = None
|
default_custom_nodes_path = None
|
||||||
|
|
||||||
|
|
||||||
class InvalidChannel(Exception):
|
|
||||||
def __init__(self, channel):
|
|
||||||
self.channel = channel
|
|
||||||
super().__init__(channel)
|
|
||||||
|
|
||||||
def get_default_custom_nodes_path():
|
def get_default_custom_nodes_path():
|
||||||
global default_custom_nodes_path
|
global default_custom_nodes_path
|
||||||
if default_custom_nodes_path is None:
|
if default_custom_nodes_path is None:
|
||||||
@@ -256,7 +251,6 @@ comfy_ui_revision = "Unknown"
|
|||||||
comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0)
|
comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0)
|
||||||
|
|
||||||
channel_dict = None
|
channel_dict = None
|
||||||
valid_channels = {'default', 'local'}
|
|
||||||
channel_list = None
|
channel_list = None
|
||||||
|
|
||||||
|
|
||||||
@@ -361,7 +355,7 @@ def normalize_channel(channel):
|
|||||||
if channel_url:
|
if channel_url:
|
||||||
return channel_url
|
return channel_url
|
||||||
|
|
||||||
raise InvalidChannel(channel)
|
raise Exception(f"Invalid channel name '{channel}'")
|
||||||
|
|
||||||
|
|
||||||
class ManagedResult:
|
class ManagedResult:
|
||||||
@@ -768,9 +762,6 @@ class UnifiedManager:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def load_nightly(channel, mode):
|
async def load_nightly(channel, mode):
|
||||||
if channel is None:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
|
|
||||||
channel_url = normalize_channel(channel)
|
channel_url = normalize_channel(channel)
|
||||||
@@ -779,11 +770,6 @@ class UnifiedManager:
|
|||||||
print(f"[bold red]ERROR: Invalid mode is specified `--mode {mode}`[/bold red]", file=sys.stderr)
|
print(f"[bold red]ERROR: Invalid mode is specified `--mode {mode}`[/bold red]", file=sys.stderr)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# validate channel - only the channel set by the user is allowed.
|
|
||||||
if channel_url not in valid_channels:
|
|
||||||
logging.error(f'[ComfyUI-Manager] An invalid channel was used: {channel_url}')
|
|
||||||
raise InvalidChannel(channel_url)
|
|
||||||
|
|
||||||
json_obj = await get_data_by_mode(mode, 'custom-node-list.json', channel_url=channel_url)
|
json_obj = await get_data_by_mode(mode, 'custom-node-list.json', channel_url=channel_url)
|
||||||
for x in json_obj['custom_nodes']:
|
for x in json_obj['custom_nodes']:
|
||||||
try:
|
try:
|
||||||
@@ -801,9 +787,8 @@ class UnifiedManager:
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
async def get_custom_nodes(self, channel, mode):
|
async def get_custom_nodes(self, channel, mode):
|
||||||
if channel is None and mode is None:
|
# default_channel = normalize_channel('default')
|
||||||
channel = 'default'
|
# cache = self.custom_node_map_cache.get((default_channel, mode)) # CNR/nightly should always be based on the default channel.
|
||||||
mode = 'cache'
|
|
||||||
|
|
||||||
channel = normalize_channel(channel)
|
channel = normalize_channel(channel)
|
||||||
cache = self.custom_node_map_cache.get((channel, mode)) # CNR/nightly should always be based on the default channel.
|
cache = self.custom_node_map_cache.get((channel, mode)) # CNR/nightly should always be based on the default channel.
|
||||||
@@ -812,6 +797,7 @@ class UnifiedManager:
|
|||||||
return cache
|
return cache
|
||||||
|
|
||||||
channel = normalize_channel(channel)
|
channel = normalize_channel(channel)
|
||||||
|
print(f"nightly_channel: {channel}/{mode}")
|
||||||
nodes = await self.load_nightly(channel, mode)
|
nodes = await self.load_nightly(channel, mode)
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
@@ -855,7 +841,6 @@ class UnifiedManager:
|
|||||||
install_script_path = os.path.join(repo_path, "install.py")
|
install_script_path = os.path.join(repo_path, "install.py")
|
||||||
requirements_path = os.path.join(repo_path, "requirements.txt")
|
requirements_path = os.path.join(repo_path, "requirements.txt")
|
||||||
|
|
||||||
res = True
|
|
||||||
if lazy_mode:
|
if lazy_mode:
|
||||||
install_cmd = ["#LAZY-INSTALL-SCRIPT", sys.executable]
|
install_cmd = ["#LAZY-INSTALL-SCRIPT", sys.executable]
|
||||||
return try_install_script(url, repo_path, install_cmd)
|
return try_install_script(url, repo_path, install_cmd)
|
||||||
@@ -863,6 +848,7 @@ class UnifiedManager:
|
|||||||
if os.path.exists(requirements_path) and not no_deps:
|
if os.path.exists(requirements_path) and not no_deps:
|
||||||
print("Install: pip packages")
|
print("Install: pip packages")
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
||||||
|
res = True
|
||||||
lines = manager_util.robust_readlines(requirements_path)
|
lines = manager_util.robust_readlines(requirements_path)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
package_name = remap_pip_package(line.strip())
|
package_name = remap_pip_package(line.strip())
|
||||||
@@ -873,14 +859,15 @@ class UnifiedManager:
|
|||||||
res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
|
res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
|
||||||
|
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
return res
|
||||||
|
|
||||||
if os.path.exists(install_script_path) and install_script_path not in self.processed_install:
|
if os.path.exists(install_script_path) and install_script_path not in self.processed_install:
|
||||||
self.processed_install.add(install_script_path)
|
self.processed_install.add(install_script_path)
|
||||||
print("Install: install script")
|
print("Install: install script")
|
||||||
install_cmd = [sys.executable, "install.py"]
|
install_cmd = [sys.executable, "install.py"]
|
||||||
return res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
|
return try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
|
||||||
|
|
||||||
return res
|
return True
|
||||||
|
|
||||||
def reserve_cnr_switch(self, target, zip_url, from_path, to_path, no_deps):
|
def reserve_cnr_switch(self, target, zip_url, from_path, to_path, no_deps):
|
||||||
script_path = os.path.join(manager_startup_script_path, "install-scripts.txt")
|
script_path = os.path.join(manager_startup_script_path, "install-scripts.txt")
|
||||||
@@ -892,6 +879,14 @@ class UnifiedManager:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def reserve_migration(self, moves):
|
||||||
|
script_path = os.path.join(manager_startup_script_path, "install-scripts.txt")
|
||||||
|
with open(script_path, "a") as file:
|
||||||
|
obj = ["", "#LAZY-MIGRATION", moves]
|
||||||
|
file.write(f"{obj}\n")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def unified_fix(self, node_id, version_spec, instant_execution=False, no_deps=False):
|
def unified_fix(self, node_id, version_spec, instant_execution=False, no_deps=False):
|
||||||
"""
|
"""
|
||||||
fix dependencies
|
fix dependencies
|
||||||
@@ -1320,66 +1315,67 @@ class UnifiedManager:
|
|||||||
return result.fail(f'Path not found: {repo_path}')
|
return result.fail(f'Path not found: {repo_path}')
|
||||||
|
|
||||||
# version check
|
# version check
|
||||||
with git.Repo(repo_path) as repo:
|
repo = git.Repo(repo_path)
|
||||||
if repo.head.is_detached:
|
|
||||||
if not switch_to_default_branch(repo):
|
|
||||||
return result.fail(f"Failed to switch to default branch: {repo_path}")
|
|
||||||
|
|
||||||
current_branch = repo.active_branch
|
if repo.head.is_detached:
|
||||||
branch_name = current_branch.name
|
if not switch_to_default_branch(repo):
|
||||||
|
return result.fail(f"Failed to switch to default branch: {repo_path}")
|
||||||
|
|
||||||
if current_branch.tracking_branch() is None:
|
current_branch = repo.active_branch
|
||||||
print(f"[ComfyUI-Manager] There is no tracking branch ({current_branch})")
|
branch_name = current_branch.name
|
||||||
remote_name = get_remote_name(repo)
|
|
||||||
|
if current_branch.tracking_branch() is None:
|
||||||
|
print(f"[ComfyUI-Manager] There is no tracking branch ({current_branch})")
|
||||||
|
remote_name = get_remote_name(repo)
|
||||||
|
else:
|
||||||
|
remote_name = current_branch.tracking_branch().remote_name
|
||||||
|
|
||||||
|
if remote_name is None:
|
||||||
|
return result.fail(f"Failed to get remote when installing: {repo_path}")
|
||||||
|
|
||||||
|
remote = repo.remote(name=remote_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
remote.fetch()
|
||||||
|
except Exception as e:
|
||||||
|
if 'detected dubious' in str(e):
|
||||||
|
print(f"[ComfyUI-Manager] Try fixing 'dubious repository' error on '{repo_path}' repository")
|
||||||
|
safedir_path = repo_path.replace('\\', '/')
|
||||||
|
subprocess.run(['git', 'config', '--global', '--add', 'safe.directory', safedir_path])
|
||||||
|
try:
|
||||||
|
remote.fetch()
|
||||||
|
except Exception:
|
||||||
|
print("\n[ComfyUI-Manager] Failed to fixing repository setup. Please execute this command on cmd: \n"
|
||||||
|
"-----------------------------------------------------------------------------------------\n"
|
||||||
|
f'git config --global --add safe.directory "{safedir_path}"\n'
|
||||||
|
"-----------------------------------------------------------------------------------------\n")
|
||||||
|
|
||||||
|
commit_hash = repo.head.commit.hexsha
|
||||||
|
if f'{remote_name}/{branch_name}' in repo.refs:
|
||||||
|
remote_commit_hash = repo.refs[f'{remote_name}/{branch_name}'].object.hexsha
|
||||||
|
else:
|
||||||
|
return result.fail(f"Not updatable branch: {branch_name}")
|
||||||
|
|
||||||
|
if commit_hash != remote_commit_hash:
|
||||||
|
git_pull(repo_path)
|
||||||
|
|
||||||
|
if len(repo.remotes) > 0:
|
||||||
|
url = repo.remotes[0].url
|
||||||
else:
|
else:
|
||||||
remote_name = current_branch.tracking_branch().remote_name
|
url = "unknown repo"
|
||||||
|
|
||||||
if remote_name is None:
|
def postinstall():
|
||||||
return result.fail(f"Failed to get remote when installing: {repo_path}")
|
return self.execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps)
|
||||||
|
|
||||||
remote = repo.remote(name=remote_name)
|
if return_postinstall:
|
||||||
|
return result.with_postinstall(postinstall)
|
||||||
try:
|
|
||||||
remote.fetch()
|
|
||||||
except Exception as e:
|
|
||||||
if 'detected dubious' in str(e):
|
|
||||||
print(f"[ComfyUI-Manager] Try fixing 'dubious repository' error on '{repo_path}' repository")
|
|
||||||
safedir_path = repo_path.replace('\\', '/')
|
|
||||||
subprocess.run(['git', 'config', '--global', '--add', 'safe.directory', safedir_path])
|
|
||||||
try:
|
|
||||||
remote.fetch()
|
|
||||||
except Exception:
|
|
||||||
print("\n[ComfyUI-Manager] Failed to fixing repository setup. Please execute this command on cmd: \n"
|
|
||||||
"-----------------------------------------------------------------------------------------\n"
|
|
||||||
f'git config --global --add safe.directory "{safedir_path}"\n'
|
|
||||||
"-----------------------------------------------------------------------------------------\n")
|
|
||||||
|
|
||||||
commit_hash = repo.head.commit.hexsha
|
|
||||||
if f'{remote_name}/{branch_name}' in repo.refs:
|
|
||||||
remote_commit_hash = repo.refs[f'{remote_name}/{branch_name}'].object.hexsha
|
|
||||||
else:
|
else:
|
||||||
return result.fail(f"Not updatable branch: {branch_name}")
|
if not postinstall():
|
||||||
|
return result.fail(f"Failed to execute install script: {url}")
|
||||||
|
|
||||||
if commit_hash != remote_commit_hash:
|
return result
|
||||||
git_pull(repo_path)
|
else:
|
||||||
|
return ManagedResult('skip').with_msg('Up to date')
|
||||||
if len(repo.remotes) > 0:
|
|
||||||
url = repo.remotes[0].url
|
|
||||||
else:
|
|
||||||
url = "unknown repo"
|
|
||||||
|
|
||||||
def postinstall():
|
|
||||||
return self.execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps)
|
|
||||||
|
|
||||||
if return_postinstall:
|
|
||||||
return result.with_postinstall(postinstall)
|
|
||||||
else:
|
|
||||||
if not postinstall():
|
|
||||||
return result.fail(f"Failed to execute install script: {url}")
|
|
||||||
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return ManagedResult('skip').with_msg('Up to date')
|
|
||||||
|
|
||||||
def unified_update(self, node_id, version_spec=None, instant_execution=False, no_deps=False, return_postinstall=False):
|
def unified_update(self, node_id, version_spec=None, instant_execution=False, no_deps=False, return_postinstall=False):
|
||||||
orig_print(f"\x1b[2K\rUpdating: {node_id}", end='')
|
orig_print(f"\x1b[2K\rUpdating: {node_id}", end='')
|
||||||
@@ -1419,11 +1415,7 @@ class UnifiedManager:
|
|||||||
version_spec = self.resolve_unspecified_version(node_id)
|
version_spec = self.resolve_unspecified_version(node_id)
|
||||||
|
|
||||||
if version_spec == 'unknown' or version_spec == 'nightly':
|
if version_spec == 'unknown' or version_spec == 'nightly':
|
||||||
try:
|
custom_nodes = await self.get_custom_nodes(channel, mode)
|
||||||
custom_nodes = await self.get_custom_nodes(channel, mode)
|
|
||||||
except InvalidChannel as e:
|
|
||||||
return ManagedResult('fail').fail(f'Invalid channel is used: {e.channel}')
|
|
||||||
|
|
||||||
the_node = custom_nodes.get(node_id)
|
the_node = custom_nodes.get(node_id)
|
||||||
if the_node is not None:
|
if the_node is not None:
|
||||||
if version_spec == 'unknown':
|
if version_spec == 'unknown':
|
||||||
@@ -1481,6 +1473,28 @@ class UnifiedManager:
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
async def migrate_unmanaged_nodes(self):
|
||||||
|
"""
|
||||||
|
fix path for nightly and unknown nodes of unmanaged nodes
|
||||||
|
"""
|
||||||
|
await self.reload('cache')
|
||||||
|
await self.get_custom_nodes('default', 'cache')
|
||||||
|
|
||||||
|
print("Migration: STAGE 1")
|
||||||
|
moves = []
|
||||||
|
|
||||||
|
# migrate nightly inactive
|
||||||
|
for x, v in self.nightly_inactive_nodes.items():
|
||||||
|
if v.endswith('@nightly'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_path = os.path.join(get_default_custom_nodes_path(), '.disabled', f"{x}@nightly")
|
||||||
|
moves.append((v, new_path))
|
||||||
|
|
||||||
|
self.reserve_migration(moves)
|
||||||
|
|
||||||
|
print("DONE (Migration reserved)")
|
||||||
|
|
||||||
|
|
||||||
unified_manager = UnifiedManager()
|
unified_manager = UnifiedManager()
|
||||||
|
|
||||||
@@ -1550,14 +1564,8 @@ def get_installed_node_packs():
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def refresh_channel_dict():
|
|
||||||
if channel_dict is None:
|
|
||||||
get_channel_dict()
|
|
||||||
|
|
||||||
|
|
||||||
def get_channel_dict():
|
def get_channel_dict():
|
||||||
global channel_dict
|
global channel_dict
|
||||||
global valid_channels
|
|
||||||
|
|
||||||
if channel_dict is None:
|
if channel_dict is None:
|
||||||
channel_dict = {}
|
channel_dict = {}
|
||||||
@@ -1571,7 +1579,6 @@ def get_channel_dict():
|
|||||||
channel_info = x.split("::")
|
channel_info = x.split("::")
|
||||||
if len(channel_info) == 2:
|
if len(channel_info) == 2:
|
||||||
channel_dict[channel_info[0]] = channel_info[1]
|
channel_dict[channel_info[0]] = channel_info[1]
|
||||||
valid_channels.add(channel_info[1])
|
|
||||||
|
|
||||||
return channel_dict
|
return channel_dict
|
||||||
|
|
||||||
@@ -1624,6 +1631,7 @@ def write_config():
|
|||||||
'model_download_by_agent': get_config()['model_download_by_agent'],
|
'model_download_by_agent': get_config()['model_download_by_agent'],
|
||||||
'downgrade_blacklist': get_config()['downgrade_blacklist'],
|
'downgrade_blacklist': get_config()['downgrade_blacklist'],
|
||||||
'security_level': get_config()['security_level'],
|
'security_level': get_config()['security_level'],
|
||||||
|
'skip_migration_check': get_config()['skip_migration_check'],
|
||||||
'always_lazy_install': get_config()['always_lazy_install'],
|
'always_lazy_install': get_config()['always_lazy_install'],
|
||||||
'network_mode': get_config()['network_mode'],
|
'network_mode': get_config()['network_mode'],
|
||||||
'db_mode': get_config()['db_mode'],
|
'db_mode': get_config()['db_mode'],
|
||||||
@@ -1662,6 +1670,7 @@ def read_config():
|
|||||||
'windows_selector_event_loop_policy': get_bool('windows_selector_event_loop_policy', False),
|
'windows_selector_event_loop_policy': get_bool('windows_selector_event_loop_policy', False),
|
||||||
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
||||||
'downgrade_blacklist': default_conf.get('downgrade_blacklist', '').lower(),
|
'downgrade_blacklist': default_conf.get('downgrade_blacklist', '').lower(),
|
||||||
|
'skip_migration_check': get_bool('skip_migration_check', False),
|
||||||
'always_lazy_install': get_bool('always_lazy_install', False),
|
'always_lazy_install': get_bool('always_lazy_install', False),
|
||||||
'network_mode': default_conf.get('network_mode', 'public').lower(),
|
'network_mode': default_conf.get('network_mode', 'public').lower(),
|
||||||
'security_level': default_conf.get('security_level', 'normal').lower(),
|
'security_level': default_conf.get('security_level', 'normal').lower(),
|
||||||
@@ -1685,6 +1694,7 @@ def read_config():
|
|||||||
'windows_selector_event_loop_policy': False,
|
'windows_selector_event_loop_policy': False,
|
||||||
'model_download_by_agent': False,
|
'model_download_by_agent': False,
|
||||||
'downgrade_blacklist': '',
|
'downgrade_blacklist': '',
|
||||||
|
'skip_migration_check': False,
|
||||||
'always_lazy_install': False,
|
'always_lazy_install': False,
|
||||||
'network_mode': 'public', # public | private | offline
|
'network_mode': 'public', # public | private | offline
|
||||||
'security_level': 'normal', # strong | normal | normal- | weak
|
'security_level': 'normal', # strong | normal | normal- | weak
|
||||||
@@ -2089,7 +2099,7 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
|
|||||||
cnr = unified_manager.get_cnr_by_repo(url)
|
cnr = unified_manager.get_cnr_by_repo(url)
|
||||||
if cnr:
|
if cnr:
|
||||||
cnr_id = cnr['id']
|
cnr_id = cnr['id']
|
||||||
return await unified_manager.install_by_id(cnr_id, version_spec='nightly', channel='default', mode='cache')
|
return await unified_manager.install_by_id(cnr_id, version_spec='nightly')
|
||||||
else:
|
else:
|
||||||
repo_name = os.path.splitext(os.path.basename(url))[0]
|
repo_name = os.path.splitext(os.path.basename(url))[0]
|
||||||
|
|
||||||
@@ -2639,8 +2649,22 @@ async def get_current_snapshot(custom_nodes_only = False):
|
|||||||
|
|
||||||
cnr_custom_nodes[info['id']] = info['ver']
|
cnr_custom_nodes[info['id']] = info['ver']
|
||||||
else:
|
else:
|
||||||
commit_hash = git_utils.get_commit_hash(fullpath)
|
repo = git.Repo(fullpath)
|
||||||
url = git_utils.git_url(fullpath)
|
|
||||||
|
if repo.head.is_detached:
|
||||||
|
remote_name = get_remote_name(repo)
|
||||||
|
else:
|
||||||
|
current_branch = repo.active_branch
|
||||||
|
|
||||||
|
if current_branch.tracking_branch() is None:
|
||||||
|
remote_name = get_remote_name(repo)
|
||||||
|
else:
|
||||||
|
remote_name = current_branch.tracking_branch().remote_name
|
||||||
|
|
||||||
|
commit_hash = repo.head.commit.hexsha
|
||||||
|
|
||||||
|
url = repo.remotes[remote_name].url
|
||||||
|
|
||||||
git_custom_nodes[url] = dict(hash=commit_hash, disabled=is_disabled)
|
git_custom_nodes[url] = dict(hash=commit_hash, disabled=is_disabled)
|
||||||
except:
|
except:
|
||||||
print(f"Failed to extract snapshots for the custom node '{path}'.")
|
print(f"Failed to extract snapshots for the custom node '{path}'.")
|
||||||
@@ -3003,9 +3027,6 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
enabled_repos = []
|
enabled_repos = []
|
||||||
disabled_repos = []
|
disabled_repos = []
|
||||||
skip_node_packs = []
|
skip_node_packs = []
|
||||||
switched_node_packs = []
|
|
||||||
installed_node_packs = []
|
|
||||||
failed = []
|
|
||||||
|
|
||||||
await unified_manager.reload('cache')
|
await unified_manager.reload('cache')
|
||||||
await unified_manager.get_custom_nodes('default', 'cache')
|
await unified_manager.get_custom_nodes('default', 'cache')
|
||||||
@@ -3051,13 +3072,8 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
disabled_repos.append(x)
|
disabled_repos.append(x)
|
||||||
|
|
||||||
for x in todo_checkout:
|
for x in todo_checkout:
|
||||||
ps = unified_manager.cnr_switch_version(x[0], x[1], instant_execution=True, no_deps=True, return_postinstall=False)
|
unified_manager.cnr_switch_version(x[0], x[1], instant_execution=True, no_deps=True, return_postinstall=False)
|
||||||
if ps.action == 'switch-cnr' and ps.result:
|
checkout_repos.append(x[1])
|
||||||
switched_node_packs.append(f"{x[0]}@{x[1]}")
|
|
||||||
elif ps.action == 'skip':
|
|
||||||
skip_node_packs.append(f"{x[0]}@{x[1]}")
|
|
||||||
elif not ps.result:
|
|
||||||
failed.append(f"{x[0]}@{x[1]}")
|
|
||||||
|
|
||||||
# install listed cnr nodes
|
# install listed cnr nodes
|
||||||
for k, v in cnr_info.items():
|
for k, v in cnr_info.items():
|
||||||
@@ -3065,9 +3081,7 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
ps = await unified_manager.install_by_id(k, version_spec=v, instant_execution=True, return_postinstall=True)
|
ps = await unified_manager.install_by_id(k, version_spec=v, instant_execution=True, return_postinstall=True)
|
||||||
if ps.action == 'install-cnr' and ps.result:
|
cloned_repos.append(k)
|
||||||
installed_node_packs.append(f"{k}@{v}")
|
|
||||||
|
|
||||||
if ps is not None and ps.result:
|
if ps is not None and ps.result:
|
||||||
if hasattr(ps, 'postinstall'):
|
if hasattr(ps, 'postinstall'):
|
||||||
postinstalls.append(ps.postinstall)
|
postinstalls.append(ps.postinstall)
|
||||||
@@ -3125,41 +3139,40 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
disabled_repos.append(x)
|
disabled_repos.append(x)
|
||||||
|
|
||||||
for x in todo_enable:
|
for x in todo_enable:
|
||||||
res = unified_manager.unified_enable(x[0], 'nightly')
|
res = unified_manager.unified_enable(x, 'nightly')
|
||||||
|
|
||||||
is_switched = False
|
is_switched = False
|
||||||
if res and res.target:
|
if res and res.target:
|
||||||
is_switched = repo_switch_commit(res.target, x[1])
|
is_switched = repo_switch_commit(res.target, x[1])
|
||||||
|
|
||||||
if is_switched:
|
if is_switched:
|
||||||
checkout_repos.append(f"{x[0]}@{x[1]}")
|
checkout_repos.append(x)
|
||||||
else:
|
else:
|
||||||
enabled_repos.append(x[0])
|
enabled_repos.append(x)
|
||||||
|
|
||||||
for x in todo_checkout:
|
for x in todo_checkout:
|
||||||
is_switched = repo_switch_commit(x[0], x[1])
|
is_switched = repo_switch_commit(x[0], x[1])
|
||||||
|
|
||||||
if is_switched:
|
if is_switched:
|
||||||
checkout_repos.append(f"{x[0]}@{x[1]}")
|
checkout_repos.append(x)
|
||||||
|
else:
|
||||||
|
skip_node_packs.append(x[0])
|
||||||
|
|
||||||
for x in git_info.keys():
|
for x in git_info.keys():
|
||||||
normalized_url = git_utils.normalize_url(x)
|
normalized_url = git_utils.normalize_url(x)
|
||||||
cnr = unified_manager.repo_cnr_map.get(normalized_url)
|
cnr = unified_manager.repo_cnr_map.get(normalized_url)
|
||||||
if cnr is not None:
|
if cnr is not None:
|
||||||
pack_id = cnr['id']
|
pack_id = cnr['id']
|
||||||
res = await unified_manager.install_by_id(pack_id, 'nightly', instant_execution=True, no_deps=False, return_postinstall=False)
|
await unified_manager.install_by_id(pack_id, 'nightly', instant_execution=True, no_deps=False, return_postinstall=False)
|
||||||
if res.action == 'install-git' and res.result:
|
cloned_repos.append(pack_id)
|
||||||
cloned_repos.append(pack_id)
|
|
||||||
elif res.action == 'skip':
|
|
||||||
skip_node_packs.append(pack_id)
|
|
||||||
elif not res.result:
|
|
||||||
failed.append(pack_id)
|
|
||||||
processed_urls.append(x)
|
processed_urls.append(x)
|
||||||
|
|
||||||
for x in processed_urls:
|
for x in processed_urls:
|
||||||
if x in git_info:
|
if x in git_info:
|
||||||
del git_info[x]
|
del git_info[x]
|
||||||
|
|
||||||
|
# remained nightly will be installed and migrated
|
||||||
|
|
||||||
# for unknown restore
|
# for unknown restore
|
||||||
todo_disable = []
|
todo_disable = []
|
||||||
todo_enable = []
|
todo_enable = []
|
||||||
@@ -3206,15 +3219,15 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
is_switched = repo_switch_commit(res.target, x[1])
|
is_switched = repo_switch_commit(res.target, x[1])
|
||||||
|
|
||||||
if is_switched:
|
if is_switched:
|
||||||
checkout_repos.append(f"{x[0]}@{x[1]}")
|
checkout_repos.append(x)
|
||||||
else:
|
else:
|
||||||
enabled_repos.append(x[0])
|
enabled_repos.append(x)
|
||||||
|
|
||||||
for x in todo_checkout:
|
for x in todo_checkout:
|
||||||
is_switched = repo_switch_commit(x[0], x[1])
|
is_switched = repo_switch_commit(x[0], x[1])
|
||||||
|
|
||||||
if is_switched:
|
if is_switched:
|
||||||
checkout_repos.append(f"{x[0]}@{x[1]}")
|
checkout_repos.append(x)
|
||||||
else:
|
else:
|
||||||
skip_node_packs.append(x[0])
|
skip_node_packs.append(x[0])
|
||||||
|
|
||||||
@@ -3231,28 +3244,53 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
|||||||
unified_manager.repo_install(repo_url, to_path, instant_execution=True, no_deps=False, return_postinstall=False)
|
unified_manager.repo_install(repo_url, to_path, instant_execution=True, no_deps=False, return_postinstall=False)
|
||||||
cloned_repos.append(repo_name)
|
cloned_repos.append(repo_name)
|
||||||
|
|
||||||
|
# reload
|
||||||
|
await unified_manager.migrate_unmanaged_nodes()
|
||||||
|
|
||||||
# print summary
|
# print summary
|
||||||
for x in cloned_repos:
|
for x in cloned_repos:
|
||||||
print(f"[ INSTALLED ] {x}")
|
print(f"[ INSTALLED ] {x}")
|
||||||
for x in installed_node_packs:
|
|
||||||
print(f"[ INSTALLED ] {x}")
|
|
||||||
for x in checkout_repos:
|
for x in checkout_repos:
|
||||||
print(f"[ CHECKOUT ] {x}")
|
print(f"[ CHECKOUT ] {x}")
|
||||||
for x in switched_node_packs:
|
|
||||||
print(f"[ SWITCHED ] {x}")
|
|
||||||
for x in enabled_repos:
|
for x in enabled_repos:
|
||||||
print(f"[ ENABLED ] {x}")
|
print(f"[ ENABLED ] {x}")
|
||||||
for x in disabled_repos:
|
for x in disabled_repos:
|
||||||
print(f"[ DISABLED ] {x}")
|
print(f"[ DISABLED ] {x}")
|
||||||
for x in skip_node_packs:
|
for x in skip_node_packs:
|
||||||
print(f"[ SKIPPED ] {x}")
|
print(f"[ SKIPPED ] {x}")
|
||||||
for x in failed:
|
|
||||||
print(f"[ FAILED ] {x}")
|
|
||||||
|
|
||||||
# if is_failed:
|
# if is_failed:
|
||||||
# print("[bold red]ERROR: Failed to restore snapshot.[/bold red]")
|
# print("[bold red]ERROR: Failed to restore snapshot.[/bold red]")
|
||||||
|
|
||||||
|
|
||||||
|
# check need to migrate
|
||||||
|
need_to_migrate = False
|
||||||
|
|
||||||
|
|
||||||
|
async def check_need_to_migrate():
|
||||||
|
global need_to_migrate
|
||||||
|
|
||||||
|
await unified_manager.reload('cache')
|
||||||
|
await unified_manager.load_nightly(channel='default', mode='cache')
|
||||||
|
|
||||||
|
legacy_custom_nodes = []
|
||||||
|
|
||||||
|
for x in unified_manager.active_nodes.values():
|
||||||
|
if x[0] == 'nightly' and not x[1].endswith('@nightly'):
|
||||||
|
legacy_custom_nodes.append(x[1])
|
||||||
|
|
||||||
|
for x in unified_manager.nightly_inactive_nodes.values():
|
||||||
|
if not x.endswith('@nightly'):
|
||||||
|
legacy_custom_nodes.append(x)
|
||||||
|
|
||||||
|
if len(legacy_custom_nodes) > 0:
|
||||||
|
print("\n--------------------- ComfyUI-Manager migration notice --------------------")
|
||||||
|
print("The following custom nodes were installed using the old management method and require migration:\n")
|
||||||
|
print("\n".join(legacy_custom_nodes))
|
||||||
|
print("---------------------------------------------------------------------------\n")
|
||||||
|
need_to_migrate = True
|
||||||
|
|
||||||
|
|
||||||
def get_comfyui_versions(repo=None):
|
def get_comfyui_versions(repo=None):
|
||||||
if repo is None:
|
if repo is None:
|
||||||
repo = git.Repo(comfy_path)
|
repo = git.Repo(comfy_path)
|
||||||
|
|||||||
@@ -279,17 +279,8 @@ def get_model_dir(data, show_log=False):
|
|||||||
else:
|
else:
|
||||||
models_base = folder_paths.models_dir
|
models_base = folder_paths.models_dir
|
||||||
|
|
||||||
# NOTE: Validate to prevent path traversal.
|
|
||||||
if any(char in data['filename'] for char in {'/', '\\', ':'}):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def resolve_custom_node(save_path):
|
def resolve_custom_node(save_path):
|
||||||
save_path = save_path[13:] # remove 'custom_nodes/'
|
save_path = save_path[13:] # remove 'custom_nodes/'
|
||||||
|
|
||||||
# NOTE: Validate to prevent path traversal.
|
|
||||||
if save_path.startswith(os.path.sep) or ':' in save_path:
|
|
||||||
return None
|
|
||||||
|
|
||||||
repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name
|
repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name
|
||||||
|
|
||||||
# NOTE: The creation of files within the custom node path should be removed in the future.
|
# NOTE: The creation of files within the custom node path should be removed in the future.
|
||||||
@@ -408,6 +399,7 @@ async def task_worker():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
|
node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
|
||||||
|
|
||||||
if node_spec is None:
|
if node_spec is None:
|
||||||
logging.error(f"Cannot resolve install target: '{node_spec_str}'")
|
logging.error(f"Cannot resolve install target: '{node_spec_str}'")
|
||||||
return f"Cannot resolve install target: '{node_spec_str}'"
|
return f"Cannot resolve install target: '{node_spec_str}'"
|
||||||
@@ -937,7 +929,6 @@ def check_model_installed(json_obj):
|
|||||||
|
|
||||||
@routes.get("/externalmodel/getlist")
|
@routes.get("/externalmodel/getlist")
|
||||||
async def fetch_externalmodel_list(request):
|
async def fetch_externalmodel_list(request):
|
||||||
# The model list is only allowed in the default channel, yet.
|
|
||||||
json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json')
|
json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json')
|
||||||
|
|
||||||
check_model_installed(json_obj)
|
check_model_installed(json_obj)
|
||||||
@@ -1206,8 +1197,9 @@ async def install_custom_node(request):
|
|||||||
|
|
||||||
git_url = None
|
git_url = None
|
||||||
|
|
||||||
selected_version = json_data.get('selected_version')
|
if json_data['version'] != 'unknown':
|
||||||
if json_data['version'] != 'unknown' and selected_version != 'unknown':
|
selected_version = json_data.get('selected_version')
|
||||||
|
|
||||||
if skip_post_install:
|
if skip_post_install:
|
||||||
if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes:
|
if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes:
|
||||||
core.unified_manager.unified_enable(cnr_id)
|
core.unified_manager.unified_enable(cnr_id)
|
||||||
@@ -1224,9 +1216,6 @@ async def install_custom_node(request):
|
|||||||
if git_url is None:
|
if git_url is None:
|
||||||
logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}")
|
logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}")
|
||||||
return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}")
|
return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}")
|
||||||
elif json_data['version'] != 'unknown' and selected_version == 'unknown':
|
|
||||||
logging.error(f"[ComfyUI-Manager] Invalid installation request: {json_data}")
|
|
||||||
return web.Response(status=400, text="Invalid installation request")
|
|
||||||
else:
|
else:
|
||||||
# unknown
|
# unknown
|
||||||
unknown_name = os.path.basename(json_data['files'][0])
|
unknown_name = os.path.basename(json_data['files'][0])
|
||||||
@@ -1418,20 +1407,17 @@ async def disable_node(request):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
async def check_whitelist_for_model(item):
|
@routes.get("/manager/migrate_unmanaged_nodes")
|
||||||
json_obj = await core.get_data_by_mode('cache', 'model-list.json')
|
async def migrate_unmanaged_nodes(request):
|
||||||
|
logging.info("[ComfyUI-Manager] Migrating unmanaged nodes...")
|
||||||
|
await core.unified_manager.migrate_unmanaged_nodes()
|
||||||
|
logging.info("Done.")
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
for x in json_obj.get('models', []):
|
|
||||||
if x['save_path'] == item['save_path'] and x['base'] == item['base'] and x['filename'] == item['filename']:
|
|
||||||
return True
|
|
||||||
|
|
||||||
json_obj = await core.get_data_by_mode('local', 'model-list.json')
|
@routes.get("/manager/need_to_migrate")
|
||||||
|
async def need_to_migrate(request):
|
||||||
for x in json_obj.get('models', []):
|
return web.Response(text=str(core.need_to_migrate), status=200)
|
||||||
if x['save_path'] == item['save_path'] and x['base'] == item['base'] and x['filename'] == item['filename']:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/install_model")
|
@routes.post("/manager/queue/install_model")
|
||||||
@@ -1442,11 +1428,6 @@ async def install_model(request):
|
|||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
# validate request
|
|
||||||
if not await check_whitelist_for_model(json_data):
|
|
||||||
logging.error(f"[ComfyUI-Manager] Invalid model install request is detected: {json_data}")
|
|
||||||
return web.Response(status=400, text="Invalid model install request is detected")
|
|
||||||
|
|
||||||
if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'):
|
if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'):
|
||||||
models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default')
|
models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default')
|
||||||
|
|
||||||
@@ -1714,7 +1695,6 @@ cm_global.register_api('cm.try-install-custom-node', confirm_try_install)
|
|||||||
|
|
||||||
|
|
||||||
async def default_cache_update():
|
async def default_cache_update():
|
||||||
core.refresh_channel_dict()
|
|
||||||
channel_url = core.get_config()['channel_url']
|
channel_url = core.get_config()['channel_url']
|
||||||
async def get_cache(filename):
|
async def get_cache(filename):
|
||||||
try:
|
try:
|
||||||
@@ -1754,6 +1734,11 @@ async def default_cache_update():
|
|||||||
|
|
||||||
logging.info("[ComfyUI-Manager] All startup tasks have been completed.")
|
logging.info("[ComfyUI-Manager] All startup tasks have been completed.")
|
||||||
|
|
||||||
|
# NOTE: hide migration button temporarily.
|
||||||
|
# if not core.get_config()['skip_migration_check']:
|
||||||
|
# await core.check_need_to_migrate()
|
||||||
|
# else:
|
||||||
|
# logging.info("[ComfyUI-Manager] Migration check is skipped...")
|
||||||
|
|
||||||
threading.Thread(target=lambda: asyncio.run(default_cache_update())).start()
|
threading.Thread(target=lambda: asyncio.run(default_cache_update())).start()
|
||||||
|
|
||||||
|
|||||||
@@ -35,17 +35,11 @@ def add_python_path_to_env():
|
|||||||
|
|
||||||
|
|
||||||
def make_pip_cmd(cmd):
|
def make_pip_cmd(cmd):
|
||||||
if 'python_embeded' in sys.executable:
|
if use_uv:
|
||||||
if use_uv:
|
return [sys.executable, '-m', 'uv', 'pip'] + cmd
|
||||||
return [sys.executable, '-s', '-m', 'uv', 'pip'] + cmd
|
|
||||||
else:
|
|
||||||
return [sys.executable, '-s', '-m', 'pip'] + cmd
|
|
||||||
else:
|
else:
|
||||||
# FIXED: https://github.com/ltdrdata/ComfyUI-Manager/issues/1667
|
return [sys.executable, '-m', 'pip'] + cmd
|
||||||
if use_uv:
|
|
||||||
return [sys.executable, '-m', 'uv', 'pip'] + cmd
|
|
||||||
else:
|
|
||||||
return [sys.executable, '-m', 'pip'] + cmd
|
|
||||||
|
|
||||||
# DON'T USE StrictVersion - cannot handle pre_release version
|
# DON'T USE StrictVersion - cannot handle pre_release version
|
||||||
# try:
|
# try:
|
||||||
@@ -307,7 +301,6 @@ def parse_requirement_line(line):
|
|||||||
|
|
||||||
|
|
||||||
torch_torchvision_torchaudio_version_map = {
|
torch_torchvision_torchaudio_version_map = {
|
||||||
'2.7.0': ('0.22.0', '2.7.0'),
|
|
||||||
'2.6.0': ('0.21.0', '2.6.0'),
|
'2.6.0': ('0.21.0', '2.6.0'),
|
||||||
'2.5.1': ('0.20.0', '2.5.0'),
|
'2.5.1': ('0.20.0', '2.5.0'),
|
||||||
'2.5.0': ('0.20.0', '2.5.0'),
|
'2.5.0': ('0.20.0', '2.5.0'),
|
||||||
@@ -446,12 +439,10 @@ class PIPFixer:
|
|||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
|
|
||||||
front_line = next((line.strip() for line in lines if line.startswith('comfyui-frontend-package')), None)
|
front_line = next((line.strip() for line in lines if line.startswith('comfyui-frontend-package')), None)
|
||||||
if front_line is None:
|
cmd = make_pip_cmd(['install', front_line])
|
||||||
logging.info("[ComfyUI-Manager] Skipped fixing the 'comfyui-frontend-package' dependency because the ComfyUI is outdated.")
|
subprocess.check_output(cmd , universal_newlines=True)
|
||||||
else:
|
|
||||||
cmd = make_pip_cmd(['install', front_line])
|
logging.info("[ComfyUI-Manager] 'comfyui-frontend-package' dependency were fixed")
|
||||||
subprocess.check_output(cmd , universal_newlines=True)
|
|
||||||
logging.info("[ComfyUI-Manager] 'comfyui-frontend-package' dependency were fixed")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[ComfyUI-Manager] Failed to restore comfyui-frontend-package")
|
logging.error("[ComfyUI-Manager] Failed to restore comfyui-frontend-package")
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
||||||
import {
|
import {
|
||||||
free_models, install_pip, install_via_git_url, manager_instance,
|
free_models, install_pip, install_via_git_url, manager_instance,
|
||||||
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
rebootAPI, migrateAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||||
infoToast, showTerminal, setNeedRestart
|
infoToast, showTerminal, setNeedRestart
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
||||||
@@ -946,6 +946,28 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
restart_stop_button,
|
restart_stop_button,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let migration_btn =
|
||||||
|
$el("button.cm-button-orange", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Migrate to New Node System",
|
||||||
|
onclick: () => migrateAPI()
|
||||||
|
});
|
||||||
|
|
||||||
|
migration_btn.style.display = 'none';
|
||||||
|
|
||||||
|
res.push(migration_btn);
|
||||||
|
|
||||||
|
api.fetchApi('/manager/need_to_migrate')
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(text => {
|
||||||
|
if (text === 'True') {
|
||||||
|
migration_btn.style.display = 'block';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error checking migration status:', error);
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
187
js/common.js
187
js/common.js
@@ -1,7 +1,6 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
import { $el, ComfyDialog } from "../../scripts/ui.js";
|
import { $el, ComfyDialog } from "../../scripts/ui.js";
|
||||||
import { getBestPosition, getPositionStyle, getRect } from './popover-helper.js';
|
|
||||||
|
|
||||||
|
|
||||||
function internalCustomConfirm(message, confirmMessage, cancelMessage) {
|
function internalCustomConfirm(message, confirmMessage, cancelMessage) {
|
||||||
@@ -182,6 +181,23 @@ export function rebootAPI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function migrateAPI() {
|
||||||
|
let confirmed = await customConfirm("When performing a migration, existing installed custom nodes will be renamed and the server will be restarted. Are you sure you want to apply this?\n\n(If you don't perform the migration, ComfyUI-Manager's start-up time will be longer each time due to re-checking during startup.)")
|
||||||
|
if (confirmed) {
|
||||||
|
try {
|
||||||
|
await api.fetchApi("/manager/migrate_unmanaged_nodes");
|
||||||
|
api.fetchApi("/manager/reboot");
|
||||||
|
}
|
||||||
|
catch(exception) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export var manager_instance = null;
|
export var manager_instance = null;
|
||||||
|
|
||||||
export function setManagerInstance(obj) {
|
export function setManagerInstance(obj) {
|
||||||
@@ -388,14 +404,12 @@ export async function fetchData(route, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://cenfun.github.io/open-icons/
|
|
||||||
export const icons = {
|
export const icons = {
|
||||||
search: '<svg viewBox="0 0 24 24" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0"/></svg>',
|
search: '<svg viewBox="0 0 24 24" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0"/></svg>',
|
||||||
|
extensions: '<svg viewBox="64 64 896 896" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M843.5 737.4c-12.4-75.2-79.2-129.1-155.3-125.4S550.9 676 546 752c-153.5-4.8-208-40.7-199.1-113.7 3.3-27.3 19.8-41.9 50.1-49 18.4-4.3 38.8-4.9 57.3-3.2 1.7.2 3.5.3 5.2.5 11.3 2.7 22.8 5 34.3 6.8 34.1 5.6 68.8 8.4 101.8 6.6 92.8-5 156-45.9 159.2-132.7 3.1-84.1-54.7-143.7-147.9-183.6-29.9-12.8-61.6-22.7-93.3-30.2-14.3-3.4-26.3-5.7-35.2-7.2-7.9-75.9-71.5-133.8-147.8-134.4S189.7 168 180.5 243.8s40 146.3 114.2 163.9 149.9-23.3 175.7-95.1c9.4 1.7 18.7 3.6 28 5.8 28.2 6.6 56.4 15.4 82.4 26.6 70.7 30.2 109.3 70.1 107.5 119.9-1.6 44.6-33.6 65.2-96.2 68.6-27.5 1.5-57.6-.9-87.3-5.8-8.3-1.4-15.9-2.8-22.6-4.3-3.9-.8-6.6-1.5-7.8-1.8l-3.1-.6c-2.2-.3-5.9-.8-10.7-1.3-25-2.3-52.1-1.5-78.5 4.6-55.2 12.9-93.9 47.2-101.1 105.8-15.7 126.2 78.6 184.7 276 188.9 29.1 70.4 106.4 107.9 179.6 87 73.3-20.9 119.3-93.4 106.9-168.6M329.1 345.2a83.3 83.3 0 1 1 .01-166.61 83.3 83.3 0 0 1-.01 166.61M695.6 845a83.3 83.3 0 1 1 .01-166.61A83.3 83.3 0 0 1 695.6 845"/></svg>',
|
||||||
conflicts: '<svg viewBox="0 0 400 400" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m397.2 350.4.2-.2-180-320-.2.2C213.8 24.2 207.4 20 200 20s-13.8 4.2-17.2 10.4l-.2-.2-180 320 .2.2c-1.6 2.8-2.8 6-2.8 9.6 0 11 9 20 20 20h360c11 0 20-9 20-20 0-3.6-1.2-6.8-2.8-9.6M220 340h-40v-40h40zm0-60h-40V120h40z"/></svg>',
|
conflicts: '<svg viewBox="0 0 400 400" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m397.2 350.4.2-.2-180-320-.2.2C213.8 24.2 207.4 20 200 20s-13.8 4.2-17.2 10.4l-.2-.2-180 320 .2.2c-1.6 2.8-2.8 6-2.8 9.6 0 11 9 20 20 20h360c11 0 20-9 20-20 0-3.6-1.2-6.8-2.8-9.6M220 340h-40v-40h40zm0-60h-40V120h40z"/></svg>',
|
||||||
passed: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.667 426.667"><path fill="#6AC259" d="M213.333,0C95.518,0,0,95.514,0,213.333s95.518,213.333,213.333,213.333c117.828,0,213.333-95.514,213.333-213.333S331.157,0,213.333,0z M174.199,322.918l-93.935-93.931l31.309-31.309l62.626,62.622l140.894-140.898l31.309,31.309L174.199,322.918z"/></svg>',
|
passed: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.667 426.667"><path fill="#6AC259" d="M213.333,0C95.518,0,0,95.514,0,213.333s95.518,213.333,213.333,213.333c117.828,0,213.333-95.514,213.333-213.333S331.157,0,213.333,0z M174.199,322.918l-93.935-93.931l31.309-31.309l62.626,62.622l140.894-140.898l31.309,31.309L174.199,322.918z"/></svg>',
|
||||||
download: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" width="100%" height="100%" viewBox="0 0 32 32"><path fill="currentColor" d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10l10-10z"></path></svg>',
|
download: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" width="100%" height="100%" viewBox="0 0 32 32"><path fill="currentColor" d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10l10-10z"></path></svg>'
|
||||||
close: '<svg xmlns="http://www.w3.org/2000/svg" pointer-events="none" width="100%" height="100%" viewBox="0 0 16 16"><g fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="m7.116 8-4.558 4.558.884.884L8 8.884l4.558 4.558.884-.884L8.884 8l4.558-4.558-.884-.884L8 7.116 3.442 2.558l-.884.884L7.116 8z"/></g></svg>',
|
|
||||||
arrowRight: '<svg xmlns="http://www.w3.org/2000/svg" pointer-events="none" width="100%" height="100%" viewBox="0 0 20 20"><path fill="currentColor" fill-rule="evenodd" d="m2.542 2.154 7.254 7.26c.136.14.204.302.204.483a.73.73 0 0 1-.204.5l-7.575 7.398c-.383.317-.724.317-1.022 0-.299-.317-.299-.643 0-.98l7.08-6.918-6.754-6.763c-.237-.343-.215-.654.066-.935.281-.28.598-.295.951-.045Zm9 0 7.254 7.26c.136.14.204.302.204.483a.73.73 0 0 1-.204.5l-7.575 7.398c-.383.317-.724.317-1.022 0-.299-.317-.299-.643 0-.98l7.08-6.918-6.754-6.763c-.237-.343-.215-.654.066-.935.281-.28.598-.295.951-.045Z"/></svg>'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeHTML(str) {
|
export function sanitizeHTML(str) {
|
||||||
@@ -489,166 +503,3 @@ export function restoreColumnWidth(gridId, columns) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTimeAgo(dateStr) {
|
|
||||||
const date = new Date(dateStr);
|
|
||||||
|
|
||||||
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const units = [
|
|
||||||
{ max: 2760000, value: 60000, name: 'minute', past: 'a minute ago', future: 'in a minute' },
|
|
||||||
{ max: 72000000, value: 3600000, name: 'hour', past: 'an hour ago', future: 'in an hour' },
|
|
||||||
{ max: 518400000, value: 86400000, name: 'day', past: 'yesterday', future: 'tomorrow' },
|
|
||||||
{ max: 2419200000, value: 604800000, name: 'week', past: 'last week', future: 'in a week' },
|
|
||||||
{ max: 28512000000, value: 2592000000, name: 'month', past: 'last month', future: 'in a month' }
|
|
||||||
];
|
|
||||||
const diff = Date.now() - date.getTime();
|
|
||||||
// less than a minute
|
|
||||||
if (Math.abs(diff) < 60000)
|
|
||||||
return 'just now';
|
|
||||||
for (let i = 0; i < units.length; i++) {
|
|
||||||
if (Math.abs(diff) < units[i].max) {
|
|
||||||
return format(diff, units[i].value, units[i].name, units[i].past, units[i].future, diff < 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function format(diff, divisor, unit, past, future, isInTheFuture) {
|
|
||||||
const val = Math.round(Math.abs(diff) / divisor);
|
|
||||||
if (isInTheFuture)
|
|
||||||
return val <= 1 ? future : 'in ' + val + ' ' + unit + 's';
|
|
||||||
return val <= 1 ? past : val + ' ' + unit + 's ago';
|
|
||||||
}
|
|
||||||
return format(diff, 31536000000, 'year', 'last year', 'in a year', diff < 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const loadCss = (cssFile) => {
|
|
||||||
const cssPath = import.meta.resolve(cssFile);
|
|
||||||
//console.log(cssPath);
|
|
||||||
const $link = document.createElement("link");
|
|
||||||
$link.setAttribute("rel", 'stylesheet');
|
|
||||||
$link.setAttribute("href", cssPath);
|
|
||||||
document.head.appendChild($link);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const copyText = (text) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let err;
|
|
||||||
try {
|
|
||||||
navigator.clipboard.writeText(text);
|
|
||||||
} catch (e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
if (err) {
|
|
||||||
resolve(false);
|
|
||||||
} else {
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderPopover($elem, target, options = {}) {
|
|
||||||
// async microtask
|
|
||||||
queueMicrotask(() => {
|
|
||||||
|
|
||||||
const containerRect = getRect(window);
|
|
||||||
const targetRect = getRect(target);
|
|
||||||
const elemRect = getRect($elem);
|
|
||||||
|
|
||||||
const positionInfo = getBestPosition(
|
|
||||||
containerRect,
|
|
||||||
targetRect,
|
|
||||||
elemRect,
|
|
||||||
options.positions
|
|
||||||
);
|
|
||||||
const style = getPositionStyle(positionInfo, {
|
|
||||||
bgColor: options.bgColor,
|
|
||||||
borderColor: options.borderColor,
|
|
||||||
borderRadius: options.borderRadius
|
|
||||||
});
|
|
||||||
|
|
||||||
$elem.style.top = positionInfo.top + "px";
|
|
||||||
$elem.style.left = positionInfo.left + "px";
|
|
||||||
$elem.style.background = style.background;
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let $popover;
|
|
||||||
export function hidePopover() {
|
|
||||||
if ($popover) {
|
|
||||||
$popover.remove();
|
|
||||||
$popover = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export function showPopover(target, text, className, options) {
|
|
||||||
hidePopover();
|
|
||||||
$popover = document.createElement("div");
|
|
||||||
$popover.className = ['cn-popover', className].filter(it => it).join(" ");
|
|
||||||
document.body.appendChild($popover);
|
|
||||||
$popover.innerHTML = text;
|
|
||||||
$popover.style.display = "block";
|
|
||||||
renderPopover($popover, target, {
|
|
||||||
borderRadius: 10,
|
|
||||||
... options
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let $tooltip;
|
|
||||||
export function hideTooltip(target) {
|
|
||||||
if ($tooltip) {
|
|
||||||
$tooltip.style.display = "none";
|
|
||||||
$tooltip.innerHTML = "";
|
|
||||||
$tooltip.style.top = "0px";
|
|
||||||
$tooltip.style.left = "0px";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export function showTooltip(target, text, className = 'cn-tooltip', styleMap = {}) {
|
|
||||||
if (!$tooltip) {
|
|
||||||
$tooltip = document.createElement("div");
|
|
||||||
$tooltip.className = className;
|
|
||||||
$tooltip.style.cssText = `
|
|
||||||
pointer-events: none;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10001;
|
|
||||||
padding: 20px;
|
|
||||||
color: #1e1e1e;
|
|
||||||
max-width: 350px;
|
|
||||||
filter: drop-shadow(1px 5px 5px rgb(0 0 0 / 30%));
|
|
||||||
${Object.keys(styleMap).map(k=>k+":"+styleMap[k]+";").join("")}
|
|
||||||
`;
|
|
||||||
document.body.appendChild($tooltip);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tooltip.innerHTML = text;
|
|
||||||
$tooltip.style.display = "block";
|
|
||||||
renderPopover($tooltip, target, {
|
|
||||||
positions: ['top', 'bottom', 'right', 'center'],
|
|
||||||
bgColor: "#ffffff",
|
|
||||||
borderColor: "#cccccc",
|
|
||||||
borderRadius: 5
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTooltip () {
|
|
||||||
const mouseenterHandler = (e) => {
|
|
||||||
const target = e.target;
|
|
||||||
const text = target.getAttribute('tooltip');
|
|
||||||
if (text) {
|
|
||||||
showTooltip(target, text);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const mouseleaveHandler = (e) => {
|
|
||||||
const target = e.target;
|
|
||||||
const text = target.getAttribute('tooltip');
|
|
||||||
if (text) {
|
|
||||||
hideTooltip(target);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
document.body.removeEventListener('mouseenter', mouseenterHandler, true);
|
|
||||||
document.body.removeEventListener('mouseleave', mouseleaveHandler, true);
|
|
||||||
document.body.addEventListener('mouseenter', mouseenterHandler, true);
|
|
||||||
document.body.addEventListener('mouseleave', mouseleaveHandler, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
initTooltip();
|
|
||||||
@@ -1,699 +0,0 @@
|
|||||||
.cn-manager {
|
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
|
||||||
z-index: 1099;
|
|
||||||
width: 80%;
|
|
||||||
height: 80%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
color: var(--fg-color);
|
|
||||||
font-family: arial, sans-serif;
|
|
||||||
text-underline-offset: 3px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-flex-auto {
|
|
||||||
flex: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager button {
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--input-text);
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
border-radius: 8px;
|
|
||||||
border-color: var(--border-color);
|
|
||||||
border-style: solid;
|
|
||||||
margin: 0;
|
|
||||||
padding: 4px 8px;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager button:disabled,
|
|
||||||
.cn-manager input:disabled,
|
|
||||||
.cn-manager select:disabled {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager button:disabled {
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-manager-restart {
|
|
||||||
display: none;
|
|
||||||
background-color: #500000;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-manager-stop {
|
|
||||||
display: none;
|
|
||||||
background-color: #500000;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-manager-back {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
height: 1em;
|
|
||||||
width: 1em;
|
|
||||||
margin-right: 5px;
|
|
||||||
transform: translateY(2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-icon {
|
|
||||||
display: block;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-icon svg {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-header {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-header label {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-filter {
|
|
||||||
height: 28px;
|
|
||||||
line-height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-keywords {
|
|
||||||
height: 28px;
|
|
||||||
line-height: 28px;
|
|
||||||
padding: 0 5px 0 26px;
|
|
||||||
background-size: 16px;
|
|
||||||
background-position: 5px center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-status {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid {
|
|
||||||
flex: auto;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-selection {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-message {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-footer {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .tg-turbogrid {
|
|
||||||
font-family: var(--grid-font);
|
|
||||||
font-size: 15px;
|
|
||||||
background: var(--bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .tg-turbogrid .tg-highlight::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: #80bdff11;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .cn-pack-name a {
|
|
||||||
color: skyblue;
|
|
||||||
text-decoration: none;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .cn-pack-desc a {
|
|
||||||
color: #5555FF;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .tg-cell a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .cn-pack-version {
|
|
||||||
line-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .cn-pack-nodes {
|
|
||||||
line-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .cn-pack-nodes:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-grid .cn-pack-conflicts {
|
|
||||||
color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-popover {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 10000;
|
|
||||||
padding: 20px;
|
|
||||||
color: #1e1e1e;
|
|
||||||
filter: drop-shadow(1px 5px 5px rgb(0 0 0 / 30%));
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
display: none;
|
|
||||||
width: 50%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: var(--comfy-menu-bg);
|
|
||||||
animation-duration: 0.2s;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
content: "";
|
|
||||||
z-index: 10;
|
|
||||||
display: block;
|
|
||||||
width: 10px;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
left: -10px;
|
|
||||||
background-image: linear-gradient(to left, rgb(0 0 0 / 20%), rgb(0 0 0 / 0%));
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover-header {
|
|
||||||
height: 45px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover-close {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 10px;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.8;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover-close:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover-close svg {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-weight: bold;
|
|
||||||
gap: 10px;
|
|
||||||
flex: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-flyover-body {
|
|
||||||
height: calc(100% - 45px);
|
|
||||||
overflow-y: auto;
|
|
||||||
position: relative;
|
|
||||||
background-color: var(--comfy-menu-secondary-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cn-slide-in-right {
|
|
||||||
from {
|
|
||||||
visibility: visible;
|
|
||||||
transform: translate3d(100%, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-slide-in-right {
|
|
||||||
animation-name: cn-slide-in-right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cn-slide-out-right {
|
|
||||||
from {
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
visibility: hidden;
|
|
||||||
transform: translate3d(100%, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-slide-out-right {
|
|
||||||
animation-name: cn-slide-out-right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-list {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-row:nth-child(odd) {
|
|
||||||
background-color: rgb(0 0 0 / 5%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-row:hover {
|
|
||||||
background-color: rgb(0 0 0 / 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-sn {
|
|
||||||
text-align: right;
|
|
||||||
min-width: 35px;
|
|
||||||
color: var(--drag-text);
|
|
||||||
flex-shrink: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 8px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-name {
|
|
||||||
cursor: pointer;
|
|
||||||
white-space: nowrap;
|
|
||||||
flex-shrink: 0;
|
|
||||||
position: relative;
|
|
||||||
padding: 8px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-name::after {
|
|
||||||
content: attr(action);
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
top: 50%;
|
|
||||||
left: 100%;
|
|
||||||
transform: translate(5px, -50%);
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--drag-text);
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
padding: 3px 8px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-name.action::after {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-name:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-conflict .cn-nodes-name,
|
|
||||||
.cn-nodes-conflict .cn-icon {
|
|
||||||
color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-conflicts-list {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-conflicts-list b {
|
|
||||||
font-weight: normal;
|
|
||||||
color: var(--descrip-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-pack {
|
|
||||||
cursor: pointer;
|
|
||||||
color: skyblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-nodes-pack:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-pack-badge {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: normal;
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
padding: 3px 8px;
|
|
||||||
color: var(--error-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview {
|
|
||||||
min-width: 300px;
|
|
||||||
max-width: 500px;
|
|
||||||
min-height: 120px;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 12px;
|
|
||||||
pointer-events: none;
|
|
||||||
padding: 12px;
|
|
||||||
color: var(--fg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-header {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: 1px solid var(--comfy-input-bg);
|
|
||||||
padding: 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-dot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: grey;
|
|
||||||
position: relative;
|
|
||||||
filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 30%));
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-dot.cn-preview-optional::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 3px;
|
|
||||||
height: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-dot.cn-preview-grid {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-dot.cn-preview-grid::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
border-left: 1px solid var(--comfy-input-bg);
|
|
||||||
border-right: 1px solid var(--comfy-input-bg);
|
|
||||||
width: 4px;
|
|
||||||
height: 100%;
|
|
||||||
left: 2px;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-dot.cn-preview-grid::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
border-top: 1px solid var(--comfy-input-bg);
|
|
||||||
border-bottom: 1px solid var(--comfy-input-bg);
|
|
||||||
width: 100%;
|
|
||||||
height: 4px;
|
|
||||||
left: 0;
|
|
||||||
top: 2px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-name {
|
|
||||||
flex: auto;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-io {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-column > div {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
height: 18px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-input {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-output {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 3px;
|
|
||||||
padding: 0 10px 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-switch {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
background: var(--bg-color);
|
|
||||||
border: 2px solid var(--border-color);
|
|
||||||
border-radius: 10px;
|
|
||||||
text-wrap: nowrap;
|
|
||||||
padding: 2px 20px;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-switch::before,
|
|
||||||
.cn-preview-switch::after {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(0, -50%);
|
|
||||||
color: var(--fg-color);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-switch::before {
|
|
||||||
content: "◀";
|
|
||||||
left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-switch::after {
|
|
||||||
content: "▶";
|
|
||||||
right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-value {
|
|
||||||
color: var(--descrip-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-string {
|
|
||||||
min-height: 30px;
|
|
||||||
max-height: 300px;
|
|
||||||
background: var(--bg-color);
|
|
||||||
color: var(--descrip-text);
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 3px 5px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-preview-description {
|
|
||||||
margin: 0px 10px 10px 10px;
|
|
||||||
padding: 6px;
|
|
||||||
background: var(--border-color);
|
|
||||||
color: var(--descrip-text);
|
|
||||||
border-radius: 5px;
|
|
||||||
font-style: italic;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-tag-list {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-tag-list > div {
|
|
||||||
background-color: var(--border-color);
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-install-buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 3px;
|
|
||||||
padding: 3px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-selected-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-enable {
|
|
||||||
background-color: #333399;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-disable {
|
|
||||||
background-color: #442277;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-update {
|
|
||||||
background-color: #1155AA;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-try-update {
|
|
||||||
background-color: Gray;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-try-fix {
|
|
||||||
background-color: #6495ED;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-import-failed {
|
|
||||||
background-color: #AA1111;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-install {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-try-install {
|
|
||||||
background-color: Gray;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-uninstall {
|
|
||||||
background-color: #993333;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-reinstall {
|
|
||||||
background-color: #993333;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager .cn-btn-switch {
|
|
||||||
background-color: #448833;
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cn-btn-loading-bg {
|
|
||||||
0% {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
left: -105px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager button.cn-btn-loading {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
border-color: rgb(0 119 207 / 80%);
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager button.cn-btn-loading::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
content: "";
|
|
||||||
width: 500px;
|
|
||||||
height: 100%;
|
|
||||||
background-image: repeating-linear-gradient(
|
|
||||||
-45deg,
|
|
||||||
rgb(0 119 207 / 30%),
|
|
||||||
rgb(0 119 207 / 30%) 10px,
|
|
||||||
transparent 10px,
|
|
||||||
transparent 15px
|
|
||||||
);
|
|
||||||
animation: cn-btn-loading-bg 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-light .cn-pack-name a {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-light .cm-warn-note {
|
|
||||||
background-color: #ccc !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cn-manager-light .cn-btn-install {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,213 +0,0 @@
|
|||||||
.cmm-manager {
|
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
|
||||||
z-index: 1099;
|
|
||||||
width: 80%;
|
|
||||||
height: 80%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
color: var(--fg-color);
|
|
||||||
font-family: arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager .cmm-flex-auto {
|
|
||||||
flex: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager button {
|
|
||||||
font-size: 16px;
|
|
||||||
color: var(--input-text);
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
border-radius: 8px;
|
|
||||||
border-color: var(--border-color);
|
|
||||||
border-style: solid;
|
|
||||||
margin: 0;
|
|
||||||
padding: 4px 8px;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager button:disabled,
|
|
||||||
.cmm-manager input:disabled,
|
|
||||||
.cmm-manager select:disabled {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager button:disabled {
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager .cmm-manager-refresh {
|
|
||||||
display: none;
|
|
||||||
background-color: #000080;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager .cmm-manager-stop {
|
|
||||||
display: none;
|
|
||||||
background-color: #500000;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-header {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-header label {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-type,
|
|
||||||
.cmm-manager-base,
|
|
||||||
.cmm-manager-filter {
|
|
||||||
height: 28px;
|
|
||||||
line-height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-keywords {
|
|
||||||
height: 28px;
|
|
||||||
line-height: 28px;
|
|
||||||
padding: 0 5px 0 26px;
|
|
||||||
background-size: 16px;
|
|
||||||
background-position: 5px center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-status {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-grid {
|
|
||||||
flex: auto;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-selection {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-footer {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-grid .tg-turbogrid {
|
|
||||||
font-family: var(--grid-font);
|
|
||||||
font-size: 15px;
|
|
||||||
background: var(--bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-grid .cmm-node-name a {
|
|
||||||
color: skyblue;
|
|
||||||
text-decoration: none;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-grid .cmm-node-desc a {
|
|
||||||
color: #5555FF;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-grid .tg-cell a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-icon-passed {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
position: absolute;
|
|
||||||
left: calc(50% - 10px);
|
|
||||||
top: calc(50% - 10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager .cmm-btn-enable {
|
|
||||||
background-color: blue;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager .cmm-btn-disable {
|
|
||||||
background-color: MediumSlateBlue;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager .cmm-btn-install {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-btn-download {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
position: absolute;
|
|
||||||
left: calc(50% - 10px);
|
|
||||||
top: calc(50% - 10px);
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.8;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-btn-download:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-light .cmm-btn-download {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cmm-btn-loading-bg {
|
|
||||||
0% {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
left: -105px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager button.cmm-btn-loading {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
border-color: rgb(0 119 207 / 80%);
|
|
||||||
background-color: var(--comfy-input-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager button.cmm-btn-loading::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
content: "";
|
|
||||||
width: 500px;
|
|
||||||
height: 100%;
|
|
||||||
background-image: repeating-linear-gradient(
|
|
||||||
-45deg,
|
|
||||||
rgb(0 119 207 / 30%),
|
|
||||||
rgb(0 119 207 / 30%) 10px,
|
|
||||||
transparent 10px,
|
|
||||||
transparent 15px
|
|
||||||
);
|
|
||||||
animation: cmm-btn-loading-bg 2s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-light .cmm-node-name a {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-light .cm-warn-note {
|
|
||||||
background-color: #ccc !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmm-manager-light .cmm-btn-install {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
@@ -3,17 +3,236 @@ import { $el } from "../../scripts/ui.js";
|
|||||||
import {
|
import {
|
||||||
manager_instance, rebootAPI,
|
manager_instance, rebootAPI,
|
||||||
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
||||||
storeColumnWidth, restoreColumnWidth, loadCss
|
storeColumnWidth, restoreColumnWidth
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
import TG from "./turbogrid.esm.js";
|
import TG from "./turbogrid.esm.js";
|
||||||
|
|
||||||
loadCss("./model-manager.css");
|
|
||||||
|
|
||||||
const gridId = "model";
|
const gridId = "model";
|
||||||
|
|
||||||
|
const pageCss = `
|
||||||
|
.cmm-manager {
|
||||||
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
|
z-index: 1099;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
color: var(--fg-color);
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-flex-auto {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager button {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--input-text);
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
border-radius: 8px;
|
||||||
|
border-color: var(--border-color);
|
||||||
|
border-style: solid;
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 8px;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager button:disabled,
|
||||||
|
.cmm-manager input:disabled,
|
||||||
|
.cmm-manager select:disabled {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager button:disabled {
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-manager-refresh {
|
||||||
|
display: none;
|
||||||
|
background-color: #000080;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-manager-stop {
|
||||||
|
display: none;
|
||||||
|
background-color: #500000;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-header {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-header label {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-type,
|
||||||
|
.cmm-manager-base,
|
||||||
|
.cmm-manager-filter {
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-keywords {
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
padding: 0 5px 0 26px;
|
||||||
|
background-size: 16px;
|
||||||
|
background-position: 5px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-image: url("data:image/svg+xml;charset=utf8,${encodeURIComponent(icons.search.replace("currentColor", "#888"))}");
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-status {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-grid {
|
||||||
|
flex: auto;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-selection {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-message {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-grid .tg-turbogrid {
|
||||||
|
font-family: var(--grid-font);
|
||||||
|
font-size: 15px;
|
||||||
|
background: var(--bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-grid .cmm-node-name a {
|
||||||
|
color: skyblue;
|
||||||
|
text-decoration: none;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-grid .cmm-node-desc a {
|
||||||
|
color: #5555FF;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-grid .tg-cell a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-icon-passed {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
left: calc(50% - 10px);
|
||||||
|
top: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-btn-enable {
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-btn-disable {
|
||||||
|
background-color: MediumSlateBlue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-btn-install {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-btn-download {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
position: absolute;
|
||||||
|
left: calc(50% - 10px);
|
||||||
|
top: calc(50% - 10px);
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.8;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-btn-download:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-light .cmm-btn-download {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cmm-btn-loading-bg {
|
||||||
|
0% {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: -105px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager button.cmm-btn-loading {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-color: rgb(0 119 207 / 80%);
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager button.cmm-btn-loading::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
content: "";
|
||||||
|
width: 500px;
|
||||||
|
height: 100%;
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
rgb(0 119 207 / 30%),
|
||||||
|
rgb(0 119 207 / 30%) 10px,
|
||||||
|
transparent 10px,
|
||||||
|
transparent 15px
|
||||||
|
);
|
||||||
|
animation: cmm-btn-loading-bg 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-light .cmm-node-name a {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-light .cm-warn-note {
|
||||||
|
background-color: #ccc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager-light .cmm-btn-install {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
const pageHtml = `
|
const pageHtml = `
|
||||||
<div class="cmm-manager-header">
|
<div class="cmm-manager-header">
|
||||||
<label>Filter
|
<label>Filter
|
||||||
@@ -64,6 +283,14 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
||||||
|
if (!document.querySelector(`style[context="${this.id}"]`)) {
|
||||||
|
const $style = document.createElement("style");
|
||||||
|
$style.setAttribute("context", this.id);
|
||||||
|
$style.innerHTML = pageCss;
|
||||||
|
document.head.appendChild($style);
|
||||||
|
}
|
||||||
|
|
||||||
this.element = $el("div", {
|
this.element = $el("div", {
|
||||||
parent: document.body,
|
parent: document.body,
|
||||||
className: "comfy-modal cmm-manager"
|
className: "comfy-modal cmm-manager"
|
||||||
@@ -334,7 +561,7 @@ export class ModelManager {
|
|||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
formatter: (url, rowItem, columnItem) => {
|
formatter: (url, rowItem, columnItem) => {
|
||||||
return `<a class="cmm-btn-download" tooltip="Download file" href="${url}" target="_blank">${icons.download}</a>`;
|
return `<a class="cmm-btn-download" title="Download file" href="${url}" target="_blank">${icons.download}</a>`;
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
id: 'size',
|
id: 'size',
|
||||||
|
|||||||
@@ -1,619 +0,0 @@
|
|||||||
const hasOwn = function(obj, key) {
|
|
||||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isNum = function(num) {
|
|
||||||
if (typeof num !== 'number' || isNaN(num)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const isInvalid = function(n) {
|
|
||||||
if (n === Number.MAX_VALUE || n === Number.MIN_VALUE || n === Number.NEGATIVE_INFINITY || n === Number.POSITIVE_INFINITY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if (isInvalid(num)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toNum = (num) => {
|
|
||||||
if (typeof (num) !== 'number') {
|
|
||||||
num = parseFloat(num);
|
|
||||||
}
|
|
||||||
if (isNaN(num)) {
|
|
||||||
num = 0;
|
|
||||||
}
|
|
||||||
num = Math.round(num);
|
|
||||||
return num;
|
|
||||||
};
|
|
||||||
|
|
||||||
const clamp = function(value, min, max) {
|
|
||||||
return Math.max(min, Math.min(max, value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const isWindow = (obj) => {
|
|
||||||
return Boolean(obj && obj === obj.window);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isDocument = (obj) => {
|
|
||||||
return Boolean(obj && obj.nodeType === 9);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isElement = (obj) => {
|
|
||||||
return Boolean(obj && obj.nodeType === 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
export const toRect = (obj) => {
|
|
||||||
if (obj) {
|
|
||||||
return {
|
|
||||||
left: toNum(obj.left || obj.x),
|
|
||||||
top: toNum(obj.top || obj.y),
|
|
||||||
width: toNum(obj.width),
|
|
||||||
height: toNum(obj.height)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getElement = (selector) => {
|
|
||||||
if (typeof selector === 'string' && selector) {
|
|
||||||
if (selector.startsWith('#')) {
|
|
||||||
return document.getElementById(selector.slice(1));
|
|
||||||
}
|
|
||||||
return document.querySelector(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDocument(selector)) {
|
|
||||||
return selector.body;
|
|
||||||
}
|
|
||||||
if (isElement(selector)) {
|
|
||||||
return selector;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getRect = (target, fixed) => {
|
|
||||||
if (!target) {
|
|
||||||
return toRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWindow(target)) {
|
|
||||||
return {
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: window.innerWidth,
|
|
||||||
height: window.innerHeight
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const elem = getElement(target);
|
|
||||||
if (!elem) {
|
|
||||||
return toRect(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
const br = elem.getBoundingClientRect();
|
|
||||||
const rect = toRect(br);
|
|
||||||
|
|
||||||
// fix offset
|
|
||||||
if (!fixed) {
|
|
||||||
rect.left += window.scrollX;
|
|
||||||
rect.top += window.scrollY;
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.width = elem.offsetWidth;
|
|
||||||
rect.height = elem.offsetHeight;
|
|
||||||
|
|
||||||
return rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const calculators = {
|
|
||||||
|
|
||||||
bottom: (info, containerRect, targetRect) => {
|
|
||||||
info.space = containerRect.top + containerRect.height - targetRect.top - targetRect.height - info.height;
|
|
||||||
info.top = targetRect.top + targetRect.height;
|
|
||||||
info.left = Math.round(targetRect.left + targetRect.width * 0.5 - info.width * 0.5);
|
|
||||||
},
|
|
||||||
|
|
||||||
top: (info, containerRect, targetRect) => {
|
|
||||||
info.space = targetRect.top - info.height - containerRect.top;
|
|
||||||
info.top = targetRect.top - info.height;
|
|
||||||
info.left = Math.round(targetRect.left + targetRect.width * 0.5 - info.width * 0.5);
|
|
||||||
},
|
|
||||||
|
|
||||||
right: (info, containerRect, targetRect) => {
|
|
||||||
info.space = containerRect.left + containerRect.width - targetRect.left - targetRect.width - info.width;
|
|
||||||
info.top = Math.round(targetRect.top + targetRect.height * 0.5 - info.height * 0.5);
|
|
||||||
info.left = targetRect.left + targetRect.width;
|
|
||||||
},
|
|
||||||
|
|
||||||
left: (info, containerRect, targetRect) => {
|
|
||||||
info.space = targetRect.left - info.width - containerRect.left;
|
|
||||||
info.top = Math.round(targetRect.top + targetRect.height * 0.5 - info.height * 0.5);
|
|
||||||
info.left = targetRect.left - info.width;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// with order
|
|
||||||
export const getDefaultPositions = () => {
|
|
||||||
return Object.keys(calculators);
|
|
||||||
};
|
|
||||||
|
|
||||||
const calculateSpace = (info, containerRect, targetRect) => {
|
|
||||||
const calculator = calculators[info.position];
|
|
||||||
calculator(info, containerRect, targetRect);
|
|
||||||
if (info.space >= 0) {
|
|
||||||
info.passed += 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const calculateAlignOffset = (info, containerRect, targetRect, alignType, sizeType) => {
|
|
||||||
|
|
||||||
const popoverStart = info[alignType];
|
|
||||||
const popoverSize = info[sizeType];
|
|
||||||
|
|
||||||
const containerStart = containerRect[alignType];
|
|
||||||
const containerSize = containerRect[sizeType];
|
|
||||||
|
|
||||||
const targetStart = targetRect[alignType];
|
|
||||||
const targetSize = targetRect[sizeType];
|
|
||||||
|
|
||||||
const targetCenter = targetStart + targetSize * 0.5;
|
|
||||||
|
|
||||||
// size overflow
|
|
||||||
if (popoverSize > containerSize) {
|
|
||||||
const overflow = (popoverSize - containerSize) * 0.5;
|
|
||||||
info[alignType] = containerStart - overflow;
|
|
||||||
info.offset = targetCenter - containerStart + overflow;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const space1 = popoverStart - containerStart;
|
|
||||||
const space2 = (containerStart + containerSize) - (popoverStart + popoverSize);
|
|
||||||
|
|
||||||
// both side passed, default to center
|
|
||||||
if (space1 >= 0 && space2 >= 0) {
|
|
||||||
if (info.passed) {
|
|
||||||
info.passed += 2;
|
|
||||||
}
|
|
||||||
info.offset = popoverSize * 0.5;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// one side passed
|
|
||||||
if (info.passed) {
|
|
||||||
info.passed += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (space1 < 0) {
|
|
||||||
const min = containerStart;
|
|
||||||
info[alignType] = min;
|
|
||||||
info.offset = targetCenter - min;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// space2 < 0
|
|
||||||
const max = containerStart + containerSize - popoverSize;
|
|
||||||
info[alignType] = max;
|
|
||||||
info.offset = targetCenter - max;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const calculateHV = (info, containerRect) => {
|
|
||||||
if (['top', 'bottom'].includes(info.position)) {
|
|
||||||
info.top = clamp(info.top, containerRect.top, containerRect.top + containerRect.height - info.height);
|
|
||||||
return ['left', 'width'];
|
|
||||||
}
|
|
||||||
info.left = clamp(info.left, containerRect.left, containerRect.left + containerRect.width - info.width);
|
|
||||||
return ['top', 'height'];
|
|
||||||
};
|
|
||||||
|
|
||||||
const calculateOffset = (info, containerRect, targetRect) => {
|
|
||||||
|
|
||||||
const [alignType, sizeType] = calculateHV(info, containerRect);
|
|
||||||
|
|
||||||
calculateAlignOffset(info, containerRect, targetRect, alignType, sizeType);
|
|
||||||
|
|
||||||
info.offset = clamp(info.offset, 0, info[sizeType]);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const calculateDistance = (info, previousPositionInfo) => {
|
|
||||||
if (!previousPositionInfo) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// no change if position no change with previous
|
|
||||||
if (info.position === previousPositionInfo.position) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ax = info.left + info.width * 0.5;
|
|
||||||
const ay = info.top + info.height * 0.5;
|
|
||||||
const bx = previousPositionInfo.left + previousPositionInfo.width * 0.5;
|
|
||||||
const by = previousPositionInfo.top + previousPositionInfo.height * 0.5;
|
|
||||||
const dx = Math.abs(ax - bx);
|
|
||||||
const dy = Math.abs(ay - by);
|
|
||||||
info.distance = Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const calculatePositionInfo = (info, containerRect, targetRect, previousPositionInfo) => {
|
|
||||||
calculateSpace(info, containerRect, targetRect);
|
|
||||||
calculateOffset(info, containerRect, targetRect);
|
|
||||||
calculateDistance(info, previousPositionInfo);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const calculateBestPosition = (containerRect, targetRect, infoMap, withOrder, previousPositionInfo) => {
|
|
||||||
|
|
||||||
// position space: +1
|
|
||||||
// align space:
|
|
||||||
// two side passed: +2
|
|
||||||
// one side passed: +1
|
|
||||||
|
|
||||||
const safePassed = 3;
|
|
||||||
|
|
||||||
if (previousPositionInfo) {
|
|
||||||
const prevInfo = infoMap[previousPositionInfo.position];
|
|
||||||
if (prevInfo) {
|
|
||||||
calculatePositionInfo(prevInfo, containerRect, targetRect);
|
|
||||||
if (prevInfo.passed >= safePassed) {
|
|
||||||
return prevInfo;
|
|
||||||
}
|
|
||||||
prevInfo.calculated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const positionList = [];
|
|
||||||
Object.values(infoMap).forEach((info) => {
|
|
||||||
if (!info.calculated) {
|
|
||||||
calculatePositionInfo(info, containerRect, targetRect, previousPositionInfo);
|
|
||||||
}
|
|
||||||
positionList.push(info);
|
|
||||||
});
|
|
||||||
|
|
||||||
positionList.sort((a, b) => {
|
|
||||||
if (a.passed !== b.passed) {
|
|
||||||
return b.passed - a.passed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (withOrder && a.passed >= safePassed && b.passed >= safePassed) {
|
|
||||||
return a.index - b.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.space !== b.space) {
|
|
||||||
return b.space - a.space;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.index - b.index;
|
|
||||||
});
|
|
||||||
|
|
||||||
// logTable(positionList);
|
|
||||||
|
|
||||||
return positionList[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
// const logTable = (() => {
|
|
||||||
// let time_id;
|
|
||||||
// return (info) => {
|
|
||||||
// clearTimeout(time_id);
|
|
||||||
// time_id = setTimeout(() => {
|
|
||||||
// console.table(info);
|
|
||||||
// }, 10);
|
|
||||||
// };
|
|
||||||
// })();
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const getAllowPositions = (positions, defaultAllowPositions) => {
|
|
||||||
if (!positions) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Array.isArray(positions)) {
|
|
||||||
positions = positions.join(',');
|
|
||||||
}
|
|
||||||
positions = String(positions).split(',').map((it) => it.trim().toLowerCase()).filter((it) => it);
|
|
||||||
positions = positions.filter((it) => defaultAllowPositions.includes(it));
|
|
||||||
if (!positions.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return positions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isPositionChanged = (info, previousPositionInfo) => {
|
|
||||||
if (!previousPositionInfo) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.left !== previousPositionInfo.left) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.top !== previousPositionInfo.top) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
// const log = (name, time) => {
|
|
||||||
// if (time > 0.1) {
|
|
||||||
// console.log(name, time);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
export const getBestPosition = (containerRect, targetRect, popoverRect, positions, previousPositionInfo) => {
|
|
||||||
|
|
||||||
const defaultAllowPositions = getDefaultPositions();
|
|
||||||
let withOrder = true;
|
|
||||||
let allowPositions = getAllowPositions(positions, defaultAllowPositions);
|
|
||||||
if (!allowPositions) {
|
|
||||||
allowPositions = defaultAllowPositions;
|
|
||||||
withOrder = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('withOrder', withOrder);
|
|
||||||
|
|
||||||
// const start_time = performance.now();
|
|
||||||
|
|
||||||
const infoMap = {};
|
|
||||||
allowPositions.forEach((k, i) => {
|
|
||||||
infoMap[k] = {
|
|
||||||
position: k,
|
|
||||||
index: i,
|
|
||||||
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: popoverRect.width,
|
|
||||||
height: popoverRect.height,
|
|
||||||
|
|
||||||
space: 0,
|
|
||||||
|
|
||||||
offset: 0,
|
|
||||||
passed: 0,
|
|
||||||
|
|
||||||
distance: 0
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// log('infoMap', performance.now() - start_time);
|
|
||||||
|
|
||||||
|
|
||||||
const bestPosition = calculateBestPosition(containerRect, targetRect, infoMap, withOrder, previousPositionInfo);
|
|
||||||
|
|
||||||
// check left/top
|
|
||||||
bestPosition.changed = isPositionChanged(bestPosition, previousPositionInfo);
|
|
||||||
|
|
||||||
return bestPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
const getTemplatePath = (width, height, arrowOffset, arrowSize, borderRadius) => {
|
|
||||||
const p = (px, py) => {
|
|
||||||
return [px, py].join(',');
|
|
||||||
};
|
|
||||||
|
|
||||||
const px = function(num, alignEnd) {
|
|
||||||
const floor = Math.floor(num);
|
|
||||||
let n = num < floor + 0.5 ? floor + 0.5 : floor + 1.5;
|
|
||||||
if (alignEnd) {
|
|
||||||
n -= 1;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
};
|
|
||||||
|
|
||||||
const pxe = function(num) {
|
|
||||||
return px(num, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ls = [];
|
|
||||||
|
|
||||||
const innerLeft = px(arrowSize);
|
|
||||||
const innerRight = pxe(width - arrowSize);
|
|
||||||
arrowOffset = clamp(arrowOffset, innerLeft, innerRight);
|
|
||||||
|
|
||||||
const innerTop = px(arrowSize);
|
|
||||||
const innerBottom = pxe(height - arrowSize);
|
|
||||||
|
|
||||||
const startPoint = p(innerLeft, innerTop + borderRadius);
|
|
||||||
const arrowPoint = p(arrowOffset, 1);
|
|
||||||
|
|
||||||
const LT = p(innerLeft, innerTop);
|
|
||||||
const RT = p(innerRight, innerTop);
|
|
||||||
|
|
||||||
const AOT = p(arrowOffset - arrowSize, innerTop);
|
|
||||||
const RRT = p(innerRight - borderRadius, innerTop);
|
|
||||||
|
|
||||||
ls.push(`M${startPoint}`);
|
|
||||||
ls.push(`V${innerBottom - borderRadius}`);
|
|
||||||
ls.push(`Q${p(innerLeft, innerBottom)} ${p(innerLeft + borderRadius, innerBottom)}`);
|
|
||||||
ls.push(`H${innerRight - borderRadius}`);
|
|
||||||
ls.push(`Q${p(innerRight, innerBottom)} ${p(innerRight, innerBottom - borderRadius)}`);
|
|
||||||
ls.push(`V${innerTop + borderRadius}`);
|
|
||||||
|
|
||||||
if (arrowOffset < innerLeft + arrowSize + borderRadius) {
|
|
||||||
ls.push(`Q${RT} ${RRT}`);
|
|
||||||
ls.push(`H${arrowOffset + arrowSize}`);
|
|
||||||
ls.push(`L${arrowPoint}`);
|
|
||||||
if (arrowOffset < innerLeft + arrowSize) {
|
|
||||||
ls.push(`L${LT}`);
|
|
||||||
ls.push(`L${startPoint}`);
|
|
||||||
} else {
|
|
||||||
ls.push(`L${AOT}`);
|
|
||||||
ls.push(`Q${LT} ${startPoint}`);
|
|
||||||
}
|
|
||||||
} else if (arrowOffset > innerRight - arrowSize - borderRadius) {
|
|
||||||
if (arrowOffset > innerRight - arrowSize) {
|
|
||||||
ls.push(`L${RT}`);
|
|
||||||
} else {
|
|
||||||
ls.push(`Q${RT} ${p(arrowOffset + arrowSize, innerTop)}`);
|
|
||||||
}
|
|
||||||
ls.push(`L${arrowPoint}`);
|
|
||||||
ls.push(`L${AOT}`);
|
|
||||||
ls.push(`H${innerLeft + borderRadius}`);
|
|
||||||
ls.push(`Q${LT} ${startPoint}`);
|
|
||||||
} else {
|
|
||||||
ls.push(`Q${RT} ${RRT}`);
|
|
||||||
ls.push(`H${arrowOffset + arrowSize}`);
|
|
||||||
ls.push(`L${arrowPoint}`);
|
|
||||||
ls.push(`L${AOT}`);
|
|
||||||
ls.push(`H${innerLeft + borderRadius}`);
|
|
||||||
ls.push(`Q${LT} ${startPoint}`);
|
|
||||||
}
|
|
||||||
return ls.join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPathData = function(position, width, height, arrowOffset, arrowSize, borderRadius) {
|
|
||||||
|
|
||||||
const handlers = {
|
|
||||||
|
|
||||||
bottom: () => {
|
|
||||||
const d = getTemplatePath(width, height, arrowOffset, arrowSize, borderRadius);
|
|
||||||
return {
|
|
||||||
d,
|
|
||||||
transform: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
top: () => {
|
|
||||||
const d = getTemplatePath(width, height, width - arrowOffset, arrowSize, borderRadius);
|
|
||||||
return {
|
|
||||||
d,
|
|
||||||
transform: `rotate(180,${width * 0.5},${height * 0.5})`
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
left: () => {
|
|
||||||
const d = getTemplatePath(height, width, arrowOffset, arrowSize, borderRadius);
|
|
||||||
const x = (width - height) * 0.5;
|
|
||||||
const y = (height - width) * 0.5;
|
|
||||||
return {
|
|
||||||
d,
|
|
||||||
transform: `translate(${x} ${y}) rotate(90,${height * 0.5},${width * 0.5})`
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
right: () => {
|
|
||||||
const d = getTemplatePath(height, width, height - arrowOffset, arrowSize, borderRadius);
|
|
||||||
const x = (width - height) * 0.5;
|
|
||||||
const y = (height - width) * 0.5;
|
|
||||||
return {
|
|
||||||
d,
|
|
||||||
transform: `translate(${x} ${y}) rotate(-90,${height * 0.5},${width * 0.5})`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return handlers[position]();
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================================
|
|
||||||
|
|
||||||
// position style cache
|
|
||||||
const styleCache = {
|
|
||||||
// position: '',
|
|
||||||
// top: {},
|
|
||||||
// bottom: {},
|
|
||||||
// left: {},
|
|
||||||
// right: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getPositionStyle = (info, options = {}) => {
|
|
||||||
|
|
||||||
const o = {
|
|
||||||
bgColor: '#fff',
|
|
||||||
borderColor: '#ccc',
|
|
||||||
borderRadius: 5,
|
|
||||||
arrowSize: 10
|
|
||||||
};
|
|
||||||
Object.keys(o).forEach((k) => {
|
|
||||||
|
|
||||||
if (hasOwn(options, k)) {
|
|
||||||
const d = o[k];
|
|
||||||
const v = options[k];
|
|
||||||
|
|
||||||
if (typeof d === 'string') {
|
|
||||||
// string
|
|
||||||
if (typeof v === 'string' && v) {
|
|
||||||
o[k] = v;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// number
|
|
||||||
if (isNum(v) && v >= 0) {
|
|
||||||
o[k] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const key = [
|
|
||||||
info.width,
|
|
||||||
info.height,
|
|
||||||
info.offset,
|
|
||||||
o.arrowSize,
|
|
||||||
o.borderRadius,
|
|
||||||
o.bgColor,
|
|
||||||
o.borderColor
|
|
||||||
].join('-');
|
|
||||||
|
|
||||||
const positionCache = styleCache[info.position];
|
|
||||||
if (positionCache && key === positionCache.key) {
|
|
||||||
const st = positionCache.style;
|
|
||||||
st.changed = styleCache.position !== info.position;
|
|
||||||
styleCache.position = info.position;
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(options);
|
|
||||||
|
|
||||||
const data = getPathData(info.position, info.width, info.height, info.offset, o.arrowSize, o.borderRadius);
|
|
||||||
// console.log(data);
|
|
||||||
|
|
||||||
const viewBox = [0, 0, info.width, info.height].join(' ');
|
|
||||||
const svg = [
|
|
||||||
`<svg viewBox="${viewBox}" xmlns="http://www.w3.org/2000/svg">`,
|
|
||||||
`<path d="${data.d}" fill="${o.bgColor}" stroke="${o.borderColor}" transform="${data.transform}" />`,
|
|
||||||
'</svg>'
|
|
||||||
].join('');
|
|
||||||
|
|
||||||
// console.log(svg);
|
|
||||||
const backgroundImage = `url("data:image/svg+xml;charset=utf8,${encodeURIComponent(svg)}")`;
|
|
||||||
|
|
||||||
const background = `${backgroundImage} center no-repeat`;
|
|
||||||
|
|
||||||
const padding = `${o.arrowSize + o.borderRadius}px`;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
background,
|
|
||||||
backgroundImage,
|
|
||||||
padding,
|
|
||||||
changed: true
|
|
||||||
};
|
|
||||||
|
|
||||||
styleCache.position = info.position;
|
|
||||||
styleCache[info.position] = {
|
|
||||||
key,
|
|
||||||
style
|
|
||||||
};
|
|
||||||
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
@@ -70,8 +70,8 @@ class WorkflowMetadataExtension {
|
|||||||
if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name
|
if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name
|
||||||
if (cnr_id) nodeProperties.cnr_id = cnr_id;
|
if (cnr_id) nodeProperties.cnr_id = cnr_id;
|
||||||
else nodeProperties.aux_id = aux_id;
|
else nodeProperties.aux_id = aux_id;
|
||||||
if (ver) nodeProperties.ver = ver.trim();
|
if (ver) nodeProperties.ver = ver;
|
||||||
} else if (["nodes", "comfy_extras", "comfy_api_nodes"].includes(moduleType)) {
|
} else if (["nodes", "comfy_extras"].includes(moduleType)) {
|
||||||
nodeProperties.cnr_id = "comfy-core";
|
nodeProperties.cnr_id = "comfy-core";
|
||||||
nodeProperties.ver = this.comfyCoreVersion;
|
nodeProperties.ver = this.comfyCoreVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
215
model-list.json
215
model-list.json
@@ -4750,221 +4750,6 @@
|
|||||||
"filename": "diffusion_pytorch_model.safetensors",
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors",
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors",
|
||||||
"size": "335MB"
|
"size": "335MB"
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_bf16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_e4m3fn)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_e4m3fn)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_scaled)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_scaled)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_bf16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_e4m3fn)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_e4m3fn)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_scaled)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_scaled)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 1.3B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_1.3B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_bf16.safetensors",
|
|
||||||
"size": "2.84GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 1.3B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_1.3B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_fp16.safetensors",
|
|
||||||
"size": "2.84GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_bf16.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp16.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_e4m3fn)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_e4m3fn)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"size": "14.3GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_scaled)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_scaled)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_fp8_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_scaled.safetensors",
|
|
||||||
"size": "14.3GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 VAE",
|
|
||||||
"type": "vae",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "vae",
|
|
||||||
"description": "Wan2.1 VAE model",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan_2.1_vae.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors",
|
|
||||||
"size": "254MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/clip_vision_h.safetensors",
|
|
||||||
"type": "clip_vision",
|
|
||||||
"base": "clip_vision_h",
|
|
||||||
"save_path": "clip_vision",
|
|
||||||
"description": "clip_vision_h model for Wan2.1",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "clip_vision_h.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/clip_vision/clip_vision_h.safetensors",
|
|
||||||
"size": "1.26GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/umt5_xxl_fp16.safetensors",
|
|
||||||
"type": "clip",
|
|
||||||
"base": "umt5_xxl",
|
|
||||||
"save_path": "text_encoders",
|
|
||||||
"description": "umt5_xxl_fp16 text encoder for Wan2.1",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "umt5_xxl_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp16.safetensors",
|
|
||||||
"size": "11.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
|
||||||
"type": "clip",
|
|
||||||
"base": "umt5_xxl",
|
|
||||||
"save_path": "text_encoders",
|
|
||||||
"description": "umt5_xxl_fp8_e4m3fn_scaled text encoder for Wan2.1",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
|
||||||
"size": "6.74GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"type": "FramePackI2V",
|
|
||||||
"base": "FramePackI2V",
|
|
||||||
"save_path": "diffusers/lllyasviel",
|
|
||||||
"description": "[SNAPSHOT] This is the f1k1_x_g9_f1k1f2k2f16k4_td FramePack for HY. [w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
|
||||||
"reference": "https://huggingface.co/lllyasviel/FramePackI2V_HY",
|
|
||||||
"filename": "<huggingface>",
|
|
||||||
"url": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"size": "25.75GB"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,5 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"custom_nodes": [
|
||||||
{
|
|
||||||
"author": "SanDiegoDude",
|
|
||||||
"title": "ComfyUI-HiDream-Sampler [WIP]",
|
|
||||||
"reference": "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A collection of enhanced nodes for ComfyUI that provide powerful additional functionality to your workflows.\nNOTE: The files in the repo are not organized."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "PramaLLC",
|
"author": "PramaLLC",
|
||||||
"title": "ComfyUI BEN - Background Erase Network",
|
"title": "ComfyUI BEN - Background Erase Network",
|
||||||
|
|||||||
@@ -8,455 +8,9 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "If you see this message, your ComfyUI-Manager is outdated.\nLegacy channel provides only the list of the deprecated nodes. If you want to find the complete node list, please go to the Default channel."
|
"description": "If you see this message, your ComfyUI-Manager is outdated.\nLegacy channel provides only the list of the deprecated nodes. If you want to find the complete node list, please go to the Default channel."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-event-handler [USAFE/REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-event-handler",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-event-handler"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Javascript code will run when an event fires. [w/This node allows you to execute arbitrary JavaScript code as input for the workflow.]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Moooonet",
|
|
||||||
"title": "ComfyUI-ArteMoon [REMOVED]",
|
|
||||||
"reference": "https://github.com/Moooonet/ComfyUI-ArteMoon",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Moooonet/ComfyUI-ArteMoon"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This plugin works with [a/IF_AI_Tools](https://github.com/if-ai/ComfyUI-IF_AI_tools) to build a workflow in ComfyUI that uses AI to assist in generating prompts."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "ryanontheinside",
|
|
||||||
"title": "ComfyUI-MediaPipe-Vision [REMOVED]",
|
|
||||||
"reference": "https://github.com/ryanontheinside/ComfyUI-MediaPipe-Vision",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/ryanontheinside/ComfyUI-MediaPipe-Vision"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A centralized wrapper of all MediaPipe vision tasks for ComfyUI."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-textarea-command [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-textarea-command",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-textarea-command"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Add command and comment in textarea. (e.g. // Disabled line)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-parse-image [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-parse-image",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-parse-image"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Extract metadata from image."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-put-image [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-put-image",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-put-image"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Load image from directory."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "fredconex",
|
|
||||||
"title": "TripoSG Nodes for ComfyUI [REMOVED]",
|
|
||||||
"reference": "https://github.com/fredconex/ComfyUI-TripoSG",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/fredconex/ComfyUI-TripoSG"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Created by Alfredo Fernandes inspired by Hunyuan3D nodes by Kijai. This extension adds TripoSG 3D mesh generation capabilities to ComfyUI, allowing you to generate 3D meshes from a single image using the TripoSG model."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "fredconex",
|
|
||||||
"title": "ComfyUI-PaintTurbo [REMOVED]",
|
|
||||||
"reference": "https://github.com/fredconex/ComfyUI-PaintTurbo",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/fredconex/ComfyUI-PaintTurbo"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Hunyuan3D Texture Mesh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "zhuanqianfish",
|
|
||||||
"title": "TaesdDecoder [REMOVED]",
|
|
||||||
"reference": "https://github.com/zhuanqianfish/TaesdDecoder",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/zhuanqianfish/TaesdDecoder"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "use TAESD decoded image.you need donwload taesd_decoder.pth and taesdxl_decoder.pth to vae_approx folder first.\n It will result in a slight loss of image quality and a significant decrease in peak video memory during decoding."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "myAiLemon",
|
|
||||||
"title": "MagicAutomaticPicture [REMOVED]",
|
|
||||||
"reference": "https://github.com/myAiLemon/MagicAutomaticPicture",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/myAiLemon/MagicAutomaticPicture"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A comfyui node package that can generate pictures and automatically save positive prompts and eliminate unwanted prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "thisiseddy-ab",
|
|
||||||
"title": "ComfyUI-Edins-Ultimate-Pack [REMOVED]",
|
|
||||||
"reference": "https://github.com/thisiseddy-ab/ComfyUI-Edins-Ultimate-Pack",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/thisiseddy-ab/ComfyUI-Edins-Ultimate-Pack"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Well i needet a Tiled Ksampler that still works for Comfy UI there were none so i made one, in this Package i will put all Nodes i will develop for Comfy Ui still in beta alot will change.."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Davros666",
|
|
||||||
"title": "safetriggers [REMOVED]",
|
|
||||||
"reference": "https://github.com/Davros666/safetriggers",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Davros666/safetriggers"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "ComfyUI Nodes for READING TRIGGERS, TRIGGER-WORDS, TRIGGER-PHRASES FROM LoRAs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "cubiq",
|
|
||||||
"title": "Simple Math [REMOVED]",
|
|
||||||
"id": "simplemath",
|
|
||||||
"reference": "https://github.com/cubiq/ComfyUI_SimpleMath",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/cubiq/ComfyUI_SimpleMath"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "custom node for ComfyUI to perform simple math operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "lucafoscili",
|
|
||||||
"title": "LF Nodes [DEPRECATED]",
|
|
||||||
"reference": "https://github.com/lucafoscili/comfyui-lf",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/lucafoscili/comfyui-lf"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Custom nodes with a touch of extra UX, including: history for primitives, JSON manipulation, logic switches with visual feedback, LLM chat... and more!"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "AI2lab",
|
|
||||||
"title": "comfyUI-tool-2lab [REMOVED]",
|
|
||||||
"id": "tool-2lab",
|
|
||||||
"reference": "https://github.com/AI2lab/comfyUI-tool-2lab",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/AI2lab/comfyUI-tool-2lab"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "tool set for developing workflow and publish to web api server"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "AI2lab",
|
|
||||||
"title": "comfyUI-DeepSeek-2lab [REMOVED]",
|
|
||||||
"id": "deepseek",
|
|
||||||
"reference": "https://github.com/AI2lab/comfyUI-DeepSeek-2lab",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/AI2lab/comfyUI-DeepSeek-2lab"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Unofficial implementation of DeepSeek for ComfyUI"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "AI2lab",
|
|
||||||
"title": "comfyUI-kling-api-2lab [REMOVED]",
|
|
||||||
"reference": "https://github.com/AI2lab/comfyUI-kling-api-2lab",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/AI2lab/comfyUI-kling-api-2lab"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Unofficial implementation of KLing for ComfyUI"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "ZhiHui6",
|
|
||||||
"title": "comfyui_zhihui_nodes [REMOVED]",
|
|
||||||
"reference": "https://github.com/ZhiHui6/comfyui_zhihui_nodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/ZhiHui6/comfyui_zhihui_nodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Prompt Preset, Video Batch Loader, Video Combiner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "ImagineerNL",
|
|
||||||
"title": "comfyui_potrace_svg [REMOVED]",
|
|
||||||
"reference": "https://github.com/ImagineerNL/comfyui_potrace_svg",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/ImagineerNL/comfyui_potrace_svg"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This project converts raster images into SVG format using the Potrace library."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "kayselmecnun",
|
|
||||||
"title": "ComfyUI-Qwen-25-VL [REMOVED]",
|
|
||||||
"reference": "https://github.com/kayselmecnun/ComfyUI-Qwen-25-VL",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/kayselmecnun/ComfyUI-Qwen-25-VL"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A custom Comfy UI node for using Qwen2.5-VL-3B/7B-Instruct models"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "IfnotFr",
|
|
||||||
"title": "⚡ ComfyUI Connect [REMOVED]",
|
|
||||||
"reference": "https://github.com/IfnotFr/ComfyUI-Connect",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/IfnotFr/ComfyUI-Connect"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "ginlov",
|
|
||||||
"title": "segment_to_mask_comfyui [REMOVED]",
|
|
||||||
"reference": "https://github.com/ginlov/segment_to_mask_comfyui",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/ginlov/segment_to_mask_comfyui"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Nodes:SegToMask"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "TGu-97",
|
|
||||||
"title": "TGu Utilities [REMOVED]",
|
|
||||||
"id": "tgu",
|
|
||||||
"reference": "https://github.com/TGu-97/ComfyUI-TGu-utils",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/TGu-97/ComfyUI-TGu-utils"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Nodes: MPN Switch, MPN Reroute, PN Switch. This is a set of custom nodes for ComfyUI. Mainly focus on control switches."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "IfnotFr",
|
|
||||||
"title": "ComfyUI-Connect [REMOVED]",
|
|
||||||
"reference": "https://github.com/IfnotFr/ComfyUI-Connect",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/IfnotFr/ComfyUI-Connect"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "KurtHokke",
|
|
||||||
"title": "ComfyUI_KurtHokke-Nodes [REMOVED]",
|
|
||||||
"reference": "https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "ComfyUI_KurtHokke-Nodes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "SpatialDeploy",
|
|
||||||
"title": "ComfyUI-Voxels [REMOVED]",
|
|
||||||
"reference": "https://github.com/SpatialDeploy/ComfyUI-Voxels",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/SpatialDeploy/ComfyUI-Voxels"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Tools for creating voxel based videos"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-group-selection [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-group-selection",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-group-selection"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Create a new group of nodes."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "connect-from-afar [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-connect-from-afar",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-connect-from-afar"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Connect a new link from out of screen."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-local-db [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-local-db",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-local-db"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Store text to Key-Values pair json."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-model-db [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-model-db",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-model-db"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Store settings by model."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-target-search [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-target-search",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-target-search"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Move canvas to target on dragging connection."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "chrisgoringe",
|
|
||||||
"title": "Image chooser [DEPRECATED]",
|
|
||||||
"id": "image-chooser",
|
|
||||||
"reference": "https://github.com/chrisgoringe/cg-image-picker",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/chrisgoringe/cg-image-picker"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A custom node that pauses the flow while you choose which image (or latent) to pass on to the rest of the workflow."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "weilin9999",
|
|
||||||
"title": "WeiLin-ComfyUI-prompt-all-in-one [DEPRECATED]",
|
|
||||||
"id": "prompt-all-in-one",
|
|
||||||
"reference": "https://github.com/weilin9999/WeiLin-ComfyUI-prompt-all-in-one",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/weilin9999/WeiLin-ComfyUI-prompt-all-in-one"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Write prompt words like WebUI"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "svetozarov",
|
|
||||||
"title": "AS_GeminiCaptioning Node [REMOVED]",
|
|
||||||
"reference": "https://github.com/svetozarov/AS_GeminiCaptioning",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/svetozarov/AS_GeminiCaptioning"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A ComfyUI node that combines an image with simple text parameters to create a prompt, sends it to the Google Gemini API via the google-generativeai SDK, and returns the generated text response along with the original prompt and an execution log"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-load-image-in-seq [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-load-image-in-seq",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-load-image-in-seq"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This node is load png image sequentially with metadata. Only supported for PNG format that has been created by ComfyUI.[w/renamed from comfyui-load-image-39. You need to remove previous one and reinstall to this.]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-model-metadata [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-model-metadata",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-model-metadata"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Print model metadata on note node"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-view-recommendations [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-view-recommendations",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-view-recommendations"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Load model generation data from civitai."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "jonstreeter",
|
|
||||||
"title": "Comfyui-PySceneDetect [REMOVED]",
|
|
||||||
"reference": "https://github.com/jonstreeter/Comfyui-PySceneDetect",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/jonstreeter/Comfyui-PySceneDetect"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: PySceneDetect Video Processor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "muxueChen",
|
|
||||||
"title": "ComfyUI-NTQwen25-VL [REMOVED]",
|
|
||||||
"reference": "https://github.com/muxueChen/ComfyUI-NTQwen25-VL",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/muxueChen/ComfyUI-NTQwen25-VL"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Qwen25-VL is a plugin for ComfyU"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Makki_Shizu",
|
|
||||||
"title": "ComfyUI-SaveAnimatedGIF [DEPRECATED]",
|
|
||||||
"id": "SaveAnimatedGIF",
|
|
||||||
"reference": "https://github.com/MakkiShizu/ComfyUI-SaveAnimatedGIF",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/MakkiShizu/ComfyUI-SaveAnimatedGIF"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Save animated GIF format nodes in ComfyUI"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "l1yongch1",
|
|
||||||
"title": "ComfyUI_PhiCaption [REMOVED]",
|
|
||||||
"reference": "https://github.com/l1yongch1/ComfyUI_PhiCaption",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/l1yongch1/ComfyUI_PhiCaption"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "In addition to achieving conventional single-image, single-round reverse engineering, it can also achieve single-image multi-round and multi-image single-round reverse engineering. Moreover, the Phi model has a better understanding of prompts."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "nova-florealis",
|
|
||||||
"title": "comfyui-alien [REMOVED]",
|
|
||||||
"reference": "https://github.com/nova-florealis/comfyui-alien",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/nova-florealis/comfyui-alien"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Text to Text (LLM), Text Output, Convert to Markdown, List Display (Debug)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "PluMaZero",
|
|
||||||
"title": "ComfyUI-SpaceFlower [REMOVED]",
|
|
||||||
"reference": "https://github.com/PluMaZero/ComfyUI-SpaceFlower",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/PluMaZero/ComfyUI-SpaceFlower"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Nodes: SpaceFlower_Prompt, SpaceFlower_HangulPrompt, ..."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "vahidzxc",
|
|
||||||
"title": "ComfyUI-My-Handy-Nodes [REMOVED]",
|
|
||||||
"reference": "https://github.com/vahidzxc/ComfyUI-My-Handy-Nodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/vahidzxc/ComfyUI-My-Handy-Nodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES:VahCropImage"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "Samulebotin",
|
"author": "Samulebotin",
|
||||||
"title": "ComfyUI-FreeVC_wrapper [REMOVED]",
|
"title": "ComfyUI-FreeVC_wrapper [REMOVED]",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,220 +1,5 @@
|
|||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
{
|
|
||||||
"name": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"type": "FramePackI2V",
|
|
||||||
"base": "FramePackI2V",
|
|
||||||
"save_path": "diffusers/lllyasviel",
|
|
||||||
"description": "[SNAPSHOT] This is the f1k1_x_g9_f1k1f2k2f16k4_td FramePack for HY. [w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
|
||||||
"reference": "https://huggingface.co/lllyasviel/FramePackI2V_HY",
|
|
||||||
"filename": "<huggingface>",
|
|
||||||
"url": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"size": "25.75GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_bf16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_e4m3fn)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_e4m3fn)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_scaled)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_scaled)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_bf16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp16.safetensors",
|
|
||||||
"size": "32.8GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_e4m3fn)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_e4m3fn)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_scaled)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_scaled)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
|
||||||
"size": "16.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/clip_vision_h.safetensors",
|
|
||||||
"type": "clip_vision",
|
|
||||||
"base": "clip_vision_h",
|
|
||||||
"save_path": "clip_vision",
|
|
||||||
"description": "clip_vision_h model for Wan2.1",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "clip_vision_h.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/clip_vision/clip_vision_h.safetensors",
|
|
||||||
"size": "1.26GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 1.3B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_1.3B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_bf16.safetensors",
|
|
||||||
"size": "2.84GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 1.3B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_1.3B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_fp16.safetensors",
|
|
||||||
"size": "2.84GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (bf16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (bf16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_bf16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_bf16.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp16)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (fp16)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp16.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_e4m3fn)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_e4m3fn)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
|
||||||
"size": "14.3GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_scaled)",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "diffusion_models/Wan2.1",
|
|
||||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_scaled)",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan2.1_t2v_14B_fp8_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_scaled.safetensors",
|
|
||||||
"size": "14.3GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/Wan2.1 VAE",
|
|
||||||
"type": "vae",
|
|
||||||
"base": "Wan2.1",
|
|
||||||
"save_path": "vae",
|
|
||||||
"description": "Wan2.1 VAE model",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "wan_2.1_vae.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors",
|
|
||||||
"size": "254MB"
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/umt5_xxl_fp16.safetensors",
|
|
||||||
"type": "clip",
|
|
||||||
"base": "umt5_xxl",
|
|
||||||
"save_path": "text_encoders",
|
|
||||||
"description": "umt5_xxl_fp16 text encoder for Wan2.1",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "umt5_xxl_fp16.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp16.safetensors",
|
|
||||||
"size": "11.4GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Comfy-Org/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
|
||||||
"type": "clip",
|
|
||||||
"base": "umt5_xxl",
|
|
||||||
"save_path": "text_encoders",
|
|
||||||
"description": "umt5_xxl_fp8_e4m3fn_scaled text encoder for Wan2.1",
|
|
||||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
|
||||||
"filename": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
|
||||||
"size": "6.74GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
"name": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
"type": "diffusion_model",
|
"type": "diffusion_model",
|
||||||
@@ -694,6 +479,257 @@
|
|||||||
"filename": "pulid_v1.1.safetensors",
|
"filename": "pulid_v1.1.safetensors",
|
||||||
"url": "https://huggingface.co/guozinan/PuLID/resolve/main/pulid_v1.1.safetensors",
|
"url": "https://huggingface.co/guozinan/PuLID/resolve/main/pulid_v1.1.safetensors",
|
||||||
"size": "984MB"
|
"size": "984MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Kolors-IP-Adapter-Plus.bin (Kwai-Kolors/Kolors-IP-Adapter-Plus)",
|
||||||
|
"type": "IP-Adapter",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "ipadapter",
|
||||||
|
"description": "You can use this model in the [a/ComfyUI IPAdapter plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) extension.",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus",
|
||||||
|
"filename": "Kolors-IP-Adapter-Plus.bin",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus/resolve/main/ip_adapter_plus_general.bin",
|
||||||
|
"size": "1.01GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Kolors-IP-Adapter-FaceID-Plus.bin (Kwai-Kolors/Kolors-IP-Adapter-Plus)",
|
||||||
|
"type": "IP-Adapter",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "ipadapter",
|
||||||
|
"description": "You can use this model in the [a/ComfyUI IPAdapter plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) extension.",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-FaceID-Plus",
|
||||||
|
"filename": "Kolors-IP-Adapter-FaceID-Plus.bin",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-FaceID-Plus/resolve/main/ipa-faceid-plus.bin",
|
||||||
|
"size": "2.39GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CLIPVision model (Kwai-Kolors/Kolors-IP-Adapter-Plus/clip-vit-large)",
|
||||||
|
"type": "clip_vision",
|
||||||
|
"base": "ViT-L",
|
||||||
|
"save_path": "clip_vision",
|
||||||
|
"description": "CLIPVision model (This is required in cubiq/ComfyUI_IPAdapter_plus)",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus",
|
||||||
|
"filename": "clip-vit-large-patch14-336.bin",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus/resolve/main/image_encoder/pytorch_model.bin",
|
||||||
|
"size": "1.71GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus depth d model v1.1 (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus depth d model v1.1 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-depth-d-v-1-1-fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-depth-d-v-1-1-fp16.safetensors",
|
||||||
|
"size": "1.74GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus depth g model v1.0 (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus depth g model v1.0 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-depth-g-v1-0-fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-depth-g-v1-0-fp16.safetensors",
|
||||||
|
"size": "1.74GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus depth g model v1.0",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus depth g model v1.0. This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-depth-g-v1-0.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-depth-g-v1-0.safetensors",
|
||||||
|
"size": "3.47GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus normal d model v1.0 (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus normal d model v1.0 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-normal-d-v1-0-fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-d-v1-0-fp16.safetensors",
|
||||||
|
"size": "1.74GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus normal d model v1.0",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus normal d model v1.0. This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-normal-d-v1-0.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-d-v1-0.safetensors",
|
||||||
|
"size": "3.47GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus normal g model v1.0 (fp16)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus normal g model v1.0 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-normal-g-v1-0-fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-g-v1-0-fp16.safetensors",
|
||||||
|
"size": "1.74GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/lotus normal g model v1.0",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "lotus",
|
||||||
|
"save_path": "diffusion_models",
|
||||||
|
"description": "lotus normal g model v1.0. This model can be used in ComfyUI-Lotus custom nodes.",
|
||||||
|
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||||
|
"filename": "lotus-normal-g-v1-0.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-g-v1-0.safetensors",
|
||||||
|
"size": "3.47GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Depth Pro model",
|
||||||
|
"type": "depth-pro",
|
||||||
|
"base": "depth-pro",
|
||||||
|
"save_path": "depth/ml-depth-pro",
|
||||||
|
"description": "Depth pro model for [a/ComfyUI-Depth-Pro](https://github.com/spacepxl/ComfyUI-Depth-Pro)",
|
||||||
|
"reference": "https://huggingface.co/spacepxl/ml-depth-pro",
|
||||||
|
"filename": "depth_pro.fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/spacepxl/ml-depth-pro/resolve/main/depth_pro.fp16.safetensors",
|
||||||
|
"size": "1.9GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "jasperai/FLUX.1-dev-Controlnet-Upscaler",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "controlnet/FLUX.1/jasperai-dev-Upscaler",
|
||||||
|
"description": "This is Flux.1-dev ControlNet for low resolution images developed by Jasper research team.",
|
||||||
|
"reference": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Upscaler",
|
||||||
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Upscaler/resolve/main/diffusion_pytorch_model.safetensors",
|
||||||
|
"size": "3.58GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "jasperai/FLUX.1-dev-Controlnet-Depth",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "controlnet/FLUX.1/jasperai-dev-Depth",
|
||||||
|
"description": "This is Flux.1-dev ControlNet for Depth map developed by Jasper research team.",
|
||||||
|
"reference": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Depth",
|
||||||
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Depth/resolve/main/diffusion_pytorch_model.safetensors",
|
||||||
|
"size": "3.58GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "jasperai/Flux.1-dev-Controlnet-Surface-Normals",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "controlnet/FLUX.1/jasperai-dev-Surface-Normals",
|
||||||
|
"description": "This is Flux.1-dev ControlNet for Surface Normals map developed by Jasper research team.",
|
||||||
|
"reference": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Surface-Normals",
|
||||||
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Surface-Normals/resolve/main/diffusion_pytorch_model.safetensors",
|
||||||
|
"size": "3.58GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro (fp8_e4m3fn) by Kijai",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "controlnet/FLUX.1",
|
||||||
|
"description": "FLUX.1 [Dev] Union Controlnet. Supports Canny, Tile, Depth, Blur, Pose, Gray, Low Quality\nVersion quantized to fp8_e4m3fn by Kijai",
|
||||||
|
"reference": "https://huggingface.co/Kijai/flux-fp8",
|
||||||
|
"filename": "flux_shakker_labs_union_pro-fp8_e4m3fn.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/flux-fp8/resolve/main/flux_shakker_labs_union_pro-fp8_e4m3fn.safetensors",
|
||||||
|
"size": "3.3GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors [Long CLIP L]",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "clip",
|
||||||
|
"save_path": "text_encoders/long_clip",
|
||||||
|
"description": "Greatly improved TEXT + Detail (as CLIP-L for Flux.1)",
|
||||||
|
"reference": "https://huggingface.co/zer0int",
|
||||||
|
"filename": "ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors",
|
||||||
|
"url": "https://huggingface.co/zer0int/CLIP-GmP-ViT-L-14/resolve/main/ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors",
|
||||||
|
"size": "931MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors [Long CLIP L]",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "clip",
|
||||||
|
"save_path": "text_encoders/long_clip",
|
||||||
|
"description": "Greatly improved TEXT + Detail (as CLIP-L for Flux.1)",
|
||||||
|
"reference": "https://huggingface.co/zer0int",
|
||||||
|
"filename": "ViT-L-14-TEXT-detail-improved-hiT-GmP-TE-only-HF.safetensors",
|
||||||
|
"url": "https://huggingface.co/zer0int/CLIP-GmP-ViT-L-14/resolve/main/ViT-L-14-TEXT-detail-improved-hiT-GmP-TE-only-HF.safetensors",
|
||||||
|
"size": "323MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro",
|
||||||
|
"type": "controlnet",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "controlnet/FLUX.1/Shakker-Labs-ControlNet-Union-Pro",
|
||||||
|
"description": "FLUX.1 [Dev] Union Controlnet. Supports Canny, Tile, Depth, Blur, Pose, Gray, Low Quality",
|
||||||
|
"reference": "https://huggingface.co/Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro",
|
||||||
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro/resolve/main/diffusion_pytorch_model.safetensors",
|
||||||
|
"size": "6.6GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Hyper-SD LoRA (8steps) - FLUX.1 [Dev]",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "loras/HyperSD/FLUX.1",
|
||||||
|
"description": "Hyper-SD LoRA (8steps) - FLUX.1 [Dev]",
|
||||||
|
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
||||||
|
"filename": "Hyper-FLUX.1-dev-8steps-lora.safetensors",
|
||||||
|
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-FLUX.1-dev-8steps-lora.safetensors",
|
||||||
|
"size": "1.39GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hyper-SD LoRA (16steps) - FLUX.1 [Dev]",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "loras/HyperSD/FLUX.1",
|
||||||
|
"description": "Hyper-SD LoRA (16steps) - FLUX.1 [Dev]",
|
||||||
|
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
||||||
|
"filename": "Hyper-FLUX.1-dev-16steps-lora.safetensors",
|
||||||
|
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-FLUX.1-dev-16steps-lora.safetensors",
|
||||||
|
"size": "1.39GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "DMD2 LoRA (4steps)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "SDXL",
|
||||||
|
"save_path": "loras/DMD2",
|
||||||
|
"description": "DMD2 LoRA (4steps)",
|
||||||
|
"reference": "https://huggingface.co/tianweiy/DMD2",
|
||||||
|
"filename": "dmd2_sdxl_4step_lora.safetensors",
|
||||||
|
"url": "https://huggingface.co/tianweiy/DMD2/resolve/main/dmd2_sdxl_4step_lora.safetensors",
|
||||||
|
"size": "787MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DMD2 LoRA (4steps/fp16)",
|
||||||
|
"type": "lora",
|
||||||
|
"base": "SDXL",
|
||||||
|
"save_path": "loras/DMD2",
|
||||||
|
"description": "DMD2 LoRA (4steps/fp16)",
|
||||||
|
"reference": "https://huggingface.co/tianweiy/DMD2",
|
||||||
|
"filename": "dmd2_sdxl_4step_lora_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/tianweiy/DMD2/resolve/main/dmd2_sdxl_4step_lora_fp16.safetensors",
|
||||||
|
"size": "394MB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,16 +311,6 @@
|
|||||||
],
|
],
|
||||||
"description": "ComfyUI node for creating some Turtle Graphic demos.",
|
"description": "ComfyUI node for creating some Turtle Graphic demos.",
|
||||||
"install_type": "git-clone"
|
"install_type": "git-clone"
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "cozy-comfyui",
|
|
||||||
"title": "cozy_ex_dynamic",
|
|
||||||
"reference": "https://github.com/cozy-comfyui/cozy_ex_dynamic",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/cozy-comfyui/cozy_ex_dynamic"
|
|
||||||
],
|
|
||||||
"description": "Dynamic Node examples for ComfyUI",
|
|
||||||
"install_type": "git-clone"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -21,27 +21,24 @@ import cm_global
|
|||||||
import manager_downloader
|
import manager_downloader
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
manager_util.add_python_path_to_env()
|
import datetime
|
||||||
|
if hasattr(datetime, 'datetime'):
|
||||||
import datetime as dt
|
from datetime import datetime
|
||||||
|
|
||||||
if hasattr(dt, 'datetime'):
|
|
||||||
from datetime import datetime as dt_datetime
|
|
||||||
|
|
||||||
def current_timestamp():
|
def current_timestamp():
|
||||||
return dt_datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
||||||
else:
|
else:
|
||||||
# NOTE: Occurs in some Mac environments.
|
# NOTE: Occurs in some Mac environments.
|
||||||
import time
|
import time
|
||||||
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{dt.__file__}'")
|
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{datetime.__file__}'")
|
||||||
|
|
||||||
def current_timestamp():
|
def current_timestamp():
|
||||||
return str(time.time()).split('.')[0]
|
return str(time.time()).split('.')[0]
|
||||||
|
|
||||||
security_check.security_check()
|
security_check.security_check()
|
||||||
|
|
||||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
manager_util.add_python_path_to_env()
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
|
||||||
|
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||||
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
|
|
||||||
|
|
||||||
def skip_pip_spam(x):
|
def skip_pip_spam(x):
|
||||||
@@ -121,11 +118,12 @@ read_config()
|
|||||||
read_uv_mode()
|
read_uv_mode()
|
||||||
check_file_logging()
|
check_file_logging()
|
||||||
|
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
cm_global.pip_overrides = {'numpy': 'numpy<2', 'ultralytics': 'ultralytics==8.3.40'}
|
||||||
if os.path.exists(manager_pip_overrides_path):
|
if os.path.exists(manager_pip_overrides_path):
|
||||||
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
cm_global.pip_overrides = json.load(json_file)
|
cm_global.pip_overrides = json.load(json_file)
|
||||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
||||||
|
cm_global.pip_overrides['ultralytics'] = 'ultralytics==8.3.40' # for security
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists(manager_pip_blacklist_path):
|
if os.path.exists(manager_pip_blacklist_path):
|
||||||
@@ -691,6 +689,14 @@ def execute_lazy_cnr_switch(target, zip_url, from_path, to_path, no_deps, custom
|
|||||||
file.write('\n'.join(list(extracted)))
|
file.write('\n'.join(list(extracted)))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_migration(moves):
|
||||||
|
import shutil
|
||||||
|
for x in moves:
|
||||||
|
if os.path.exists(x[0]) and not os.path.exists(x[1]):
|
||||||
|
shutil.move(x[0], x[1])
|
||||||
|
print(f"[ComfyUI-Manager] MIGRATION: '{x[0]}' -> '{x[1]}'")
|
||||||
|
|
||||||
|
|
||||||
script_executed = False
|
script_executed = False
|
||||||
|
|
||||||
def execute_startup_script():
|
def execute_startup_script():
|
||||||
@@ -748,6 +754,9 @@ def execute_startup_script():
|
|||||||
execute_lazy_cnr_switch(script[0], script[2], script[3], script[4], script[5], script[6])
|
execute_lazy_cnr_switch(script[0], script[2], script[3], script[4], script[5], script[6])
|
||||||
execute_lazy_install_script(script[3], script[7])
|
execute_lazy_install_script(script[3], script[7])
|
||||||
|
|
||||||
|
elif script[1] == "#LAZY-MIGRATION":
|
||||||
|
execute_migration(script[2])
|
||||||
|
|
||||||
elif script[1] == "#LAZY-DELETE-NODEPACK":
|
elif script[1] == "#LAZY-DELETE-NODEPACK":
|
||||||
execute_lazy_delete(script[2])
|
execute_lazy_delete(script[2])
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
name = "comfyui-manager"
|
||||||
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||||
version = "3.31.13"
|
version = "3.30.3"
|
||||||
license = { file = "LICENSE.txt" }
|
license = { file = "LICENSE.txt" }
|
||||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user