diff --git a/comfy/ldm/models/diffusion/ddim.py b/comfy/ldm/models/diffusion/ddim.py index fe39c76c7..5e2d73645 100644 --- a/comfy/ldm/models/diffusion/ddim.py +++ b/comfy/ldm/models/diffusion/ddim.py @@ -18,7 +18,7 @@ class DDIMSampler(object): def register_buffer(self, name, attr): if type(attr) == torch.Tensor: if attr.device != self.device: - attr = attr.to(self.device) + attr = attr.float().to(self.device) setattr(self, name, attr) def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True): diff --git a/comfyui_screenshot.png b/comfyui_screenshot.png index 72642438d..c357e2439 100644 Binary files a/comfyui_screenshot.png and b/comfyui_screenshot.png differ diff --git a/nodes.py b/nodes.py index 7e94fbaa5..ddaf6ac37 100644 --- a/nodes.py +++ b/nodes.py @@ -193,8 +193,7 @@ class CheckpointLoader: @classmethod def INPUT_TYPES(s): return {"required": { "config_name": (folder_paths.get_filename_list("configs"), ), - "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ) }, - } + "ckpt_name": (folder_paths.get_filename_list("checkpoints"), )}} RETURN_TYPES = ("MODEL", "CLIP", "VAE") FUNCTION = "load_checkpoint" @@ -208,8 +207,8 @@ class CheckpointLoader: class CheckpointLoaderSimple: @classmethod def INPUT_TYPES(s): - return {"required": { "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ) }, - } + return {"required": { "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), + }} RETURN_TYPES = ("MODEL", "CLIP", "VAE") FUNCTION = "load_checkpoint" @@ -242,9 +241,9 @@ class LoraLoader: return {"required": { "model": ("MODEL",), "clip": ("CLIP", ), "lora_name": (folder_paths.get_filename_list("loras"), ), - "strength_model": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "strength_clip": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}) }, - } + "strength_model": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), + "strength_clip": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), + }} RETURN_TYPES = ("MODEL", "CLIP") FUNCTION = "load_lora" @@ -258,8 +257,7 @@ class LoraLoader: class VAELoader: @classmethod def INPUT_TYPES(s): - return {"required": { "vae_name": (folder_paths.get_filename_list("vae"), ) }, - } + return {"required": { "vae_name": (folder_paths.get_filename_list("vae"), )}} RETURN_TYPES = ("VAE",) FUNCTION = "load_vae" @@ -274,8 +272,7 @@ class VAELoader: class ControlNetLoader: @classmethod def INPUT_TYPES(s): - return {"required": { "control_net_name": (folder_paths.get_filename_list("controlnet"), )}, - } + return {"required": { "control_net_name": (folder_paths.get_filename_list("controlnet"), )}} RETURN_TYPES = ("CONTROL_NET",) FUNCTION = "load_controlnet" @@ -291,8 +288,7 @@ class DiffControlNetLoader: @classmethod def INPUT_TYPES(s): return {"required": { "model": ("MODEL",), - "control_net_name": (folder_paths.get_filename_list("controlnet"), )}, - } + "control_net_name": (folder_paths.get_filename_list("controlnet"), )}} RETURN_TYPES = ("CONTROL_NET",) FUNCTION = "load_controlnet" @@ -334,9 +330,8 @@ class ControlNetApply: class CLIPLoader: @classmethod def INPUT_TYPES(s): - return {"required": { "clip_name": (folder_paths.get_filename_list("clip"), ),}, - } - + return {"required": { "clip_name": (folder_paths.get_filename_list("clip"), ), + }} RETURN_TYPES = ("CLIP",) FUNCTION = "load_clip" @@ -350,9 +345,8 @@ class CLIPLoader: class CLIPVisionLoader: @classmethod def INPUT_TYPES(s): - return {"required": { "clip_name": (folder_paths.get_filename_list("clip_vision"), )}, - } - + return {"required": { "clip_name": (folder_paths.get_filename_list("clip_vision"), ), + }} RETURN_TYPES = ("CLIP_VISION",) FUNCTION = "load_clip" @@ -381,8 +375,7 @@ class CLIPVisionEncode: class StyleModelLoader: @classmethod def INPUT_TYPES(s): - return {"required": { "style_model_name": (folder_paths.get_filename_list("style_models"), )}, - } + return {"required": { "style_model_name": (folder_paths.get_filename_list("style_models"), )}} RETURN_TYPES = ("STYLE_MODEL",) FUNCTION = "load_style_model" @@ -814,8 +807,9 @@ class LoadImage: def INPUT_TYPES(s): if not os.path.exists(s.input_dir): os.makedirs(s.input_dir) - return {"required": {"image": (sorted(os.listdir(s.input_dir)), )}, - } + return {"required": + {"image": (sorted(os.listdir(s.input_dir)), )}, + } CATEGORY = "image" diff --git a/server.py b/server.py index 436fdd34e..b007f9e43 100644 --- a/server.py +++ b/server.py @@ -125,7 +125,7 @@ class PromptServer(): output_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), type) if "subfolder" in request.rel_url.query: full_output_dir = os.path.join(output_dir, request.rel_url.query["subfolder"]) - if os.path.commonpath((os.path.realpath(full_output_dir), output_dir)) != output_dir: + if os.path.commonpath((os.path.abspath(full_output_dir), output_dir)) != output_dir: return web.Response(status=403) output_dir = full_output_dir diff --git a/web/scripts/app.js b/web/scripts/app.js index 6f11814a7..b4c126252 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -486,6 +486,27 @@ class ComfyApp { } } + /** + * Setup slot colors for types + */ + setupSlotColors() { + let colors = { + "CLIP": "#FFD500", // bright yellow + "CLIP_VISION": "#A8DADC", // light blue-gray + "CLIP_VISION_OUTPUT": "#ad7452", // rusty brown-orange + "CONDITIONING": "#FFA931", // vibrant orange-yellow + "CONTROL_NET": "#6EE7B7", // soft mint green + "IMAGE": "#64B5F6", // bright sky blue + "LATENT": "#FF9CF9", // light pink-purple + "MASK": "#81C784", // muted green + "MODEL": "#B39DDB", // light lavender-purple + "STYLE_MODEL": "#C2FFAE", // light green-yellow + "VAE": "#FF6E6E", // bright red + }; + + Object.assign(this.canvas.default_connection_color_byType, colors); + } + /** * Set up the app on the page */ @@ -501,6 +522,8 @@ class ComfyApp { const canvas = (this.canvas = new LGraphCanvas(canvasEl, this.graph)); this.ctx = canvasEl.getContext("2d"); + this.setupSlotColors(); + this.graph.start(); function resizeCanvas() { diff --git a/web/scripts/ui.js b/web/scripts/ui.js index a66419b89..96f5d5550 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -35,6 +35,54 @@ function $el(tag, propsOrChildren, children) { return element; } +function dragElement(dragEl) { + var posDiffX = 0, + posDiffY = 0, + posStartX = 0, + posStartY = 0, + newPosX = 0, + newPosY = 0; + if (dragEl.getElementsByClassName('drag-handle')[0]) { + // if present, the handle is where you move the DIV from: + dragEl.getElementsByClassName('drag-handle')[0].onmousedown = dragMouseDown; + } else { + // otherwise, move the DIV from anywhere inside the DIV: + dragEl.onmousedown = dragMouseDown; + } + + function dragMouseDown(e) { + e = e || window.event; + e.preventDefault(); + // get the mouse cursor position at startup: + posStartX = e.clientX; + posStartY = e.clientY; + document.onmouseup = closeDragElement; + // call a function whenever the cursor moves: + document.onmousemove = elementDrag; + } + + function elementDrag(e) { + e = e || window.event; + e.preventDefault(); + // calculate the new cursor position: + posDiffX = e.clientX - posStartX; + posDiffY = e.clientY - posStartY; + posStartX = e.clientX; + posStartY = e.clientY; + newPosX = Math.min((document.body.clientWidth - dragEl.clientWidth), Math.max(0, (dragEl.offsetLeft + posDiffX))); + newPosY = Math.min((document.body.clientHeight - dragEl.clientHeight), Math.max(0, (dragEl.offsetTop + posDiffY))); + // set the element's new position: + dragEl.style.top = newPosY + "px"; + dragEl.style.left = newPosX + "px"; + } + + function closeDragElement() { + // stop moving when mouse button is released: + document.onmouseup = null; + document.onmousemove = null; + } +} + class ComfyDialog { constructor() { this.element = $el("div.comfy-modal", { parent: document.body }, [ @@ -253,6 +301,7 @@ export class ComfyUI { this.menuContainer = $el("div.comfy-menu", { parent: document.body }, [ $el("div", { style: { overflow: "hidden", position: "relative", width: "100%" } }, [ + $el("span.drag-handle"), $el("span", { $: (q) => (this.queueSize = q) }), $el("button.comfy-settings-btn", { textContent: "⚙️", onclick: () => this.settings.show() }), ]), @@ -332,6 +381,8 @@ export class ComfyUI { $el("button", { textContent: "Load Default", onclick: () => app.loadGraphData() }), ]); + dragElement(this.menuContainer); + this.setStatus({ exec_info: { queue_remaining: "X" } }); } diff --git a/web/scripts/widgets.js b/web/scripts/widgets.js index 8c78d9551..5f5043cd0 100644 --- a/web/scripts/widgets.js +++ b/web/scripts/widgets.js @@ -20,8 +20,12 @@ export function addRandomizeWidget(node, targetWidget, name, defaultValue = fals randomize.afterQueued = () => { if (randomize.value) { const min = targetWidget.options?.min; - const max = targetWidget.options?.max; + let max = targetWidget.options?.max; if (min != null || max != null) { + if (max) { + // limit max to something that javascript can handle + max = Math.min(1125899906842624, max); + } targetWidget.value = Math.floor(Math.random() * ((max ?? 9999999999) - (min ?? 0) + 1) + (min ?? 0)); } else { targetWidget.value = Math.floor(Math.random() * 1125899906842624); diff --git a/web/style.css b/web/style.css index 52a8a9c60..d3168044f 100644 --- a/web/style.css +++ b/web/style.css @@ -111,6 +111,31 @@ body { width: 50%; } +.comfy-menu span.drag-handle { + width: 10px; + height: 20px; + display: inline-block; + overflow: hidden; + line-height: 5px; + padding: 3px 4px; + cursor: move; + vertical-align: middle; + margin-top: -.4em; + margin-left: -.2em; + font-size: 12px; + font-family: sans-serif; + letter-spacing: 2px; + color: #cccccc; + text-shadow: 1px 0 1px black; + position: absolute; + top: 0; + left: 0; +} + +.comfy-menu span.drag-handle::after { + content: '.. .. ..'; +} + .comfy-queue-btn { width: 100%; }