mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-19 10:50:15 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
d7a2bdb895
@ -1,6 +1,6 @@
|
|||||||
ComfyUI
|
ComfyUI
|
||||||
=======
|
=======
|
||||||
A powerful and modular stable diffusion GUI.
|
A powerful and modular stable diffusion GUI and backend.
|
||||||
-----------
|
-----------
|
||||||

|

|
||||||
|
|
||||||
@ -146,9 +146,9 @@ This will let you use: pip3.10 to install all the dependencies.
|
|||||||
|
|
||||||
## How to increase generation speed?
|
## How to increase generation speed?
|
||||||
|
|
||||||
Make sure you use the CheckpointLoaderSimple node to load checkpoints. It will auto pick the right settings depending on your GPU.
|
Make sure you use the regular loaders/Load Checkpoint node to load checkpoints. It will auto pick the right settings depending on your GPU.
|
||||||
|
|
||||||
You can set this command line setting to disable the upcasting to fp32 in some cross attention operations which will increase your speed. Note that this doesn't do anything when xformers is enabled and will very likely give you black images on SD2.x models.
|
You can set this command line setting to disable the upcasting to fp32 in some cross attention operations which will increase your speed. Note that this will very likely give you black images on SD2.x models. If you use xformers this option does not do anything.
|
||||||
|
|
||||||
```--dont-upcast-attention```
|
```--dont-upcast-attention```
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,6 @@ vram_group.add_argument("--cpu", action="store_true", help="To use the CPU for e
|
|||||||
|
|
||||||
parser.add_argument("--dont-print-server", action="store_true", help="Don't print server output.")
|
parser.add_argument("--dont-print-server", action="store_true", help="Don't print server output.")
|
||||||
parser.add_argument("--quick-test-for-ci", action="store_true", help="Quick test for CI.")
|
parser.add_argument("--quick-test-for-ci", action="store_true", help="Quick test for CI.")
|
||||||
parser.add_argument("--windows-standalone-build", action="store_true", help="Windows standalone build.")
|
parser.add_argument("--windows-standalone-build", action="store_true", help="Windows standalone build: Enable convenient things that most people using the standalone windows build will probably enjoy (like auto opening the page on startup).")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from transformers import CLIPVisionModelWithProjection, CLIPVisionConfig, CLIPImageProcessor
|
from transformers import CLIPVisionModelWithProjection, CLIPVisionConfig, CLIPImageProcessor
|
||||||
from .utils import load_torch_file, transformers_convert
|
from .utils import load_torch_file, transformers_convert
|
||||||
import os
|
import os
|
||||||
|
import torch
|
||||||
|
|
||||||
class ClipVisionModel():
|
class ClipVisionModel():
|
||||||
def __init__(self, json_config):
|
def __init__(self, json_config):
|
||||||
@ -20,7 +21,8 @@ class ClipVisionModel():
|
|||||||
self.model.load_state_dict(sd, strict=False)
|
self.model.load_state_dict(sd, strict=False)
|
||||||
|
|
||||||
def encode_image(self, image):
|
def encode_image(self, image):
|
||||||
inputs = self.processor(images=[image[0]], return_tensors="pt")
|
img = torch.clip((255. * image[0]), 0, 255).round().int()
|
||||||
|
inputs = self.processor(images=[img], return_tensors="pt")
|
||||||
outputs = self.model(**inputs)
|
outputs = self.model(**inputs)
|
||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,8 @@ try:
|
|||||||
except:
|
except:
|
||||||
OOM_EXCEPTION = Exception
|
OOM_EXCEPTION = Exception
|
||||||
|
|
||||||
|
XFORMERS_VERSION = ""
|
||||||
|
XFORMERS_ENABLED_VAE = True
|
||||||
if args.disable_xformers:
|
if args.disable_xformers:
|
||||||
XFORMERS_IS_AVAILABLE = False
|
XFORMERS_IS_AVAILABLE = False
|
||||||
else:
|
else:
|
||||||
@ -52,6 +54,17 @@ else:
|
|||||||
import xformers
|
import xformers
|
||||||
import xformers.ops
|
import xformers.ops
|
||||||
XFORMERS_IS_AVAILABLE = True
|
XFORMERS_IS_AVAILABLE = True
|
||||||
|
try:
|
||||||
|
XFORMERS_VERSION = xformers.version.__version__
|
||||||
|
print("xformers version:", XFORMERS_VERSION)
|
||||||
|
if XFORMERS_VERSION.startswith("0.0.18"):
|
||||||
|
print()
|
||||||
|
print("WARNING: This version of xformers has a major bug where you will get black images when generating high resolution images.")
|
||||||
|
print("Please downgrade or upgrade xformers to a different version.")
|
||||||
|
print()
|
||||||
|
XFORMERS_ENABLED_VAE = False
|
||||||
|
except:
|
||||||
|
pass
|
||||||
except:
|
except:
|
||||||
XFORMERS_IS_AVAILABLE = False
|
XFORMERS_IS_AVAILABLE = False
|
||||||
|
|
||||||
@ -223,13 +236,8 @@ def xformers_enabled_vae():
|
|||||||
enabled = xformers_enabled()
|
enabled = xformers_enabled()
|
||||||
if not enabled:
|
if not enabled:
|
||||||
return False
|
return False
|
||||||
try:
|
|
||||||
#0.0.18 has a bug where Nan is returned when inputs are too big (1152x1920 res images and above)
|
return XFORMERS_ENABLED_VAE
|
||||||
if xformers.version.__version__ == "0.0.18":
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return enabled
|
|
||||||
|
|
||||||
def pytorch_attention_enabled():
|
def pytorch_attention_enabled():
|
||||||
return ENABLE_PYTORCH_ATTENTION
|
return ENABLE_PYTORCH_ATTENTION
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 116 KiB |
@ -88,3 +88,8 @@ class Example:
|
|||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"Example": Example
|
"Example": Example
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"Example": "Example Node"
|
||||||
|
}
|
||||||
|
|||||||
50
nodes.py
50
nodes.py
@ -1106,6 +1106,54 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"DiffusersLoader": DiffusersLoader,
|
"DiffusersLoader": DiffusersLoader,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
# Sampling
|
||||||
|
"KSampler": "KSampler",
|
||||||
|
"KSamplerAdvanced": "KSampler (Advanced)",
|
||||||
|
# Loaders
|
||||||
|
"CheckpointLoader": "Load Checkpoint (With Config)",
|
||||||
|
"CheckpointLoaderSimple": "Load Checkpoint",
|
||||||
|
"VAELoader": "Load VAE",
|
||||||
|
"LoraLoader": "Load LoRA",
|
||||||
|
"CLIPLoader": "Load CLIP",
|
||||||
|
"ControlNetLoader": "Load ControlNet Model",
|
||||||
|
"DiffControlNetLoader": "Load ControlNet Model (diff)",
|
||||||
|
"StyleModelLoader": "Load Style Model",
|
||||||
|
"CLIPVisionLoader": "Load CLIP Vision",
|
||||||
|
"UpscaleModelLoader": "Load Upscale Model",
|
||||||
|
# Conditioning
|
||||||
|
"CLIPVisionEncode": "CLIP Vision Encode",
|
||||||
|
"StyleModelApply": "Apply Style Model",
|
||||||
|
"CLIPTextEncode": "CLIP Text Encode (Prompt)",
|
||||||
|
"CLIPSetLastLayer": "CLIP Set Last Layer",
|
||||||
|
"ConditioningCombine": "Conditioning (Combine)",
|
||||||
|
"ConditioningSetArea": "Conditioning (Set Area)",
|
||||||
|
"ControlNetApply": "Apply ControlNet",
|
||||||
|
# Latent
|
||||||
|
"VAEEncodeForInpaint": "VAE Encode (for Inpainting)",
|
||||||
|
"SetLatentNoiseMask": "Set Latent Noise Mask",
|
||||||
|
"VAEDecode": "VAE Decode",
|
||||||
|
"VAEEncode": "VAE Encode",
|
||||||
|
"LatentRotate": "Rotate Latent",
|
||||||
|
"LatentFlip": "Flip Latent",
|
||||||
|
"LatentCrop": "Crop Latent",
|
||||||
|
"EmptyLatentImage": "Empty Latent Image",
|
||||||
|
"LatentUpscale": "Upscale Latent",
|
||||||
|
"LatentComposite": "Latent Composite",
|
||||||
|
# Image
|
||||||
|
"SaveImage": "Save Image",
|
||||||
|
"PreviewImage": "Preview Image",
|
||||||
|
"LoadImage": "Load Image",
|
||||||
|
"LoadImageMask": "Load Image (as Mask)",
|
||||||
|
"ImageScale": "Upscale Image",
|
||||||
|
"ImageUpscaleWithModel": "Upscale Image (using Model)",
|
||||||
|
"ImageInvert": "Invert Image",
|
||||||
|
"ImagePadForOutpaint": "Pad Image for Outpainting",
|
||||||
|
# _for_testing
|
||||||
|
"VAEDecodeTiled": "VAE Decode (Tiled)",
|
||||||
|
"VAEEncodeTiled": "VAE Encode (Tiled)",
|
||||||
|
}
|
||||||
|
|
||||||
def load_custom_node(module_path):
|
def load_custom_node(module_path):
|
||||||
module_name = os.path.basename(module_path)
|
module_name = os.path.basename(module_path)
|
||||||
if os.path.isfile(module_path):
|
if os.path.isfile(module_path):
|
||||||
@ -1121,6 +1169,8 @@ def load_custom_node(module_path):
|
|||||||
module_spec.loader.exec_module(module)
|
module_spec.loader.exec_module(module)
|
||||||
if hasattr(module, "NODE_CLASS_MAPPINGS") and getattr(module, "NODE_CLASS_MAPPINGS") is not None:
|
if hasattr(module, "NODE_CLASS_MAPPINGS") and getattr(module, "NODE_CLASS_MAPPINGS") is not None:
|
||||||
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
|
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
|
||||||
|
if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS") and getattr(module, "NODE_DISPLAY_NAME_MAPPINGS") is not None:
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS)
|
||||||
else:
|
else:
|
||||||
print(f"Skip {module_path} module for custom nodes due to the lack of NODE_CLASS_MAPPINGS.")
|
print(f"Skip {module_path} module for custom nodes due to the lack of NODE_CLASS_MAPPINGS.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -47,7 +47,7 @@
|
|||||||
" !git pull\n",
|
" !git pull\n",
|
||||||
"\n",
|
"\n",
|
||||||
"!echo -= Install dependencies =-\n",
|
"!echo -= Install dependencies =-\n",
|
||||||
"!pip install xformers -r requirements.txt --extra-index-url https://download.pytorch.org/whl/cu118"
|
"!pip install xformers!=0.0.18 -r requirements.txt --extra-index-url https://download.pytorch.org/whl/cu118"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -86,6 +86,11 @@
|
|||||||
"#!wget -c https://huggingface.co/waifu-diffusion/wd-1-5-beta2/resolve/main/checkpoints/wd-1-5-beta2-fp16.safetensors -P ./models/checkpoints/\n",
|
"#!wget -c https://huggingface.co/waifu-diffusion/wd-1-5-beta2/resolve/main/checkpoints/wd-1-5-beta2-fp16.safetensors -P ./models/checkpoints/\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"# unCLIP models\n",
|
||||||
|
"#!wget -c https://huggingface.co/comfyanonymous/illuminatiDiffusionV1_v11_unCLIP/resolve/main/illuminatiDiffusionV1_v11-unclip-h-fp16.safetensors -P ./models/checkpoints/\n",
|
||||||
|
"#!wget -c https://huggingface.co/comfyanonymous/wd-1.5-beta2_unCLIP/resolve/main/wd-1-5-beta2-aesthetic-unclip-h-fp16.safetensors -P ./models/checkpoints/\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
"# VAE\n",
|
"# VAE\n",
|
||||||
"!wget -c https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors -P ./models/vae/\n",
|
"!wget -c https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors -P ./models/vae/\n",
|
||||||
"#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt -P ./models/vae/\n",
|
"#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt -P ./models/vae/\n",
|
||||||
|
|||||||
@ -214,7 +214,8 @@ class PromptServer():
|
|||||||
info['input'] = obj_class.INPUT_TYPES()
|
info['input'] = obj_class.INPUT_TYPES()
|
||||||
info['output'] = obj_class.RETURN_TYPES
|
info['output'] = obj_class.RETURN_TYPES
|
||||||
info['output_name'] = obj_class.RETURN_NAMES if hasattr(obj_class, 'RETURN_NAMES') else info['output']
|
info['output_name'] = obj_class.RETURN_NAMES if hasattr(obj_class, 'RETURN_NAMES') else info['output']
|
||||||
info['name'] = x #TODO
|
info['name'] = x
|
||||||
|
info['display_name'] = nodes.NODE_DISPLAY_NAME_MAPPINGS[x] if x in nodes.NODE_DISPLAY_NAME_MAPPINGS.keys() else x
|
||||||
info['description'] = ''
|
info['description'] = ''
|
||||||
info['category'] = 'sd'
|
info['category'] = 'sd'
|
||||||
if hasattr(obj_class, 'CATEGORY'):
|
if hasattr(obj_class, 'CATEGORY'):
|
||||||
|
|||||||
@ -21,28 +21,74 @@ const colorPalettes = {
|
|||||||
"MODEL": "#B39DDB", // light lavender-purple
|
"MODEL": "#B39DDB", // light lavender-purple
|
||||||
"STYLE_MODEL": "#C2FFAE", // light green-yellow
|
"STYLE_MODEL": "#C2FFAE", // light green-yellow
|
||||||
"VAE": "#FF6E6E", // bright red
|
"VAE": "#FF6E6E", // bright red
|
||||||
}
|
},
|
||||||
}
|
"litegraph_base": {
|
||||||
|
"NODE_TITLE_COLOR": "#999",
|
||||||
|
"NODE_SELECTED_TITLE_COLOR": "#FFF",
|
||||||
|
"NODE_TEXT_SIZE": 14,
|
||||||
|
"NODE_TEXT_COLOR": "#AAA",
|
||||||
|
"NODE_SUBTEXT_SIZE": 12,
|
||||||
|
"NODE_DEFAULT_COLOR": "#333",
|
||||||
|
"NODE_DEFAULT_BGCOLOR": "#353535",
|
||||||
|
"NODE_DEFAULT_BOXCOLOR": "#666",
|
||||||
|
"NODE_DEFAULT_SHAPE": "box",
|
||||||
|
"NODE_BOX_OUTLINE_COLOR": "#FFF",
|
||||||
|
"DEFAULT_SHADOW_COLOR": "rgba(0,0,0,0.5)",
|
||||||
|
"DEFAULT_GROUP_FONT": 24,
|
||||||
|
|
||||||
|
"WIDGET_BGCOLOR": "#222",
|
||||||
|
"WIDGET_OUTLINE_COLOR": "#666",
|
||||||
|
"WIDGET_TEXT_COLOR": "#DDD",
|
||||||
|
"WIDGET_SECONDARY_TEXT_COLOR": "#999",
|
||||||
|
|
||||||
|
"LINK_COLOR": "#9A9",
|
||||||
|
"EVENT_LINK_COLOR": "#A86",
|
||||||
|
"CONNECTING_LINK_COLOR": "#AFA",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"palette_2": {
|
"solarized": {
|
||||||
"id": "palette_2",
|
"id": "solarized",
|
||||||
"name": "Palette 2",
|
"name": "Solarized",
|
||||||
"colors": {
|
"colors": {
|
||||||
"node_slot": {
|
"node_slot": {
|
||||||
"CLIP": "#556B2F", // Dark Olive Green
|
"CLIP": "#859900", // Green
|
||||||
"CLIP_VISION": "#4B0082", // Indigo
|
"CLIP_VISION": "#6c71c4", // Indigo
|
||||||
"CLIP_VISION_OUTPUT": "#006400", // Green
|
"CLIP_VISION_OUTPUT": "#859900", // Green
|
||||||
"CONDITIONING": "#FF1493", // Deep Pink
|
"CONDITIONING": "#d33682", // Magenta
|
||||||
"CONTROL_NET": "#8B4513", // Saddle Brown
|
"CONTROL_NET": "#cb4b16", // Orange
|
||||||
"IMAGE": "#8B0000", // Dark Red
|
"IMAGE": "#dc322f", // Red
|
||||||
"LATENT": "#00008B", // Dark Blue
|
"LATENT": "#268bd2", // Blue
|
||||||
"MASK": "#2F4F4F", // Dark Slate Grey
|
"MASK": "#073642", // Base02
|
||||||
"MODEL": "#FF8C00", // Dark Orange
|
"MODEL": "#cb4b16", // Orange
|
||||||
"STYLE_MODEL": "#004A4A", // Sherpa Blue
|
"STYLE_MODEL": "#073642", // Base02
|
||||||
"UPSCALE_MODEL": "#4A004A", // Tyrian Purple
|
"UPSCALE_MODEL": "#6c71c4", // Indigo
|
||||||
"VAE": "#4F394F", // Loulou
|
"VAE": "#586e75", // Base1
|
||||||
}
|
},
|
||||||
}
|
"litegraph_base": {
|
||||||
|
"NODE_TITLE_COLOR": "#fdf6e3",
|
||||||
|
"NODE_SELECTED_TITLE_COLOR": "#b58900",
|
||||||
|
"NODE_TEXT_SIZE": 14,
|
||||||
|
"NODE_TEXT_COLOR": "#657b83",
|
||||||
|
"NODE_SUBTEXT_SIZE": 12,
|
||||||
|
"NODE_DEFAULT_COLOR": "#586e75",
|
||||||
|
"NODE_DEFAULT_BGCOLOR": "#073642",
|
||||||
|
"NODE_DEFAULT_BOXCOLOR": "#839496",
|
||||||
|
"NODE_DEFAULT_SHAPE": "box",
|
||||||
|
"NODE_BOX_OUTLINE_COLOR": "#fdf6e3",
|
||||||
|
"DEFAULT_SHADOW_COLOR": "rgba(0,0,0,0.5)",
|
||||||
|
"DEFAULT_GROUP_FONT": 24,
|
||||||
|
|
||||||
|
"WIDGET_BGCOLOR": "#002b36",
|
||||||
|
"WIDGET_OUTLINE_COLOR": "#839496",
|
||||||
|
"WIDGET_TEXT_COLOR": "#fdf6e3",
|
||||||
|
"WIDGET_SECONDARY_TEXT_COLOR": "#93a1a1",
|
||||||
|
|
||||||
|
"LINK_COLOR": "#2aa198",
|
||||||
|
"EVENT_LINK_COLOR": "#268bd2",
|
||||||
|
"CONNECTING_LINK_COLOR": "#859900",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -192,8 +238,20 @@ app.registerExtension({
|
|||||||
if (colorPalette.colors) {
|
if (colorPalette.colors) {
|
||||||
if (colorPalette.colors.node_slot) {
|
if (colorPalette.colors.node_slot) {
|
||||||
Object.assign(app.canvas.default_connection_color_byType, colorPalette.colors.node_slot);
|
Object.assign(app.canvas.default_connection_color_byType, colorPalette.colors.node_slot);
|
||||||
app.canvas.draw(true, true);
|
Object.assign(LGraphCanvas.link_type_colors, colorPalette.colors.node_slot);
|
||||||
}
|
}
|
||||||
|
if (colorPalette.colors.litegraph_base) {
|
||||||
|
// Everything updates correctly in the loop, except the Node Title and Link Color for some reason
|
||||||
|
app.canvas.node_title_color = colorPalette.colors.litegraph_base.NODE_TITLE_COLOR;
|
||||||
|
app.canvas.default_link_color = colorPalette.colors.litegraph_base.LINK_COLOR;
|
||||||
|
|
||||||
|
for (const key in colorPalette.colors.litegraph_base) {
|
||||||
|
if (colorPalette.colors.litegraph_base.hasOwnProperty(key) && LiteGraph.hasOwnProperty(key)) {
|
||||||
|
LiteGraph[key] = colorPalette.colors.litegraph_base[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.canvas.draw(true, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
184
web/extensions/core/nodeTemplates.js
Normal file
184
web/extensions/core/nodeTemplates.js
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import { app } from "/scripts/app.js";
|
||||||
|
import { ComfyDialog, $el } from "/scripts/ui.js";
|
||||||
|
|
||||||
|
// Adds the ability to save and add multiple nodes as a template
|
||||||
|
// To save:
|
||||||
|
// Select multiple nodes (ctrl + drag to select a region or ctrl+click individual nodes)
|
||||||
|
// Right click the canvas
|
||||||
|
// Save Node Template -> give it a name
|
||||||
|
//
|
||||||
|
// To add:
|
||||||
|
// Right click the canvas
|
||||||
|
// Node templates -> click the one to add
|
||||||
|
//
|
||||||
|
// To delete/rename:
|
||||||
|
// Right click the canvas
|
||||||
|
// Node templates -> Manage
|
||||||
|
|
||||||
|
const id = "Comfy.NodeTemplates";
|
||||||
|
|
||||||
|
class ManageTemplates extends ComfyDialog {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.element.classList.add("comfy-manage-templates");
|
||||||
|
this.templates = this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
createButtons() {
|
||||||
|
const btns = super.createButtons();
|
||||||
|
btns[0].textContent = "Cancel";
|
||||||
|
btns.unshift(
|
||||||
|
$el("button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Save",
|
||||||
|
onclick: () => this.save(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return btns;
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
const templates = localStorage.getItem(id);
|
||||||
|
if (templates) {
|
||||||
|
return JSON.parse(templates);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
// Find all visible inputs and save them as our new list
|
||||||
|
const inputs = this.element.querySelectorAll("input");
|
||||||
|
const updated = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
const input = inputs[i];
|
||||||
|
if (input.parentElement.style.display !== "none") {
|
||||||
|
const t = this.templates[i];
|
||||||
|
t.name = input.value.trim() || input.getAttribute("data-name");
|
||||||
|
updated.push(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.templates = updated;
|
||||||
|
this.store();
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
store() {
|
||||||
|
localStorage.setItem(id, JSON.stringify(this.templates));
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
// Show list of template names + delete button
|
||||||
|
super.show(
|
||||||
|
$el(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "1fr auto",
|
||||||
|
gap: "5px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
this.templates.flatMap((t) => {
|
||||||
|
let nameInput;
|
||||||
|
return [
|
||||||
|
$el(
|
||||||
|
"label",
|
||||||
|
{
|
||||||
|
textContent: "Name: ",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
$el("input", {
|
||||||
|
value: t.name,
|
||||||
|
dataset: { name: t.name },
|
||||||
|
$: (el) => (nameInput = el),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
$el("button", {
|
||||||
|
textContent: "Delete",
|
||||||
|
style: {
|
||||||
|
fontSize: "12px",
|
||||||
|
color: "red",
|
||||||
|
fontWeight: "normal",
|
||||||
|
},
|
||||||
|
onclick: (e) => {
|
||||||
|
nameInput.value = "";
|
||||||
|
e.target.style.display = "none";
|
||||||
|
e.target.previousElementSibling.style.display = "none";
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: id,
|
||||||
|
setup() {
|
||||||
|
const manage = new ManageTemplates();
|
||||||
|
|
||||||
|
const clipboardAction = (cb) => {
|
||||||
|
// We use the clipboard functions but dont want to overwrite the current user clipboard
|
||||||
|
// Restore it after we've run our callback
|
||||||
|
const old = localStorage.getItem("litegrapheditor_clipboard");
|
||||||
|
cb();
|
||||||
|
localStorage.setItem("litegrapheditor_clipboard", old);
|
||||||
|
};
|
||||||
|
|
||||||
|
const orig = LGraphCanvas.prototype.getCanvasMenuOptions;
|
||||||
|
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||||
|
const options = orig.apply(this, arguments);
|
||||||
|
|
||||||
|
options.push(null);
|
||||||
|
options.push({
|
||||||
|
content: `Save Selected as Template`,
|
||||||
|
disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
|
||||||
|
callback: () => {
|
||||||
|
const name = prompt("Enter name");
|
||||||
|
if (!name || !name.trim()) return;
|
||||||
|
|
||||||
|
clipboardAction(() => {
|
||||||
|
app.canvas.copyToClipboard();
|
||||||
|
manage.templates.push({
|
||||||
|
name,
|
||||||
|
data: localStorage.getItem("litegrapheditor_clipboard"),
|
||||||
|
});
|
||||||
|
manage.store();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Map each template to a menu item
|
||||||
|
const subItems = manage.templates.map((t) => ({
|
||||||
|
content: t.name,
|
||||||
|
callback: () => {
|
||||||
|
clipboardAction(() => {
|
||||||
|
localStorage.setItem("litegrapheditor_clipboard", t.data);
|
||||||
|
app.canvas.pasteFromClipboard();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (subItems.length) {
|
||||||
|
subItems.push(null, {
|
||||||
|
content: "Manage",
|
||||||
|
callback: () => manage.show(),
|
||||||
|
});
|
||||||
|
|
||||||
|
options.push({
|
||||||
|
content: "Node Templates",
|
||||||
|
submenu: {
|
||||||
|
options: subItems,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -2,6 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<title>ComfyUI</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
<link rel="stylesheet" type="text/css" href="lib/litegraph.css" />
|
<link rel="stylesheet" type="text/css" href="lib/litegraph.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||||
|
|||||||
@ -89,6 +89,7 @@
|
|||||||
NO_TITLE: 1,
|
NO_TITLE: 1,
|
||||||
TRANSPARENT_TITLE: 2,
|
TRANSPARENT_TITLE: 2,
|
||||||
AUTOHIDE_TITLE: 3,
|
AUTOHIDE_TITLE: 3,
|
||||||
|
VERTICAL_LAYOUT: "vertical", // arrange nodes vertically
|
||||||
|
|
||||||
proxy: null, //used to redirect calls
|
proxy: null, //used to redirect calls
|
||||||
node_images_path: "",
|
node_images_path: "",
|
||||||
@ -125,14 +126,14 @@
|
|||||||
registered_slot_out_types: {}, // slot types for nodeclass
|
registered_slot_out_types: {}, // slot types for nodeclass
|
||||||
slot_types_in: [], // slot types IN
|
slot_types_in: [], // slot types IN
|
||||||
slot_types_out: [], // slot types OUT
|
slot_types_out: [], // slot types OUT
|
||||||
slot_types_default_in: [], // specify for each IN slot type a(/many) deafult node(s), use single string, array, or object (with node, title, parameters, ..) like for search
|
slot_types_default_in: [], // specify for each IN slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search
|
||||||
slot_types_default_out: [], // specify for each OUT slot type a(/many) deafult node(s), use single string, array, or object (with node, title, parameters, ..) like for search
|
slot_types_default_out: [], // specify for each OUT slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search
|
||||||
|
|
||||||
alt_drag_do_clone_nodes: false, // [true!] very handy, ALT click to clone and drag the new node
|
alt_drag_do_clone_nodes: false, // [true!] very handy, ALT click to clone and drag the new node
|
||||||
|
|
||||||
do_add_triggers_slots: false, // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this
|
do_add_triggers_slots: false, // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this
|
||||||
|
|
||||||
allow_multi_output_for_events: true, // [false!] being events, it is strongly reccomended to use them sequentually, one by one
|
allow_multi_output_for_events: true, // [false!] being events, it is strongly reccomended to use them sequentially, one by one
|
||||||
|
|
||||||
middle_click_slot_add_default_node: false, //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
|
middle_click_slot_add_default_node: false, //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
|
||||||
|
|
||||||
@ -158,80 +159,67 @@
|
|||||||
console.log("Node registered: " + type);
|
console.log("Node registered: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
var categories = type.split("/");
|
const classname = base_class.name;
|
||||||
var classname = base_class.name;
|
|
||||||
|
|
||||||
var pos = type.lastIndexOf("/");
|
const pos = type.lastIndexOf("/");
|
||||||
base_class.category = type.substr(0, pos);
|
base_class.category = type.substring(0, pos);
|
||||||
|
|
||||||
if (!base_class.title) {
|
if (!base_class.title) {
|
||||||
base_class.title = classname;
|
base_class.title = classname;
|
||||||
}
|
}
|
||||||
//info.name = name.substr(pos+1,name.length - pos);
|
|
||||||
|
|
||||||
//extend class
|
//extend class
|
||||||
if (base_class.prototype) {
|
for (var i in LGraphNode.prototype) {
|
||||||
//is a class
|
if (!base_class.prototype[i]) {
|
||||||
for (var i in LGraphNode.prototype) {
|
base_class.prototype[i] = LGraphNode.prototype[i];
|
||||||
if (!base_class.prototype[i]) {
|
|
||||||
base_class.prototype[i] = LGraphNode.prototype[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var prev = this.registered_node_types[type];
|
const prev = this.registered_node_types[type];
|
||||||
if(prev)
|
if(prev) {
|
||||||
console.log("replacing node type: " + type);
|
console.log("replacing node type: " + type);
|
||||||
else
|
}
|
||||||
{
|
if( !Object.prototype.hasOwnProperty.call( base_class.prototype, "shape") ) {
|
||||||
if( !Object.hasOwnProperty( base_class.prototype, "shape") )
|
Object.defineProperty(base_class.prototype, "shape", {
|
||||||
Object.defineProperty(base_class.prototype, "shape", {
|
set: function(v) {
|
||||||
set: function(v) {
|
switch (v) {
|
||||||
switch (v) {
|
case "default":
|
||||||
case "default":
|
delete this._shape;
|
||||||
delete this._shape;
|
break;
|
||||||
break;
|
case "box":
|
||||||
case "box":
|
this._shape = LiteGraph.BOX_SHAPE;
|
||||||
this._shape = LiteGraph.BOX_SHAPE;
|
break;
|
||||||
break;
|
case "round":
|
||||||
case "round":
|
this._shape = LiteGraph.ROUND_SHAPE;
|
||||||
this._shape = LiteGraph.ROUND_SHAPE;
|
break;
|
||||||
break;
|
case "circle":
|
||||||
case "circle":
|
this._shape = LiteGraph.CIRCLE_SHAPE;
|
||||||
this._shape = LiteGraph.CIRCLE_SHAPE;
|
break;
|
||||||
break;
|
case "card":
|
||||||
case "card":
|
this._shape = LiteGraph.CARD_SHAPE;
|
||||||
this._shape = LiteGraph.CARD_SHAPE;
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
this._shape = v;
|
||||||
this._shape = v;
|
}
|
||||||
}
|
},
|
||||||
},
|
get: function() {
|
||||||
get: function(v) {
|
return this._shape;
|
||||||
return this._shape;
|
},
|
||||||
},
|
enumerable: true,
|
||||||
enumerable: true,
|
configurable: true
|
||||||
configurable: true
|
});
|
||||||
});
|
|
||||||
|
|
||||||
//warnings
|
//used to know which nodes to create when dragging files to the canvas
|
||||||
if (base_class.prototype.onPropertyChange) {
|
if (base_class.supported_extensions) {
|
||||||
console.warn(
|
for (let i in base_class.supported_extensions) {
|
||||||
"LiteGraph node class " +
|
const ext = base_class.supported_extensions[i];
|
||||||
type +
|
if(ext && ext.constructor === String) {
|
||||||
" has onPropertyChange method, it must be called onPropertyChanged with d at the end"
|
this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class;
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//used to know which nodes create when dragging files to the canvas
|
}
|
||||||
if (base_class.supported_extensions) {
|
|
||||||
for (var i in base_class.supported_extensions) {
|
|
||||||
var ext = base_class.supported_extensions[i];
|
|
||||||
if(ext && ext.constructor === String)
|
|
||||||
this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.registered_node_types[type] = base_class;
|
this.registered_node_types[type] = base_class;
|
||||||
if (base_class.constructor.name) {
|
if (base_class.constructor.name) {
|
||||||
@ -252,19 +240,11 @@
|
|||||||
" has onPropertyChange method, it must be called onPropertyChanged with d at the end"
|
" has onPropertyChange method, it must be called onPropertyChanged with d at the end"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//used to know which nodes create when dragging files to the canvas
|
|
||||||
if (base_class.supported_extensions) {
|
|
||||||
for (var i=0; i < base_class.supported_extensions.length; i++) {
|
|
||||||
var ext = base_class.supported_extensions[i];
|
|
||||||
if(ext && ext.constructor === String)
|
|
||||||
this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO one would want to know input and ouput :: this would allow trought registerNodeAndSlotType to get all the slots types
|
// TODO one would want to know input and ouput :: this would allow through registerNodeAndSlotType to get all the slots types
|
||||||
//console.debug("Registering "+type);
|
if (this.auto_load_slot_types) {
|
||||||
if (this.auto_load_slot_types) nodeTmp = new base_class(base_class.title || "tmpnode");
|
new base_class(base_class.title || "tmpnode");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1260,37 +1240,39 @@
|
|||||||
* Positions every node in a more readable manner
|
* Positions every node in a more readable manner
|
||||||
* @method arrange
|
* @method arrange
|
||||||
*/
|
*/
|
||||||
LGraph.prototype.arrange = function(margin) {
|
LGraph.prototype.arrange = function (margin, layout) {
|
||||||
margin = margin || 100;
|
margin = margin || 100;
|
||||||
|
|
||||||
var nodes = this.computeExecutionOrder(false, true);
|
const nodes = this.computeExecutionOrder(false, true);
|
||||||
var columns = [];
|
const columns = [];
|
||||||
for (var i = 0; i < nodes.length; ++i) {
|
for (let i = 0; i < nodes.length; ++i) {
|
||||||
var node = nodes[i];
|
const node = nodes[i];
|
||||||
var col = node._level || 1;
|
const col = node._level || 1;
|
||||||
if (!columns[col]) {
|
if (!columns[col]) {
|
||||||
columns[col] = [];
|
columns[col] = [];
|
||||||
}
|
}
|
||||||
columns[col].push(node);
|
columns[col].push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
var x = margin;
|
let x = margin;
|
||||||
|
|
||||||
for (var i = 0; i < columns.length; ++i) {
|
for (let i = 0; i < columns.length; ++i) {
|
||||||
var column = columns[i];
|
const column = columns[i];
|
||||||
if (!column) {
|
if (!column) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var max_size = 100;
|
let max_size = 100;
|
||||||
var y = margin + LiteGraph.NODE_TITLE_HEIGHT;
|
let y = margin + LiteGraph.NODE_TITLE_HEIGHT;
|
||||||
for (var j = 0; j < column.length; ++j) {
|
for (let j = 0; j < column.length; ++j) {
|
||||||
var node = column[j];
|
const node = column[j];
|
||||||
node.pos[0] = x;
|
node.pos[0] = (layout == LiteGraph.VERTICAL_LAYOUT) ? y : x;
|
||||||
node.pos[1] = y;
|
node.pos[1] = (layout == LiteGraph.VERTICAL_LAYOUT) ? x : y;
|
||||||
if (node.size[0] > max_size) {
|
const max_size_index = (layout == LiteGraph.VERTICAL_LAYOUT) ? 1 : 0;
|
||||||
max_size = node.size[0];
|
if (node.size[max_size_index] > max_size) {
|
||||||
|
max_size = node.size[max_size_index];
|
||||||
}
|
}
|
||||||
y += node.size[1] + margin + LiteGraph.NODE_TITLE_HEIGHT;
|
const node_size_index = (layout == LiteGraph.VERTICAL_LAYOUT) ? 0 : 1;
|
||||||
|
y += node.size[node_size_index] + margin + LiteGraph.NODE_TITLE_HEIGHT;
|
||||||
}
|
}
|
||||||
x += max_size + margin;
|
x += max_size + margin;
|
||||||
}
|
}
|
||||||
@ -2468,43 +2450,34 @@
|
|||||||
this.title = this.constructor.title;
|
this.title = this.constructor.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.onConnectionsChange) {
|
if (this.inputs) {
|
||||||
if (this.inputs) {
|
for (var i = 0; i < this.inputs.length; ++i) {
|
||||||
for (var i = 0; i < this.inputs.length; ++i) {
|
var input = this.inputs[i];
|
||||||
var input = this.inputs[i];
|
var link_info = this.graph ? this.graph.links[input.link] : null;
|
||||||
var link_info = this.graph
|
if (this.onConnectionsChange)
|
||||||
? this.graph.links[input.link]
|
this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); //link_info has been created now, so its updated
|
||||||
: null;
|
|
||||||
this.onConnectionsChange(
|
|
||||||
LiteGraph.INPUT,
|
|
||||||
i,
|
|
||||||
true,
|
|
||||||
link_info,
|
|
||||||
input
|
|
||||||
); //link_info has been created now, so its updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.outputs) {
|
if( this.onInputAdded )
|
||||||
for (var i = 0; i < this.outputs.length; ++i) {
|
this.onInputAdded(input);
|
||||||
var output = this.outputs[i];
|
|
||||||
if (!output.links) {
|
}
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
for (var j = 0; j < output.links.length; ++j) {
|
if (this.outputs) {
|
||||||
var link_info = this.graph
|
for (var i = 0; i < this.outputs.length; ++i) {
|
||||||
? this.graph.links[output.links[j]]
|
var output = this.outputs[i];
|
||||||
: null;
|
if (!output.links) {
|
||||||
this.onConnectionsChange(
|
continue;
|
||||||
LiteGraph.OUTPUT,
|
}
|
||||||
i,
|
for (var j = 0; j < output.links.length; ++j) {
|
||||||
true,
|
var link_info = this.graph ? this.graph.links[output.links[j]] : null;
|
||||||
link_info,
|
if (this.onConnectionsChange)
|
||||||
output
|
this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); //link_info has been created now, so its updated
|
||||||
); //link_info has been created now, so its updated
|
}
|
||||||
}
|
|
||||||
}
|
if( this.onOutputAdded )
|
||||||
}
|
this.onOutputAdded(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( this.widgets )
|
if( this.widgets )
|
||||||
@ -3200,6 +3173,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(slot == null)
|
||||||
|
{
|
||||||
|
console.error("slot must be a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(slot.constructor !== Number)
|
||||||
|
console.warn("slot must be a number, use node.trigger('name') if you want to use a string");
|
||||||
|
|
||||||
var output = this.outputs[slot];
|
var output = this.outputs[slot];
|
||||||
if (!output) {
|
if (!output) {
|
||||||
return;
|
return;
|
||||||
@ -3346,26 +3328,26 @@
|
|||||||
* @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc)
|
* @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc)
|
||||||
*/
|
*/
|
||||||
LGraphNode.prototype.addOutput = function(name, type, extra_info) {
|
LGraphNode.prototype.addOutput = function(name, type, extra_info) {
|
||||||
var o = { name: name, type: type, links: null };
|
var output = { name: name, type: type, links: null };
|
||||||
if (extra_info) {
|
if (extra_info) {
|
||||||
for (var i in extra_info) {
|
for (var i in extra_info) {
|
||||||
o[i] = extra_info[i];
|
output[i] = extra_info[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.outputs) {
|
if (!this.outputs) {
|
||||||
this.outputs = [];
|
this.outputs = [];
|
||||||
}
|
}
|
||||||
this.outputs.push(o);
|
this.outputs.push(output);
|
||||||
if (this.onOutputAdded) {
|
if (this.onOutputAdded) {
|
||||||
this.onOutputAdded(o);
|
this.onOutputAdded(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,type,true);
|
if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,type,true);
|
||||||
|
|
||||||
this.setSize( this.computeSize() );
|
this.setSize( this.computeSize() );
|
||||||
this.setDirtyCanvas(true, true);
|
this.setDirtyCanvas(true, true);
|
||||||
return o;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3437,10 +3419,10 @@
|
|||||||
*/
|
*/
|
||||||
LGraphNode.prototype.addInput = function(name, type, extra_info) {
|
LGraphNode.prototype.addInput = function(name, type, extra_info) {
|
||||||
type = type || 0;
|
type = type || 0;
|
||||||
var o = { name: name, type: type, link: null };
|
var input = { name: name, type: type, link: null };
|
||||||
if (extra_info) {
|
if (extra_info) {
|
||||||
for (var i in extra_info) {
|
for (var i in extra_info) {
|
||||||
o[i] = extra_info[i];
|
input[i] = extra_info[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3448,17 +3430,17 @@
|
|||||||
this.inputs = [];
|
this.inputs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inputs.push(o);
|
this.inputs.push(input);
|
||||||
this.setSize( this.computeSize() );
|
this.setSize( this.computeSize() );
|
||||||
|
|
||||||
if (this.onInputAdded) {
|
if (this.onInputAdded) {
|
||||||
this.onInputAdded(o);
|
this.onInputAdded(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
LiteGraph.registerNodeAndSlotType(this,type);
|
LiteGraph.registerNodeAndSlotType(this,type);
|
||||||
|
|
||||||
this.setDirtyCanvas(true, true);
|
this.setDirtyCanvas(true, true);
|
||||||
return o;
|
return input;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5210,6 +5192,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
this.allow_dragcanvas = true;
|
this.allow_dragcanvas = true;
|
||||||
this.allow_dragnodes = true;
|
this.allow_dragnodes = true;
|
||||||
this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc
|
this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc
|
||||||
|
this.multi_select = false; //allow selecting multi nodes without pressing extra keys
|
||||||
this.allow_searchbox = true;
|
this.allow_searchbox = true;
|
||||||
this.allow_reconnect_links = true; //allows to change a connection with having to redo it again
|
this.allow_reconnect_links = true; //allows to change a connection with having to redo it again
|
||||||
this.align_to_grid = false; //snap to grid
|
this.align_to_grid = false; //snap to grid
|
||||||
@ -5435,7 +5418,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the visualy active graph (in case there are more in the stack)
|
* returns the visually active graph (in case there are more in the stack)
|
||||||
* @method getCurrentGraph
|
* @method getCurrentGraph
|
||||||
* @return {LGraph} the active graph
|
* @return {LGraph} the active graph
|
||||||
*/
|
*/
|
||||||
@ -6060,9 +6043,13 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
this.graph.beforeChange();
|
this.graph.beforeChange();
|
||||||
this.node_dragged = node;
|
this.node_dragged = node;
|
||||||
}
|
}
|
||||||
if (!this.selected_nodes[node.id]) {
|
this.processNodeSelected(node, e);
|
||||||
this.processNodeSelected(node, e);
|
} else { // double-click
|
||||||
}
|
/**
|
||||||
|
* Don't call the function if the block is already selected.
|
||||||
|
* Otherwise, it could cause the block to be unselected while its panel is open.
|
||||||
|
*/
|
||||||
|
if (!node.is_selected) this.processNodeSelected(node, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dirty_canvas = true;
|
this.dirty_canvas = true;
|
||||||
@ -6474,6 +6461,10 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
var n = this.selected_nodes[i];
|
var n = this.selected_nodes[i];
|
||||||
n.pos[0] += delta[0] / this.ds.scale;
|
n.pos[0] += delta[0] / this.ds.scale;
|
||||||
n.pos[1] += delta[1] / this.ds.scale;
|
n.pos[1] += delta[1] / this.ds.scale;
|
||||||
|
if (!n.is_selected) this.processNodeSelected(n, e); /*
|
||||||
|
* Don't call the function if the block is already selected.
|
||||||
|
* Otherwise, it could cause the block to be unselected while dragging.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dirty_canvas = true;
|
this.dirty_canvas = true;
|
||||||
@ -7287,7 +7278,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
};
|
};
|
||||||
|
|
||||||
LGraphCanvas.prototype.processNodeSelected = function(node, e) {
|
LGraphCanvas.prototype.processNodeSelected = function(node, e) {
|
||||||
this.selectNode(node, e && (e.shiftKey||e.ctrlKey));
|
this.selectNode(node, e && (e.shiftKey || e.ctrlKey || this.multi_select));
|
||||||
if (this.onNodeSelected) {
|
if (this.onNodeSelected) {
|
||||||
this.onNodeSelected(node);
|
this.onNodeSelected(node);
|
||||||
}
|
}
|
||||||
@ -7323,6 +7314,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
for (var i in nodes) {
|
for (var i in nodes) {
|
||||||
var node = nodes[i];
|
var node = nodes[i];
|
||||||
if (node.is_selected) {
|
if (node.is_selected) {
|
||||||
|
this.deselectNode(node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9742,13 +9734,17 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
ctx.fillRect(margin, y, widget_width - margin * 2, H);
|
ctx.fillRect(margin, y, widget_width - margin * 2, H);
|
||||||
var range = w.options.max - w.options.min;
|
var range = w.options.max - w.options.min;
|
||||||
var nvalue = (w.value - w.options.min) / range;
|
var nvalue = (w.value - w.options.min) / range;
|
||||||
ctx.fillStyle = active_widget == w ? "#89A" : "#678";
|
if(nvalue < 0.0) nvalue = 0.0;
|
||||||
|
if(nvalue > 1.0) nvalue = 1.0;
|
||||||
|
ctx.fillStyle = w.options.hasOwnProperty("slider_color") ? w.options.slider_color : (active_widget == w ? "#89A" : "#678");
|
||||||
ctx.fillRect(margin, y, nvalue * (widget_width - margin * 2), H);
|
ctx.fillRect(margin, y, nvalue * (widget_width - margin * 2), H);
|
||||||
if(show_text && !w.disabled)
|
if(show_text && !w.disabled)
|
||||||
ctx.strokeRect(margin, y, widget_width - margin * 2, H);
|
ctx.strokeRect(margin, y, widget_width - margin * 2, H);
|
||||||
if (w.marker) {
|
if (w.marker) {
|
||||||
var marker_nvalue = (w.marker - w.options.min) / range;
|
var marker_nvalue = (w.marker - w.options.min) / range;
|
||||||
ctx.fillStyle = "#AA9";
|
if(marker_nvalue < 0.0) marker_nvalue = 0.0;
|
||||||
|
if(marker_nvalue > 1.0) marker_nvalue = 1.0;
|
||||||
|
ctx.fillStyle = w.options.hasOwnProperty("marker_color") ? w.options.marker_color : "#AA9";
|
||||||
ctx.fillRect( margin + marker_nvalue * (widget_width - margin * 2), y, 2, H );
|
ctx.fillRect( margin + marker_nvalue * (widget_width - margin * 2), y, 2, H );
|
||||||
}
|
}
|
||||||
if (show_text) {
|
if (show_text) {
|
||||||
@ -9915,6 +9911,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
case "slider":
|
case "slider":
|
||||||
var range = w.options.max - w.options.min;
|
var range = w.options.max - w.options.min;
|
||||||
var nvalue = Math.clamp((x - 15) / (widget_width - 30), 0, 1);
|
var nvalue = Math.clamp((x - 15) / (widget_width - 30), 0, 1);
|
||||||
|
if(w.options.read_only) break;
|
||||||
w.value = w.options.min + (w.options.max - w.options.min) * nvalue;
|
w.value = w.options.min + (w.options.max - w.options.min) * nvalue;
|
||||||
if (w.callback) {
|
if (w.callback) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -9926,8 +9923,16 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
case "number":
|
case "number":
|
||||||
case "combo":
|
case "combo":
|
||||||
var old_value = w.value;
|
var old_value = w.value;
|
||||||
if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") {
|
var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0;
|
||||||
w.value += event.deltaX * 0.1 * (w.options.step || 1);
|
var allow_scroll = true;
|
||||||
|
if (delta) {
|
||||||
|
if (x > -3 && x < widget_width + 3) {
|
||||||
|
allow_scroll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allow_scroll && event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") {
|
||||||
|
if(event.deltaX)
|
||||||
|
w.value += event.deltaX * 0.1 * (w.options.step || 1);
|
||||||
if ( w.options.min != null && w.value < w.options.min ) {
|
if ( w.options.min != null && w.value < w.options.min ) {
|
||||||
w.value = w.options.min;
|
w.value = w.options.min;
|
||||||
}
|
}
|
||||||
@ -9994,6 +9999,12 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0;
|
var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0;
|
||||||
if (event.click_time < 200 && delta == 0) {
|
if (event.click_time < 200 && delta == 0) {
|
||||||
this.prompt("Value",w.value,function(v) {
|
this.prompt("Value",w.value,function(v) {
|
||||||
|
// check if v is a valid equation or a number
|
||||||
|
if (/^[0-9+\-*/()\s]+$/.test(v)) {
|
||||||
|
try {//solve the equation if possible
|
||||||
|
v = eval(v);
|
||||||
|
} catch (e) { }
|
||||||
|
}
|
||||||
this.value = Number(v);
|
this.value = Number(v);
|
||||||
inner_value_change(this, this.value);
|
inner_value_change(this, this.value);
|
||||||
}.bind(w),
|
}.bind(w),
|
||||||
@ -10022,7 +10033,6 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
case "text":
|
case "text":
|
||||||
if (event.type == LiteGraph.pointerevents_method+"down") {
|
if (event.type == LiteGraph.pointerevents_method+"down") {
|
||||||
this.prompt("Value",w.value,function(v) {
|
this.prompt("Value",w.value,function(v) {
|
||||||
this.value = v;
|
|
||||||
inner_value_change(this, v);
|
inner_value_change(this, v);
|
||||||
}.bind(w),
|
}.bind(w),
|
||||||
event,w.options ? w.options.multiline : false );
|
event,w.options ? w.options.multiline : false );
|
||||||
@ -10047,6 +10057,9 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
}//end for
|
}//end for
|
||||||
|
|
||||||
function inner_value_change(widget, value) {
|
function inner_value_change(widget, value) {
|
||||||
|
if(widget.type == "number"){
|
||||||
|
value = Number(value);
|
||||||
|
}
|
||||||
widget.value = value;
|
widget.value = value;
|
||||||
if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) {
|
if ( widget.options && widget.options.property && node.properties[widget.options.property] !== undefined ) {
|
||||||
node.setProperty( widget.options.property, value );
|
node.setProperty( widget.options.property, value );
|
||||||
@ -11165,7 +11178,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
LGraphCanvas.search_limit = -1;
|
LGraphCanvas.search_limit = -1;
|
||||||
LGraphCanvas.prototype.showSearchBox = function(event, options) {
|
LGraphCanvas.prototype.showSearchBox = function(event, options) {
|
||||||
// proposed defaults
|
// proposed defaults
|
||||||
def_options = { slot_from: null
|
var def_options = { slot_from: null
|
||||||
,node_from: null
|
,node_from: null
|
||||||
,node_to: null
|
,node_to: null
|
||||||
,do_type_filter: LiteGraph.search_filter_enabled // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out
|
,do_type_filter: LiteGraph.search_filter_enabled // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out
|
||||||
@ -11863,7 +11876,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
|
|
||||||
// TODO refactor, theer are different dialog, some uses createDialog, some dont
|
// TODO refactor, theer are different dialog, some uses createDialog, some dont
|
||||||
LGraphCanvas.prototype.createDialog = function(html, options) {
|
LGraphCanvas.prototype.createDialog = function(html, options) {
|
||||||
def_options = { checkForInput: false, closeOnLeave: true, closeOnLeave_checkModified: true };
|
var def_options = { checkForInput: false, closeOnLeave: true, closeOnLeave_checkModified: true };
|
||||||
options = Object.assign(def_options, options || {});
|
options = Object.assign(def_options, options || {});
|
||||||
|
|
||||||
var dialog = document.createElement("div");
|
var dialog = document.createElement("div");
|
||||||
@ -11993,7 +12006,8 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
if (root.onClose && typeof root.onClose == "function"){
|
if (root.onClose && typeof root.onClose == "function"){
|
||||||
root.onClose();
|
root.onClose();
|
||||||
}
|
}
|
||||||
root.parentNode.removeChild(root);
|
if(root.parentNode)
|
||||||
|
root.parentNode.removeChild(root);
|
||||||
/* XXX CHECK THIS */
|
/* XXX CHECK THIS */
|
||||||
if(this.parentNode){
|
if(this.parentNode){
|
||||||
this.parentNode.removeChild(this);
|
this.parentNode.removeChild(this);
|
||||||
@ -12285,7 +12299,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
var ref_window = this.getCanvasWindow();
|
var ref_window = this.getCanvasWindow();
|
||||||
var that = this;
|
var that = this;
|
||||||
var graphcanvas = this;
|
var graphcanvas = this;
|
||||||
panel = this.createPanel(node.title || "",{
|
var panel = this.createPanel(node.title || "",{
|
||||||
closable: true
|
closable: true
|
||||||
,window: ref_window
|
,window: ref_window
|
||||||
,onOpen: function(){
|
,onOpen: function(){
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ComfyWidgets } from "./widgets.js";
|
import { ComfyWidgets } from "./widgets.js";
|
||||||
import { ComfyUI } from "./ui.js";
|
import { ComfyUI, $el } from "./ui.js";
|
||||||
import { api } from "./api.js";
|
import { api } from "./api.js";
|
||||||
import { defaultGraph } from "./defaultGraph.js";
|
import { defaultGraph } from "./defaultGraph.js";
|
||||||
import { getPngMetadata, importA1111 } from "./pnginfo.js";
|
import { getPngMetadata, importA1111 } from "./pnginfo.js";
|
||||||
@ -835,7 +835,7 @@ class ComfyApp {
|
|||||||
app.#invokeExtensionsAsync("nodeCreated", this);
|
app.#invokeExtensionsAsync("nodeCreated", this);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: nodeData.name,
|
title: nodeData.display_name || nodeData.name,
|
||||||
comfyClass: nodeData.name,
|
comfyClass: nodeData.name,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -864,12 +864,62 @@ class ComfyApp {
|
|||||||
graphData = structuredClone(defaultGraph);
|
graphData = structuredClone(defaultGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
const missingNodeTypes = [];
|
||||||
for (let n of graphData.nodes) {
|
for (let n of graphData.nodes) {
|
||||||
|
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
||||||
if (n.type == "T2IAdapterLoader") n.type = "ControlNetLoader";
|
if (n.type == "T2IAdapterLoader") n.type = "ControlNetLoader";
|
||||||
|
|
||||||
|
// Find missing node types
|
||||||
|
if (!(n.type in LiteGraph.registered_node_types)) {
|
||||||
|
missingNodeTypes.push(n.type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.graph.configure(graphData);
|
try {
|
||||||
|
this.graph.configure(graphData);
|
||||||
|
} catch (error) {
|
||||||
|
let errorHint = [];
|
||||||
|
// Try extracting filename to see if it was caused by an extension script
|
||||||
|
const filename = error.fileName || (error.stack || "").match(/(\/extensions\/.*\.js)/)?.[1];
|
||||||
|
const pos = (filename || "").indexOf("/extensions/");
|
||||||
|
if (pos > -1) {
|
||||||
|
errorHint.push(
|
||||||
|
$el("span", { textContent: "This may be due to the following script:" }),
|
||||||
|
$el("br"),
|
||||||
|
$el("span", {
|
||||||
|
style: {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
textContent: filename.substring(pos),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show dialog to let the user know something went wrong loading the data
|
||||||
|
this.ui.dialog.show(
|
||||||
|
$el("div", [
|
||||||
|
$el("p", { textContent: "Loading aborted due to error reloading workflow data" }),
|
||||||
|
$el("pre", {
|
||||||
|
style: { padding: "5px", backgroundColor: "rgba(255,0,0,0.2)" },
|
||||||
|
textContent: error.toString(),
|
||||||
|
}),
|
||||||
|
$el("pre", {
|
||||||
|
style: {
|
||||||
|
padding: "5px",
|
||||||
|
color: "#ccc",
|
||||||
|
fontSize: "10px",
|
||||||
|
maxHeight: "50vh",
|
||||||
|
overflow: "auto",
|
||||||
|
backgroundColor: "rgba(0,0,0,0.2)",
|
||||||
|
},
|
||||||
|
textContent: error.stack || "No stacktrace available",
|
||||||
|
}),
|
||||||
|
...errorHint,
|
||||||
|
]).outerHTML
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const node of this.graph._nodes) {
|
for (const node of this.graph._nodes) {
|
||||||
const size = node.computeSize();
|
const size = node.computeSize();
|
||||||
@ -893,6 +943,14 @@ class ComfyApp {
|
|||||||
|
|
||||||
this.#invokeExtensions("loadedGraphNode", node);
|
this.#invokeExtensions("loadedGraphNode", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (missingNodeTypes.length) {
|
||||||
|
this.ui.dialog.show(
|
||||||
|
`When loading the graph, the following node types were not found: <ul>${Array.from(new Set(missingNodeTypes)).map(
|
||||||
|
(t) => `<li>${t}</li>`
|
||||||
|
).join("")}</ul>Nodes that have failed to load will show as red on the graph.`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export const defaultGraph = {
|
|||||||
inputs: [{ name: "clip", type: "CLIP", link: 5 }],
|
inputs: [{ name: "clip", type: "CLIP", link: 5 }],
|
||||||
outputs: [{ name: "CONDITIONING", type: "CONDITIONING", links: [6], slot_index: 0 }],
|
outputs: [{ name: "CONDITIONING", type: "CONDITIONING", links: [6], slot_index: 0 }],
|
||||||
properties: {},
|
properties: {},
|
||||||
widgets_values: ["bad hands"],
|
widgets_values: ["text, watermark"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
@ -26,7 +26,7 @@ export const defaultGraph = {
|
|||||||
inputs: [{ name: "clip", type: "CLIP", link: 3 }],
|
inputs: [{ name: "clip", type: "CLIP", link: 3 }],
|
||||||
outputs: [{ name: "CONDITIONING", type: "CONDITIONING", links: [4], slot_index: 0 }],
|
outputs: [{ name: "CONDITIONING", type: "CONDITIONING", links: [4], slot_index: 0 }],
|
||||||
properties: {},
|
properties: {},
|
||||||
widgets_values: ["masterpiece best quality girl"],
|
widgets_values: ["beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
@ -56,7 +56,7 @@ export const defaultGraph = {
|
|||||||
],
|
],
|
||||||
outputs: [{ name: "LATENT", type: "LATENT", links: [7], slot_index: 0 }],
|
outputs: [{ name: "LATENT", type: "LATENT", links: [7], slot_index: 0 }],
|
||||||
properties: {},
|
properties: {},
|
||||||
widgets_values: [8566257, true, 20, 8, "euler", "normal", 1],
|
widgets_values: [156680208700286, true, 20, 8, "euler", "normal", 1],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: 8,
|
||||||
|
|||||||
@ -32,8 +32,9 @@ export function getPngMetadata(file) {
|
|||||||
}
|
}
|
||||||
const keyword = String.fromCharCode(...pngData.slice(offset + 8, keyword_end));
|
const keyword = String.fromCharCode(...pngData.slice(offset + 8, keyword_end));
|
||||||
// Get the text
|
// Get the text
|
||||||
const text = String.fromCharCode(...pngData.slice(keyword_end + 1, offset + 8 + length));
|
const contentArraySegment = pngData.slice(keyword_end + 1, offset + 8 + length);
|
||||||
txt_chunks[keyword] = text;
|
const contentJson = Array.from(contentArraySegment).map(s=>String.fromCharCode(s)).join('')
|
||||||
|
txt_chunks[keyword] = contentJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += 12 + length;
|
offset += 12 + length;
|
||||||
|
|||||||
@ -8,14 +8,18 @@ export function $el(tag, propsOrChildren, children) {
|
|||||||
if (Array.isArray(propsOrChildren)) {
|
if (Array.isArray(propsOrChildren)) {
|
||||||
element.append(...propsOrChildren);
|
element.append(...propsOrChildren);
|
||||||
} else {
|
} else {
|
||||||
const parent = propsOrChildren.parent;
|
const { parent, $: cb, dataset, style } = propsOrChildren;
|
||||||
delete propsOrChildren.parent;
|
delete propsOrChildren.parent;
|
||||||
const cb = propsOrChildren.$;
|
|
||||||
delete propsOrChildren.$;
|
delete propsOrChildren.$;
|
||||||
|
delete propsOrChildren.dataset;
|
||||||
|
delete propsOrChildren.style;
|
||||||
|
|
||||||
if (propsOrChildren.style) {
|
if (style) {
|
||||||
Object.assign(element.style, propsOrChildren.style);
|
Object.assign(element.style, style);
|
||||||
delete propsOrChildren.style;
|
}
|
||||||
|
|
||||||
|
if (dataset) {
|
||||||
|
Object.assign(element.dataset, dataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(element, propsOrChildren);
|
Object.assign(element, propsOrChildren);
|
||||||
@ -76,7 +80,7 @@ function dragElement(dragEl, settings) {
|
|||||||
dragEl.style.left = newPosX + "px";
|
dragEl.style.left = newPosX + "px";
|
||||||
dragEl.style.right = "unset";
|
dragEl.style.right = "unset";
|
||||||
}
|
}
|
||||||
|
|
||||||
dragEl.style.top = newPosY + "px";
|
dragEl.style.top = newPosY + "px";
|
||||||
dragEl.style.bottom = "unset";
|
dragEl.style.bottom = "unset";
|
||||||
|
|
||||||
@ -145,7 +149,7 @@ function dragElement(dragEl, settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
ensureInBounds();
|
ensureInBounds();
|
||||||
});
|
});
|
||||||
|
|
||||||
function closeDragElement() {
|
function closeDragElement() {
|
||||||
@ -155,26 +159,33 @@ function dragElement(dragEl, settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComfyDialog {
|
export class ComfyDialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.element = $el("div.comfy-modal", { parent: document.body }, [
|
this.element = $el("div.comfy-modal", { parent: document.body }, [
|
||||||
$el("div.comfy-modal-content", [
|
$el("div.comfy-modal-content", [$el("p", { $: (p) => (this.textElement = p) }), ...this.createButtons()]),
|
||||||
$el("p", { $: (p) => (this.textElement = p) }),
|
|
||||||
$el("button", {
|
|
||||||
type: "button",
|
|
||||||
textContent: "Close",
|
|
||||||
onclick: () => this.close(),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createButtons() {
|
||||||
|
return [
|
||||||
|
$el("button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Close",
|
||||||
|
onclick: () => this.close(),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.element.style.display = "none";
|
this.element.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
show(html) {
|
show(html) {
|
||||||
this.textElement.innerHTML = html;
|
if (typeof html === "string") {
|
||||||
|
this.textElement.innerHTML = html;
|
||||||
|
} else {
|
||||||
|
this.textElement.replaceChildren(html);
|
||||||
|
}
|
||||||
this.element.style.display = "flex";
|
this.element.style.display = "flex";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,7 +430,7 @@ export class ComfyUI {
|
|||||||
type: "boolean",
|
type: "boolean",
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fileInput = $el("input", {
|
const fileInput = $el("input", {
|
||||||
type: "file",
|
type: "file",
|
||||||
accept: ".json,image/png",
|
accept: ".json,image/png",
|
||||||
|
|||||||
@ -202,7 +202,8 @@ button.comfy-queue-btn {
|
|||||||
margin: 6px 0 !important;
|
margin: 6px 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comfy-modal.comfy-settings {
|
.comfy-modal.comfy-settings,
|
||||||
|
.comfy-modal.comfy-manage-templates {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
color: #999;
|
color: #999;
|
||||||
@ -256,3 +257,16 @@ button.comfy-queue-btn {
|
|||||||
color: #ddd;
|
color: #ddd;
|
||||||
border-radius: 12px 0 0 12px;
|
border-radius: 12px 0 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.litegraph .litemenu-entry.has_submenu {
|
||||||
|
position: relative;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.litemenu-entry.has_submenu::after {
|
||||||
|
content: ">";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user