mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-11 06:40:48 +08:00
Merge with upstream
This commit is contained in:
commit
8e9052c843
@ -159,7 +159,7 @@ On macOS, install exactly Python 3.11 using `brew`, which you can download from
|
||||
curl -L https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.ckpt -o ./models/checkpoints/v1-5-pruned-emaonly.ckpt
|
||||
```
|
||||
|
||||
3. Put your VAE into the `models/vae` folder.
|
||||
3. (Optional) Put your VAE into the `models/vae` folder.
|
||||
|
||||
4. (Optional) Create a virtual environment:
|
||||
1. Create an environment:
|
||||
|
||||
@ -497,7 +497,7 @@ def unet_dtype(device=None, model_params=0):
|
||||
return torch.float8_e4m3fn
|
||||
if args.fp8_e5m2_unet:
|
||||
return torch.float8_e5m2
|
||||
if should_use_fp16(device=device, model_params=model_params):
|
||||
if should_use_fp16(device=device, model_params=model_params, manual_cast=True):
|
||||
return torch.float16
|
||||
return torch.float32
|
||||
|
||||
@ -547,10 +547,8 @@ def text_encoder_dtype(device=None):
|
||||
if is_device_cpu(device):
|
||||
return torch.float16
|
||||
|
||||
if should_use_fp16(device, prioritize_performance=False):
|
||||
return torch.float16
|
||||
else:
|
||||
return torch.float32
|
||||
return torch.float16
|
||||
|
||||
|
||||
def intermediate_device():
|
||||
if args.gpu_only:
|
||||
@ -699,7 +697,7 @@ def is_device_mps(device):
|
||||
return True
|
||||
return False
|
||||
|
||||
def should_use_fp16(device=None, model_params=0, prioritize_performance=True):
|
||||
def should_use_fp16(device=None, model_params=0, prioritize_performance=True, manual_cast=False):
|
||||
global directml_enabled
|
||||
|
||||
if device is not None:
|
||||
@ -725,10 +723,13 @@ def should_use_fp16(device=None, model_params=0, prioritize_performance=True):
|
||||
if is_intel_xpu():
|
||||
return True
|
||||
|
||||
if torch.cuda.is_bf16_supported():
|
||||
if torch.version.hip:
|
||||
return True
|
||||
|
||||
props = torch.cuda.get_device_properties("cuda")
|
||||
if props.major >= 8:
|
||||
return True
|
||||
|
||||
if props.major < 6:
|
||||
return False
|
||||
|
||||
@ -741,7 +742,7 @@ def should_use_fp16(device=None, model_params=0, prioritize_performance=True):
|
||||
if x in props.name.lower():
|
||||
fp16_works = True
|
||||
|
||||
if fp16_works:
|
||||
if fp16_works or manual_cast:
|
||||
free_model_memory = (get_free_memory() * 0.9 - minimum_inference_memory())
|
||||
if (not prioritize_performance) or model_params * 4 > free_model_memory:
|
||||
return True
|
||||
|
||||
@ -462,7 +462,7 @@ def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, o
|
||||
model.load_model_weights(sd, "model.diffusion_model.")
|
||||
|
||||
if output_vae:
|
||||
vae_sd = utils.state_dict_prefix_replace(sd, {"first_stage_model.": ""}, filter_keys=True)
|
||||
vae_sd = utils.state_dict_prefix_replace(sd, {k: "" for k in model_config.vae_key_prefix}, filter_keys=True)
|
||||
vae_sd = model_config.process_vae_state_dict(vae_sd)
|
||||
vae = VAE(sd=vae_sd)
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ class BASE:
|
||||
noise_aug_config = None
|
||||
sampling_settings = {}
|
||||
latent_format = latent_formats.LatentFormat
|
||||
vae_key_prefix = ["first_stage_model."]
|
||||
|
||||
manual_cast_dtype = None
|
||||
|
||||
|
||||
@ -415,6 +415,8 @@ def tiled_scale(samples, function, tile_x=64, tile_y=64, overlap = 8, upscale_am
|
||||
out_div = torch.zeros((s.shape[0], out_channels, round(s.shape[2] * upscale_amount), round(s.shape[3] * upscale_amount)), device=output_device)
|
||||
for y in range(0, s.shape[2], tile_y - overlap):
|
||||
for x in range(0, s.shape[3], tile_x - overlap):
|
||||
x = max(0, min(s.shape[-1] - overlap, x))
|
||||
y = max(0, min(s.shape[-2] - overlap, y))
|
||||
s_in = s[:,:,y:y+tile_y,x:x+tile_x]
|
||||
|
||||
ps = function(s_in).to(output_device)
|
||||
|
||||
@ -910,6 +910,9 @@ export class GroupNodeHandler {
|
||||
const self = this;
|
||||
const onNodeCreated = this.node.onNodeCreated;
|
||||
this.node.onNodeCreated = function () {
|
||||
if (!this.widgets) {
|
||||
return;
|
||||
}
|
||||
const config = self.groupData.nodeData.config;
|
||||
if (config) {
|
||||
for (const n in config) {
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
list-style: none;
|
||||
}
|
||||
.comfy-group-manage-list-items {
|
||||
max-height: 70vh;
|
||||
max-height: calc(100% - 40px);
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ async function uploadMask(filepath, formData) {
|
||||
ClipspaceDialog.invalidatePreview();
|
||||
}
|
||||
|
||||
function prepare_mask(image, maskCanvas, maskCtx) {
|
||||
function prepare_mask(image, maskCanvas, maskCtx, maskColor) {
|
||||
// paste mask data into alpha channel
|
||||
maskCtx.drawImage(image, 0, 0, maskCanvas.width, maskCanvas.height);
|
||||
const maskData = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height);
|
||||
@ -74,9 +74,9 @@ function prepare_mask(image, maskCanvas, maskCtx) {
|
||||
else
|
||||
maskData.data[i+3] = 255;
|
||||
|
||||
maskData.data[i] = 0;
|
||||
maskData.data[i+1] = 0;
|
||||
maskData.data[i+2] = 0;
|
||||
maskData.data[i] = maskColor.r;
|
||||
maskData.data[i+1] = maskColor.g;
|
||||
maskData.data[i+2] = maskColor.b;
|
||||
}
|
||||
|
||||
maskCtx.globalCompositeOperation = 'source-over';
|
||||
@ -110,6 +110,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
|
||||
createButton(name, callback) {
|
||||
var button = document.createElement("button");
|
||||
button.style.pointerEvents = "auto";
|
||||
button.innerText = name;
|
||||
button.addEventListener("click", callback);
|
||||
return button;
|
||||
@ -146,6 +147,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
divElement.style.display = "flex";
|
||||
divElement.style.position = "relative";
|
||||
divElement.style.top = "2px";
|
||||
divElement.style.pointerEvents = "auto";
|
||||
self.brush_slider_input = document.createElement('input');
|
||||
self.brush_slider_input.setAttribute('type', 'range');
|
||||
self.brush_slider_input.setAttribute('min', '1');
|
||||
@ -173,6 +175,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
bottom_panel.style.left = "20px";
|
||||
bottom_panel.style.right = "20px";
|
||||
bottom_panel.style.height = "50px";
|
||||
bottom_panel.style.pointerEvents = "none";
|
||||
|
||||
var brush = document.createElement("div");
|
||||
brush.id = "brush";
|
||||
@ -191,14 +194,29 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
this.element.appendChild(bottom_panel);
|
||||
document.body.appendChild(brush);
|
||||
|
||||
var clearButton = this.createLeftButton("Clear", () => {
|
||||
self.maskCtx.clearRect(0, 0, self.maskCanvas.width, self.maskCanvas.height);
|
||||
});
|
||||
|
||||
this.brush_size_slider = this.createLeftSlider(self, "Thickness", (event) => {
|
||||
self.brush_size = event.target.value;
|
||||
self.updateBrushPreview(self, null, null);
|
||||
});
|
||||
var clearButton = this.createLeftButton("Clear",
|
||||
() => {
|
||||
self.maskCtx.clearRect(0, 0, self.maskCanvas.width, self.maskCanvas.height);
|
||||
});
|
||||
|
||||
this.colorButton = this.createLeftButton(this.getColorButtonText(), () => {
|
||||
if (self.brush_color_mode === "black") {
|
||||
self.brush_color_mode = "white";
|
||||
}
|
||||
else if (self.brush_color_mode === "white") {
|
||||
self.brush_color_mode = "negative";
|
||||
}
|
||||
else {
|
||||
self.brush_color_mode = "black";
|
||||
}
|
||||
|
||||
self.updateWhenBrushColorModeChanged();
|
||||
});
|
||||
|
||||
var cancelButton = this.createRightButton("Cancel", () => {
|
||||
document.removeEventListener("mouseup", MaskEditorDialog.handleMouseUp);
|
||||
document.removeEventListener("keydown", MaskEditorDialog.handleKeyDown);
|
||||
@ -219,6 +237,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
bottom_panel.appendChild(this.saveButton);
|
||||
bottom_panel.appendChild(cancelButton);
|
||||
bottom_panel.appendChild(this.brush_size_slider);
|
||||
bottom_panel.appendChild(this.colorButton);
|
||||
|
||||
imgCanvas.style.position = "absolute";
|
||||
maskCanvas.style.position = "absolute";
|
||||
@ -228,6 +247,10 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
|
||||
maskCanvas.style.top = imgCanvas.style.top;
|
||||
maskCanvas.style.left = imgCanvas.style.left;
|
||||
|
||||
const maskCanvasStyle = this.getMaskCanvasStyle();
|
||||
maskCanvas.style.mixBlendMode = maskCanvasStyle.mixBlendMode;
|
||||
maskCanvas.style.opacity = maskCanvasStyle.opacity;
|
||||
}
|
||||
|
||||
async show() {
|
||||
@ -313,7 +336,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
let maskCtx = this.maskCanvas.getContext('2d', {willReadFrequently: true });
|
||||
|
||||
imgCtx.drawImage(orig_image, 0, 0, orig_image.width, orig_image.height);
|
||||
prepare_mask(mask_image, this.maskCanvas, maskCtx);
|
||||
prepare_mask(mask_image, this.maskCanvas, maskCtx, this.getMaskColor());
|
||||
}
|
||||
|
||||
async setImages(imgCanvas) {
|
||||
@ -439,7 +462,84 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
}
|
||||
}
|
||||
|
||||
getMaskCanvasStyle() {
|
||||
if (this.brush_color_mode === "negative") {
|
||||
return {
|
||||
mixBlendMode: "difference",
|
||||
opacity: "1",
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
mixBlendMode: "initial",
|
||||
opacity: "0.7",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getMaskColor() {
|
||||
if (this.brush_color_mode === "black") {
|
||||
return { r: 0, g: 0, b: 0 };
|
||||
}
|
||||
if (this.brush_color_mode === "white") {
|
||||
return { r: 255, g: 255, b: 255 };
|
||||
}
|
||||
if (this.brush_color_mode === "negative") {
|
||||
// negative effect only works with white color
|
||||
return { r: 255, g: 255, b: 255 };
|
||||
}
|
||||
|
||||
return { r: 0, g: 0, b: 0 };
|
||||
}
|
||||
|
||||
getMaskFillStyle() {
|
||||
const maskColor = this.getMaskColor();
|
||||
|
||||
return "rgb(" + maskColor.r + "," + maskColor.g + "," + maskColor.b + ")";
|
||||
}
|
||||
|
||||
getColorButtonText() {
|
||||
let colorCaption = "unknown";
|
||||
|
||||
if (this.brush_color_mode === "black") {
|
||||
colorCaption = "black";
|
||||
}
|
||||
else if (this.brush_color_mode === "white") {
|
||||
colorCaption = "white";
|
||||
}
|
||||
else if (this.brush_color_mode === "negative") {
|
||||
colorCaption = "negative";
|
||||
}
|
||||
|
||||
return "Color: " + colorCaption;
|
||||
}
|
||||
|
||||
updateWhenBrushColorModeChanged() {
|
||||
this.colorButton.innerText = this.getColorButtonText();
|
||||
|
||||
// update mask canvas css styles
|
||||
|
||||
const maskCanvasStyle = this.getMaskCanvasStyle();
|
||||
this.maskCanvas.style.mixBlendMode = maskCanvasStyle.mixBlendMode;
|
||||
this.maskCanvas.style.opacity = maskCanvasStyle.opacity;
|
||||
|
||||
// update mask canvas rgb colors
|
||||
|
||||
const maskColor = this.getMaskColor();
|
||||
|
||||
const maskData = this.maskCtx.getImageData(0, 0, this.maskCanvas.width, this.maskCanvas.height);
|
||||
|
||||
for (let i = 0; i < maskData.data.length; i += 4) {
|
||||
maskData.data[i] = maskColor.r;
|
||||
maskData.data[i+1] = maskColor.g;
|
||||
maskData.data[i+2] = maskColor.b;
|
||||
}
|
||||
|
||||
this.maskCtx.putImageData(maskData, 0, 0);
|
||||
}
|
||||
|
||||
brush_size = 10;
|
||||
brush_color_mode = "black";
|
||||
drawing_mode = false;
|
||||
lastx = -1;
|
||||
lasty = -1;
|
||||
@ -518,6 +618,19 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
event.preventDefault();
|
||||
self.pan_move(self, event);
|
||||
}
|
||||
|
||||
let left_button_down = window.TouchEvent && event instanceof TouchEvent || event.buttons == 1;
|
||||
|
||||
if(event.shiftKey && left_button_down) {
|
||||
self.drawing_mode = false;
|
||||
|
||||
const y = event.clientY;
|
||||
let delta = (self.zoom_lasty - y)*0.005;
|
||||
self.zoom_ratio = Math.max(Math.min(10.0, self.last_zoom_ratio - delta), 0.2);
|
||||
|
||||
this.invalidatePanZoom();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pan_move(self, event) {
|
||||
@ -535,7 +648,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
draw_move(self, event) {
|
||||
if(event.ctrlKey) {
|
||||
if(event.ctrlKey || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -546,7 +659,10 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
|
||||
self.updateBrushPreview(self);
|
||||
|
||||
if (window.TouchEvent && event instanceof TouchEvent || event.buttons == 1) {
|
||||
let left_button_down = window.TouchEvent && event instanceof TouchEvent || event.buttons == 1;
|
||||
let right_button_down = [2, 5, 32].includes(event.buttons);
|
||||
|
||||
if (!event.altKey && left_button_down) {
|
||||
var diff = performance.now() - self.lasttime;
|
||||
|
||||
const maskRect = self.maskCanvas.getBoundingClientRect();
|
||||
@ -581,7 +697,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
if(diff > 20 && !this.drawing_mode)
|
||||
requestAnimationFrame(() => {
|
||||
self.maskCtx.beginPath();
|
||||
self.maskCtx.fillStyle = "rgb(0,0,0)";
|
||||
self.maskCtx.fillStyle = this.getMaskFillStyle();
|
||||
self.maskCtx.globalCompositeOperation = "source-over";
|
||||
self.maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false);
|
||||
self.maskCtx.fill();
|
||||
@ -591,7 +707,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
else
|
||||
requestAnimationFrame(() => {
|
||||
self.maskCtx.beginPath();
|
||||
self.maskCtx.fillStyle = "rgb(0,0,0)";
|
||||
self.maskCtx.fillStyle = this.getMaskFillStyle();
|
||||
self.maskCtx.globalCompositeOperation = "source-over";
|
||||
|
||||
var dx = x - self.lastx;
|
||||
@ -613,7 +729,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
|
||||
self.lasttime = performance.now();
|
||||
}
|
||||
else if(event.buttons == 2 || event.buttons == 5 || event.buttons == 32) {
|
||||
else if((event.altKey && left_button_down) || right_button_down) {
|
||||
const maskRect = self.maskCanvas.getBoundingClientRect();
|
||||
const x = (event.offsetX || event.targetTouches[0].clientX - maskRect.left) / self.zoom_ratio;
|
||||
const y = (event.offsetY || event.targetTouches[0].clientY - maskRect.top) / self.zoom_ratio;
|
||||
@ -687,13 +803,20 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
self.drawing_mode = true;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if(event.shiftKey) {
|
||||
self.zoom_lasty = event.clientY;
|
||||
self.last_zoom_ratio = self.zoom_ratio;
|
||||
return;
|
||||
}
|
||||
|
||||
const maskRect = self.maskCanvas.getBoundingClientRect();
|
||||
const x = (event.offsetX || event.targetTouches[0].clientX - maskRect.left) / self.zoom_ratio;
|
||||
const y = (event.offsetY || event.targetTouches[0].clientY - maskRect.top) / self.zoom_ratio;
|
||||
|
||||
self.maskCtx.beginPath();
|
||||
if (event.button == 0) {
|
||||
self.maskCtx.fillStyle = "rgb(0,0,0)";
|
||||
if (!event.altKey && event.button == 0) {
|
||||
self.maskCtx.fillStyle = this.getMaskFillStyle();
|
||||
self.maskCtx.globalCompositeOperation = "source-over";
|
||||
} else {
|
||||
self.maskCtx.globalCompositeOperation = "destination-out";
|
||||
|
||||
@ -260,6 +260,12 @@ app.registerExtension({
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
// Add menu options to conver to/from widgets
|
||||
const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.convertWidgetToInput = function (widget) {
|
||||
const config = getConfig.call(this, widget.name) ?? [widget.type, widget.options || {}];
|
||||
if (!isConvertableWidget(widget, config)) return false;
|
||||
convertToInput(this, widget, config);
|
||||
return true;
|
||||
};
|
||||
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
||||
const r = origGetExtraMenuOptions ? origGetExtraMenuOptions.apply(this, arguments) : undefined;
|
||||
|
||||
|
||||
@ -11549,7 +11549,7 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
dialog.close();
|
||||
} else if (e.keyCode == 13) {
|
||||
if (selected) {
|
||||
select(selected.innerHTML);
|
||||
select(unescape(selected.dataset["type"]));
|
||||
} else if (first) {
|
||||
select(first);
|
||||
} else {
|
||||
@ -11910,7 +11910,7 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
var ctor = LiteGraph.registered_node_types[ type ];
|
||||
if(filter && ctor.filter != filter )
|
||||
return false;
|
||||
if ((!options.show_all_if_empty || str) && type.toLowerCase().indexOf(str) === -1)
|
||||
if ((!options.show_all_if_empty || str) && type.toLowerCase().indexOf(str) === -1 && (!ctor.title || ctor.title.toLowerCase().indexOf(str) === -1))
|
||||
return false;
|
||||
|
||||
// filter by slot IN, OUT types
|
||||
@ -11964,7 +11964,18 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
if (!first) {
|
||||
first = type;
|
||||
}
|
||||
help.innerText = type;
|
||||
|
||||
const nodeType = LiteGraph.registered_node_types[type];
|
||||
if (nodeType?.title) {
|
||||
help.innerText = nodeType?.title;
|
||||
const typeEl = document.createElement("span");
|
||||
typeEl.className = "litegraph lite-search-item-type";
|
||||
typeEl.textContent = type;
|
||||
help.append(typeEl);
|
||||
} else {
|
||||
help.innerText = type;
|
||||
}
|
||||
|
||||
help.dataset["type"] = escape(type);
|
||||
help.className = "litegraph lite-search-item";
|
||||
if (className) {
|
||||
|
||||
@ -184,6 +184,7 @@
|
||||
color: white;
|
||||
padding-left: 10px;
|
||||
margin-right: 5px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.litegraph.litesearchbox .name {
|
||||
@ -227,6 +228,18 @@
|
||||
color: black;
|
||||
}
|
||||
|
||||
.litegraph.lite-search-item-type {
|
||||
display: inline-block;
|
||||
background: rgba(0,0,0,0.2);
|
||||
margin-left: 5px;
|
||||
font-size: 14px;
|
||||
padding: 2px 5px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
opacity: 0.8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* DIALOGs ******/
|
||||
|
||||
.litegraph .dialog {
|
||||
|
||||
@ -5,6 +5,7 @@ class ComfyApi extends EventTarget {
|
||||
super();
|
||||
this.api_host = location.host;
|
||||
this.api_base = location.pathname.split('/').slice(0, -1).join('/');
|
||||
this.initialClientId = sessionStorage.getItem("clientId");
|
||||
}
|
||||
|
||||
apiURL(route) {
|
||||
@ -118,7 +119,8 @@ class ComfyApi extends EventTarget {
|
||||
case "status":
|
||||
if (msg.data.sid) {
|
||||
this.clientId = msg.data.sid;
|
||||
window.name = this.clientId;
|
||||
window.name = this.clientId; // use window name so it isnt reused when duplicating tabs
|
||||
sessionStorage.setItem("clientId", this.clientId); // store in session storage so duplicate tab can load correct workflow
|
||||
}
|
||||
this.dispatchEvent(new CustomEvent("status", { detail: msg.data.status }));
|
||||
break;
|
||||
|
||||
@ -1499,12 +1499,17 @@ export class ComfyApp {
|
||||
// Load previous workflow
|
||||
let restored = false;
|
||||
try {
|
||||
const json = localStorage.getItem("workflow");
|
||||
if (json) {
|
||||
const workflow = JSON.parse(json);
|
||||
await this.loadGraphData(workflow);
|
||||
restored = true;
|
||||
}
|
||||
const loadWorkflow = async (json) => {
|
||||
if (json) {
|
||||
const workflow = JSON.parse(json);
|
||||
await this.loadGraphData(workflow);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
const clientId = api.initialClientId ?? api.clientId;
|
||||
restored =
|
||||
(clientId && (await loadWorkflow(sessionStorage.getItem(`workflow:${clientId}`)))) ||
|
||||
(await loadWorkflow(localStorage.getItem("workflow")));
|
||||
} catch (err) {
|
||||
console.error("Error loading previous workflow", err);
|
||||
}
|
||||
@ -1515,7 +1520,13 @@ export class ComfyApp {
|
||||
}
|
||||
|
||||
// Save current workflow automatically
|
||||
setInterval(() => localStorage.setItem("workflow", JSON.stringify(this.graph.serialize())), 1000);
|
||||
setInterval(() => {
|
||||
const workflow = JSON.stringify(this.graph.serialize());
|
||||
localStorage.setItem("workflow", workflow);
|
||||
if (api.clientId) {
|
||||
sessionStorage.setItem(`workflow:${api.clientId}`, workflow);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.#addDrawNodeHandler();
|
||||
this.#addDrawGroupsHandler();
|
||||
@ -2096,6 +2107,8 @@ export class ComfyApp {
|
||||
this.loadGraphData(JSON.parse(pngInfo.Workflow)); // Support loading workflows from that webp custom node.
|
||||
} else if (pngInfo.prompt) {
|
||||
this.loadApiJson(JSON.parse(pngInfo.prompt));
|
||||
} else if (pngInfo.Prompt) {
|
||||
this.loadApiJson(JSON.parse(pngInfo.Prompt)); // Support loading prompts from that webp custom node.
|
||||
}
|
||||
}
|
||||
} else if (file.type === "application/json" || file.name?.endsWith(".json")) {
|
||||
@ -2149,8 +2162,17 @@ export class ComfyApp {
|
||||
if (value instanceof Array) {
|
||||
const [fromId, fromSlot] = value;
|
||||
const fromNode = app.graph.getNodeById(fromId);
|
||||
const toSlot = node.inputs?.findIndex((inp) => inp.name === input);
|
||||
if (toSlot !== -1) {
|
||||
let toSlot = node.inputs?.findIndex((inp) => inp.name === input);
|
||||
if (toSlot == null || toSlot === -1) {
|
||||
try {
|
||||
// Target has no matching input, most likely a converted widget
|
||||
const widget = node.widgets?.find((w) => w.name === input);
|
||||
if (widget && node.convertWidgetToInput?.(widget)) {
|
||||
toSlot = node.inputs?.length - 1;
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
if (toSlot != null || toSlot !== -1) {
|
||||
fromNode.connect(fromSlot, node, toSlot);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -81,6 +81,9 @@ export function addValueControlWidgets(node, targetWidget, defaultValue = "rando
|
||||
|
||||
const isCombo = targetWidget.type === "combo";
|
||||
let comboFilter;
|
||||
if (isCombo) {
|
||||
valueControl.options.values.push("increment-wrap");
|
||||
}
|
||||
if (isCombo && options.addFilterList !== false) {
|
||||
comboFilter = node.addWidget(
|
||||
"string",
|
||||
@ -128,6 +131,12 @@ export function addValueControlWidgets(node, targetWidget, defaultValue = "rando
|
||||
case "increment":
|
||||
current_index += 1;
|
||||
break;
|
||||
case "increment-wrap":
|
||||
current_index += 1;
|
||||
if ( current_index >= current_length ) {
|
||||
current_index = 0;
|
||||
}
|
||||
break;
|
||||
case "decrement":
|
||||
current_index -= 1;
|
||||
break;
|
||||
@ -295,7 +304,7 @@ export const ComfyWidgets = {
|
||||
let disable_rounding = app.ui.settings.getSettingValue("Comfy.DisableFloatRounding")
|
||||
if (precision == 0) precision = undefined;
|
||||
const { val, config } = getNumberDefaults(inputData, 0.5, precision, !disable_rounding);
|
||||
return { widget: node.addWidget(widgetType, inputName, val,
|
||||
return { widget: node.addWidget(widgetType, inputName, val,
|
||||
function (v) {
|
||||
if (config.round) {
|
||||
this.value = Math.round(v/config.round)*config.round;
|
||||
|
||||
@ -126,7 +126,7 @@ class LatentBatchSeedBehavior:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "samples": ("LATENT",),
|
||||
"seed_behavior": (["random", "fixed"],),}}
|
||||
"seed_behavior": (["random", "fixed"],{"default": "fixed"}),}}
|
||||
|
||||
RETURN_TYPES = ("LATENT",)
|
||||
FUNCTION = "op"
|
||||
|
||||
@ -6,6 +6,8 @@ class Example:
|
||||
-------------
|
||||
INPUT_TYPES (dict):
|
||||
Tell the main program input parameters of nodes.
|
||||
IS_CHANGED:
|
||||
optional method to control when the node is re executed.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@ -89,6 +91,17 @@ class Example:
|
||||
image = 1.0 - image
|
||||
return (image,)
|
||||
|
||||
"""
|
||||
The node will always be re executed if any of the inputs change but
|
||||
this method can be used to force the node to execute again even when the inputs don't change.
|
||||
You can make this node return a number or a string. This value will be compared to the one returned the last time the node was
|
||||
executed, if it is different the node will be executed again.
|
||||
This method is used in the core repo for the LoadImage node where they return the image hash as a string, if the image hash
|
||||
changes between executions the LoadImage node is executed again.
|
||||
"""
|
||||
#@classmethod
|
||||
#def IS_CHANGED(s, image, string_field, int_field, float_field, print_to_screen):
|
||||
# return ""
|
||||
|
||||
# A dictionary that contains all nodes you want to export with their names
|
||||
# NOTE: names should be globally unique
|
||||
|
||||
2
setup.py
2
setup.py
@ -28,7 +28,7 @@ version = '0.0.1'
|
||||
"""
|
||||
The package index to the torch built with AMD ROCm.
|
||||
"""
|
||||
amd_torch_index = ("https://download.pytorch.org/whl/rocm5.6", "https://download.pytorch.org/whl/nightly/rocm5.7")
|
||||
amd_torch_index = ("https://download.pytorch.org/whl/rocm5.7", "https://download.pytorch.org/whl/nightly/rocm6.0")
|
||||
|
||||
"""
|
||||
The package index to torch built with CUDA.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user