Merge branch 'comfyanonymous:master' into image-to-mask

This commit is contained in:
missionfloyd 2023-04-13 03:07:08 -06:00 committed by GitHub
commit 45b907fbf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 314 additions and 132 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ temp/
custom_nodes/ custom_nodes/
!custom_nodes/example_node.py.example !custom_nodes/example_node.py.example
extra_model_paths.yaml extra_model_paths.yaml
/.vs

View File

@ -35,6 +35,11 @@ Workflow examples can be found on the [Examples page](https://comfyanonymous.git
- **Ctrl + A** select all nodes - **Ctrl + A** select all nodes
- **Ctrl + M** mute/unmute selected nodes - **Ctrl + M** mute/unmute selected nodes
- **Delete** or **Backspace** delete selected nodes - **Delete** or **Backspace** delete selected nodes
- **Space** Holding space key while moving the cursor moves the canvas around. It works when holding the mouse button down so it is easier to connect different nodes when the canvas gets too large.
- **Ctrl/Shift + Click** Add clicked node to selection.
- **Ctrl + C/Ctrl + V** - Copy and paste selected nodes, without maintaining the connection to the outputs of unselected nodes.
- **Ctrl + C/Ctrl + Shift + V** - Copy and paste selected nodes, and maintaining the connection from the outputs of unselected nodes to the inputs of the newly pasted nodes.
- Holding **Shift** and drag selected nodes - Move multiple selected nodes at the same time.
# Installing # Installing
@ -92,6 +97,11 @@ Install the dependencies by opening your terminal inside the ComfyUI folder and:
After this you should have everything installed and can proceed to running ComfyUI. After this you should have everything installed and can proceed to running ComfyUI.
### Others:
[Intel Arc](https://github.com/comfyanonymous/ComfyUI/discussions/476)
Mac/MPS: There is basic support in the code but until someone makes some install instruction you are on your own.
### I already have another UI for Stable Diffusion installed do I really have to install all of these dependencies? ### I already have another UI for Stable Diffusion installed do I really have to install all of these dependencies?

View File

@ -940,6 +940,8 @@ class LoadImageMask:
input_dir = folder_paths.get_input_directory() input_dir = folder_paths.get_input_directory()
image_path = os.path.join(input_dir, image) image_path = os.path.join(input_dir, image)
i = Image.open(image_path) i = Image.open(image_path)
if i.getbands() != ("R", "G", "B", "A"):
i = i.convert("RGBA")
mask = None mask = None
c = channel[0].upper() c = channel[0].upper()
if c in i.getbands(): if c in i.getbands():

View File

@ -5,9 +5,9 @@ import { api } from "/scripts/api.js";
// Manage color palettes // Manage color palettes
const colorPalettes = { const colorPalettes = {
"palette_1": { "dark": {
"id": "palette_1", "id": "dark",
"name": "Palette 1", "name": "Dark (Default)",
"colors": { "colors": {
"node_slot": { "node_slot": {
"CLIP": "#FFD500", // bright yellow "CLIP": "#FFD500", // bright yellow
@ -45,6 +45,70 @@ const colorPalettes = {
"EVENT_LINK_COLOR": "#A86", "EVENT_LINK_COLOR": "#A86",
"CONNECTING_LINK_COLOR": "#AFA", "CONNECTING_LINK_COLOR": "#AFA",
}, },
"comfy_base": {
"fg-color": "#fff",
"bg-color": "#202020",
"comfy-menu-bg": "#353535",
"comfy-input-bg": "#222",
"input-text": "#ddd",
"descrip-text": "#999",
"drag-text": "#ccc",
"error-text": "#ff4444",
"border-color": "#4e4e4e"
}
},
},
"light": {
"id": "light",
"name": "Light",
"colors": {
"node_slot": {
"CLIP": "#FFA726", // orange
"CLIP_VISION": "#5C6BC0", // indigo
"CLIP_VISION_OUTPUT": "#8D6E63", // brown
"CONDITIONING": "#EF5350", // red
"CONTROL_NET": "#66BB6A", // green
"IMAGE": "#42A5F5", // blue
"LATENT": "#AB47BC", // purple
"MASK": "#9CCC65", // light green
"MODEL": "#7E57C2", // deep purple
"STYLE_MODEL": "#D4E157", // lime
"VAE": "#FF7043", // deep orange
},
"litegraph_base": {
"NODE_TITLE_COLOR": "#222",
"NODE_SELECTED_TITLE_COLOR": "#000",
"NODE_TEXT_SIZE": 14,
"NODE_TEXT_COLOR": "#444",
"NODE_SUBTEXT_SIZE": 12,
"NODE_DEFAULT_COLOR": "#F7F7F7",
"NODE_DEFAULT_BGCOLOR": "#F5F5F5",
"NODE_DEFAULT_BOXCOLOR": "#CCC",
"NODE_DEFAULT_SHAPE": "box",
"NODE_BOX_OUTLINE_COLOR": "#000",
"DEFAULT_SHADOW_COLOR": "rgba(0,0,0,0.1)",
"DEFAULT_GROUP_FONT": 24,
"WIDGET_BGCOLOR": "#D4D4D4",
"WIDGET_OUTLINE_COLOR": "#999",
"WIDGET_TEXT_COLOR": "#222",
"WIDGET_SECONDARY_TEXT_COLOR": "#555",
"LINK_COLOR": "#4CAF50",
"EVENT_LINK_COLOR": "#FF9800",
"CONNECTING_LINK_COLOR": "#2196F3",
},
"comfy_base": {
"fg-color": "#222",
"bg-color": "#DDD",
"comfy-menu-bg": "#F5F5F5",
"comfy-input-bg": "#C9C9C9",
"input-text": "#222",
"descrip-text": "#444",
"drag-text": "#555",
"error-text": "#F44336",
"border-color": "#CCC"
}
}, },
}, },
"solarized": { "solarized": {
@ -52,49 +116,60 @@ const colorPalettes = {
"name": "Solarized", "name": "Solarized",
"colors": { "colors": {
"node_slot": { "node_slot": {
"CLIP": "#859900", // Green "CLIP": "#2AB7CA", // light blue
"CLIP_VISION": "#6c71c4", // Indigo "CLIP_VISION": "#6c71c4", // blue violet
"CLIP_VISION_OUTPUT": "#859900", // Green "CLIP_VISION_OUTPUT": "#859900", // olive green
"CONDITIONING": "#d33682", // Magenta "CONDITIONING": "#d33682", // magenta
"CONTROL_NET": "#cb4b16", // Orange "CONTROL_NET": "#d1ffd7", // light mint green
"IMAGE": "#dc322f", // Red "IMAGE": "#5940bb", // deep blue violet
"LATENT": "#268bd2", // Blue "LATENT": "#268bd2", // blue
"MASK": "#073642", // Base02 "MASK": "#CCC9E7", // light purple-gray
"MODEL": "#cb4b16", // Orange "MODEL": "#dc322f", // red
"STYLE_MODEL": "#073642", // Base02 "STYLE_MODEL": "#1a998a", // teal
"UPSCALE_MODEL": "#6c71c4", // Indigo "UPSCALE_MODEL": "#054A29", // dark green
"VAE": "#586e75", // Base1 "VAE": "#facfad", // light pink-orange
}, },
"litegraph_base": { "litegraph_base": {
"NODE_TITLE_COLOR": "#fdf6e3", "NODE_TITLE_COLOR": "#fdf6e3", // Base3
"NODE_SELECTED_TITLE_COLOR": "#b58900", "NODE_SELECTED_TITLE_COLOR": "#A9D400",
"NODE_TEXT_SIZE": 14, "NODE_TEXT_SIZE": 14,
"NODE_TEXT_COLOR": "#657b83", "NODE_TEXT_COLOR": "#657b83", // Base00
"NODE_SUBTEXT_SIZE": 12, "NODE_SUBTEXT_SIZE": 12,
"NODE_DEFAULT_COLOR": "#586e75", "NODE_DEFAULT_COLOR": "#094656",
"NODE_DEFAULT_BGCOLOR": "#073642", "NODE_DEFAULT_BGCOLOR": "#073642", // Base02
"NODE_DEFAULT_BOXCOLOR": "#839496", "NODE_DEFAULT_BOXCOLOR": "#839496", // Base0
"NODE_DEFAULT_SHAPE": "box", "NODE_DEFAULT_SHAPE": "box",
"NODE_BOX_OUTLINE_COLOR": "#fdf6e3", "NODE_BOX_OUTLINE_COLOR": "#fdf6e3", // Base3
"DEFAULT_SHADOW_COLOR": "rgba(0,0,0,0.5)", "DEFAULT_SHADOW_COLOR": "rgba(0,0,0,0.5)",
"DEFAULT_GROUP_FONT": 24, "DEFAULT_GROUP_FONT": 24,
"WIDGET_BGCOLOR": "#002b36", "WIDGET_BGCOLOR": "#002b36", // Base03
"WIDGET_OUTLINE_COLOR": "#839496", "WIDGET_OUTLINE_COLOR": "#839496", // Base0
"WIDGET_TEXT_COLOR": "#fdf6e3", "WIDGET_TEXT_COLOR": "#fdf6e3", // Base3
"WIDGET_SECONDARY_TEXT_COLOR": "#93a1a1", "WIDGET_SECONDARY_TEXT_COLOR": "#93a1a1", // Base1
"LINK_COLOR": "#2aa198", "LINK_COLOR": "#2aa198", // Solarized Cyan
"EVENT_LINK_COLOR": "#268bd2", "EVENT_LINK_COLOR": "#268bd2", // Solarized Blue
"CONNECTING_LINK_COLOR": "#859900", "CONNECTING_LINK_COLOR": "#859900", // Solarized Green
}, },
"comfy_base": {
"fg-color": "#fdf6e3", // Base3
"bg-color": "#002b36", // Base03
"comfy-menu-bg": "#073642", // Base02
"comfy-input-bg": "#002b36", // Base03
"input-text": "#93a1a1", // Base1
"descrip-text": "#586e75", // Base01
"drag-text": "#839496", // Base0
"error-text": "#dc322f", // Solarized Red
"border-color": "#657b83" // Base00
}
}, },
} }
}; };
const id = "Comfy.ColorPalette"; const id = "Comfy.ColorPalette";
const idCustomColorPalettes = "Comfy.CustomColorPalettes"; const idCustomColorPalettes = "Comfy.CustomColorPalettes";
const defaultColorPaletteId = "palette_1"; const defaultColorPaletteId = "dark";
const els = {} const els = {}
// const ctxMenu = LiteGraph.ContextMenu; // const ctxMenu = LiteGraph.ContextMenu;
app.registerExtension({ app.registerExtension({
@ -236,10 +311,12 @@ app.registerExtension({
const loadColorPalette = async (colorPalette) => { const loadColorPalette = async (colorPalette) => {
colorPalette = await completeColorPalette(colorPalette); colorPalette = await completeColorPalette(colorPalette);
if (colorPalette.colors) { if (colorPalette.colors) {
// Sets the colors of node slots and links
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);
Object.assign(LGraphCanvas.link_type_colors, colorPalette.colors.node_slot); Object.assign(LGraphCanvas.link_type_colors, colorPalette.colors.node_slot);
} }
// Sets the colors of the LiteGraph objects
if (colorPalette.colors.litegraph_base) { if (colorPalette.colors.litegraph_base) {
// Everything updates correctly in the loop, except the Node Title and Link Color for some reason // 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.node_title_color = colorPalette.colors.litegraph_base.NODE_TITLE_COLOR;
@ -251,6 +328,13 @@ app.registerExtension({
} }
} }
} }
// Sets the color of ComfyUI elements
if (colorPalette.colors.comfy_base) {
const rootStyle = document.documentElement.style;
for (const key in colorPalette.colors.comfy_base) {
rootStyle.setProperty('--' + key, colorPalette.colors.comfy_base[key]);
}
}
app.canvas.draw(true, true); app.canvas.draw(true, true);
} }
}; };

View File

@ -90,7 +90,7 @@ app.registerExtension({
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined; const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
if (!this.properties || !("Node name for S&R" in this.properties)) { if (!this.properties || !("Node name for S&R" in this.properties)) {
this.addProperty("Node name for S&R", this.title, "string"); this.addProperty("Node name for S&R", this.constructor.type, "string");
} }
return r; return r;

View File

@ -1,4 +1,4 @@
import { ComfyWidgets, addRandomizeWidget } from "/scripts/widgets.js"; import { ComfyWidgets, addValueControlWidget } from "/scripts/widgets.js";
import { app } from "/scripts/app.js"; import { app } from "/scripts/app.js";
const CONVERTED_TYPE = "converted-widget"; const CONVERTED_TYPE = "converted-widget";
@ -23,7 +23,7 @@ function hideWidget(node, widget, suffix = "") {
return widget.origSerializeValue ? widget.origSerializeValue() : widget.value; return widget.origSerializeValue ? widget.origSerializeValue() : widget.value;
}; };
// Hide any linked widgets, e.g. seed+randomize // Hide any linked widgets, e.g. seed+seedControl
if (widget.linkedWidgets) { if (widget.linkedWidgets) {
for (const w of widget.linkedWidgets) { for (const w of widget.linkedWidgets) {
hideWidget(node, w, ":" + widget.name); hideWidget(node, w, ":" + widget.name);
@ -40,7 +40,7 @@ function showWidget(widget) {
delete widget.origComputeSize; delete widget.origComputeSize;
delete widget.origSerializeValue; delete widget.origSerializeValue;
// Hide any linked widgets, e.g. seed+randomize // Hide any linked widgets, e.g. seed+seedControl
if (widget.linkedWidgets) { if (widget.linkedWidgets) {
for (const w of widget.linkedWidgets) { for (const w of widget.linkedWidgets) {
showWidget(w); showWidget(w);
@ -285,7 +285,7 @@ app.registerExtension({
} }
if (widget.type === "number") { if (widget.type === "number") {
addRandomizeWidget(this, widget, "Random after every gen"); addValueControlWidget(this, widget, "fixed");
} }
// When our value changes, update other widgets to reflect our changes // When our value changes, update other widgets to reflect our changes

View File

@ -142,6 +142,8 @@
pointerevents_method: "pointer", // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now) pointerevents_method: "pointer", // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
// TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary) // TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary)
ctrl_shift_v_paste_connect_unselected_outputs: true, //[true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes
/** /**
* Register a node class so it can be listed when the user wants to create a new one * Register a node class so it can be listed when the user wants to create a new one
* @method registerNodeType * @method registerNodeType
@ -253,12 +255,17 @@
* @param {String|Object} type name of the node or the node constructor itself * @param {String|Object} type name of the node or the node constructor itself
*/ */
unregisterNodeType: function(type) { unregisterNodeType: function(type) {
var base_class = type.constructor === String ? this.registered_node_types[type] : type; const base_class =
if(!base_class) type.constructor === String
throw("node type not found: " + type ); ? this.registered_node_types[type]
: type;
if (!base_class) {
throw "node type not found: " + type;
}
delete this.registered_node_types[base_class.type]; delete this.registered_node_types[base_class.type];
if(base_class.constructor.name) if (base_class.constructor.name) {
delete this.Nodes[base_class.constructor.name]; delete this.Nodes[base_class.constructor.name];
}
}, },
/** /**
@ -267,38 +274,49 @@
* @param {String|Object} type name of the node or the node constructor itself * @param {String|Object} type name of the node or the node constructor itself
* @param {String} slot_type name of the slot type (variable type), eg. string, number, array, boolean, .. * @param {String} slot_type name of the slot type (variable type), eg. string, number, array, boolean, ..
*/ */
registerNodeAndSlotType: function(type,slot_type,out){ registerNodeAndSlotType: function(type, slot_type, out){
out = out || false; out = out || false;
var base_class = type.constructor === String && this.registered_node_types[type] !== "anonymous" ? this.registered_node_types[type] : type; const base_class =
type.constructor === String &&
this.registered_node_types[type] !== "anonymous"
? this.registered_node_types[type]
: type;
var sCN = base_class.constructor.type; const class_type = base_class.constructor.type;
if (typeof slot_type == "string"){ let allTypes = [];
var aTypes = slot_type.split(","); if (typeof slot_type === "string") {
}else if (slot_type == this.EVENT || slot_type == this.ACTION){ allTypes = slot_type.split(",");
var aTypes = ["_event_"]; } else if (slot_type == this.EVENT || slot_type == this.ACTION) {
}else{ allTypes = ["_event_"];
var aTypes = ["*"]; } else {
allTypes = ["*"];
} }
for (var i = 0; i < aTypes.length; ++i) { for (let i = 0; i < allTypes.length; ++i) {
var sT = aTypes[i]; //.toLowerCase(); let slotType = allTypes[i];
if (sT === ""){ if (slotType === "") {
sT = "*"; slotType = "*";
}
const registerTo = out
? "registered_slot_out_types"
: "registered_slot_in_types";
if (this[registerTo][slotType] === undefined) {
this[registerTo][slotType] = { nodes: [] };
}
if (!this[registerTo][slotType].nodes.includes(class_type)) {
this[registerTo][slotType].nodes.push(class_type);
} }
var registerTo = out ? "registered_slot_out_types" : "registered_slot_in_types";
if (typeof this[registerTo][sT] == "undefined") this[registerTo][sT] = {nodes: []};
this[registerTo][sT].nodes.push(sCN);
// check if is a new type // check if is a new type
if (!out){ if (!out) {
if (!this.slot_types_in.includes(sT.toLowerCase())){ if (!this.slot_types_in.includes(slotType.toLowerCase())) {
this.slot_types_in.push(sT.toLowerCase()); this.slot_types_in.push(slotType.toLowerCase());
this.slot_types_in.sort(); this.slot_types_in.sort();
} }
}else{ } else {
if (!this.slot_types_out.includes(sT.toLowerCase())){ if (!this.slot_types_out.includes(slotType.toLowerCase())) {
this.slot_types_out.push(sT.toLowerCase()); this.slot_types_out.push(slotType.toLowerCase());
this.slot_types_out.sort(); this.slot_types_out.sort();
} }
} }
@ -1616,7 +1634,8 @@
var nRet = null; var nRet = null;
for (var i = nodes_list.length - 1; i >= 0; i--) { for (var i = nodes_list.length - 1; i >= 0; i--) {
var n = nodes_list[i]; var n = nodes_list[i];
if (n.isPointInside(x, y, margin)) { var skip_title = n.constructor.title_mode == LiteGraph.NO_TITLE;
if (n.isPointInside(x, y, margin, skip_title)) {
// check for lesser interest nodes (TODO check for overlapping, use the top) // check for lesser interest nodes (TODO check for overlapping, use the top)
/*if (typeof n == "LGraphGroup"){ /*if (typeof n == "LGraphGroup"){
nRet = n; nRet = n;
@ -3967,8 +3986,8 @@
var aSource = (type+"").toLowerCase().split(","); var aSource = (type+"").toLowerCase().split(",");
var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type; var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type;
aDest = (aDest+"").toLowerCase().split(","); aDest = (aDest+"").toLowerCase().split(",");
for(sI=0;sI<aSource.length;sI++){ for(var sI=0;sI<aSource.length;sI++){
for(dI=0;dI<aDest.length;dI++){ for(var dI=0;dI<aDest.length;dI++){
if (aSource[sI]=="_event_") aSource[sI] = LiteGraph.EVENT; if (aSource[sI]=="_event_") aSource[sI] = LiteGraph.EVENT;
if (aDest[sI]=="_event_") aDest[sI] = LiteGraph.EVENT; if (aDest[sI]=="_event_") aDest[sI] = LiteGraph.EVENT;
if (aSource[sI]=="*") aSource[sI] = 0; if (aSource[sI]=="*") aSource[sI] = 0;
@ -3987,8 +4006,8 @@
var aSource = (type+"").toLowerCase().split(","); var aSource = (type+"").toLowerCase().split(",");
var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type; var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type;
aDest = (aDest+"").toLowerCase().split(","); aDest = (aDest+"").toLowerCase().split(",");
for(sI=0;sI<aSource.length;sI++){ for(var sI=0;sI<aSource.length;sI++){
for(dI=0;dI<aDest.length;dI++){ for(var dI=0;dI<aDest.length;dI++){
if (aSource[sI]=="*") aSource[sI] = 0; if (aSource[sI]=="*") aSource[sI] = 0;
if (aDest[sI]=="*") aDest[sI] = 0; if (aDest[sI]=="*") aDest[sI] = 0;
if (aSource[sI] == aDest[dI]) { if (aSource[sI] == aDest[dI]) {
@ -4019,7 +4038,7 @@
if (target_node && target_node.constructor === Number) { if (target_node && target_node.constructor === Number) {
target_node = this.graph.getNodeById(target_node); target_node = this.graph.getNodeById(target_node);
} }
target_slot = target_node.findInputSlotByType(target_slotType, false, true); var target_slot = target_node.findInputSlotByType(target_slotType, false, true);
if (target_slot >= 0 && target_slot !== null){ if (target_slot >= 0 && target_slot !== null){
//console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) //console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot)
return this.connect(slot, target_node, target_slot); return this.connect(slot, target_node, target_slot);
@ -4072,7 +4091,7 @@
if (source_node && source_node.constructor === Number) { if (source_node && source_node.constructor === Number) {
source_node = this.graph.getNodeById(source_node); source_node = this.graph.getNodeById(source_node);
} }
source_slot = source_node.findOutputSlotByType(source_slotType, false, true); var source_slot = source_node.findOutputSlotByType(source_slotType, false, true);
if (source_slot >= 0 && source_slot !== null){ if (source_slot >= 0 && source_slot !== null){
//console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) //console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot)
return source_node.connect(source_slot, this, slot); return source_node.connect(source_slot, this, slot);
@ -5184,6 +5203,7 @@ LGraphNode.prototype.executeAction = function(action)
this.editor_alpha = 1; //used for transition this.editor_alpha = 1; //used for transition
this.pause_rendering = false; this.pause_rendering = false;
this.clear_background = true; this.clear_background = true;
this.clear_background_color = "#222";
this.read_only = false; //if set to true users cannot modify the graph this.read_only = false; //if set to true users cannot modify the graph
this.render_only_selected = true; this.render_only_selected = true;
@ -6986,7 +7006,7 @@ LGraphNode.prototype.executeAction = function(action)
block_default = true; block_default = true;
} }
if (e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { if ((e.keyCode === 67) && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
//copy //copy
if (this.selected_nodes) { if (this.selected_nodes) {
this.copyToClipboard(); this.copyToClipboard();
@ -6994,9 +7014,9 @@ LGraphNode.prototype.executeAction = function(action)
} }
} }
if (e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey) { if ((e.keyCode === 86) && (e.metaKey || e.ctrlKey)) {
//paste //paste
this.pasteFromClipboard(); this.pasteFromClipboard(e.shiftKey);
} }
//delete or backspace //delete or backspace
@ -7081,15 +7101,15 @@ LGraphNode.prototype.executeAction = function(action)
var target_node = this.graph.getNodeById( var target_node = this.graph.getNodeById(
link_info.origin_id link_info.origin_id
); );
if (!target_node || !this.selected_nodes[target_node.id]) { if (!target_node) {
//improve this by allowing connections to non-selected nodes
continue; continue;
} //not selected }
clipboard_info.links.push([ clipboard_info.links.push([
target_node._relative_id, target_node._relative_id,
link_info.origin_slot, //j, link_info.origin_slot, //j,
node._relative_id, node._relative_id,
link_info.target_slot link_info.target_slot,
target_node.id
]); ]);
} }
} }
@ -7100,7 +7120,11 @@ LGraphNode.prototype.executeAction = function(action)
); );
}; };
LGraphCanvas.prototype.pasteFromClipboard = function() { LGraphCanvas.prototype.pasteFromClipboard = function(isConnectUnselected = false) {
// if ctrl + shift + v is off, return when isConnectUnselected is true (shift is pressed) to maintain old behavior
if (!LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs && isConnectUnselected) {
return;
}
var data = localStorage.getItem("litegrapheditor_clipboard"); var data = localStorage.getItem("litegrapheditor_clipboard");
if (!data) { if (!data) {
return; return;
@ -7149,7 +7173,16 @@ LGraphNode.prototype.executeAction = function(action)
//create links //create links
for (var i = 0; i < clipboard_info.links.length; ++i) { for (var i = 0; i < clipboard_info.links.length; ++i) {
var link_info = clipboard_info.links[i]; var link_info = clipboard_info.links[i];
var origin_node = nodes[link_info[0]]; var origin_node;
var origin_node_relative_id = link_info[0];
if (origin_node_relative_id != null) {
origin_node = nodes[origin_node_relative_id];
} else if (LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs && isConnectUnselected) {
var origin_node_id = link_info[4];
if (origin_node_id) {
origin_node = this.graph.getNodeById(origin_node_id);
}
}
var target_node = nodes[link_info[2]]; var target_node = nodes[link_info[2]];
if( origin_node && target_node ) if( origin_node && target_node )
origin_node.connect(link_info[1], target_node, link_info[3]); origin_node.connect(link_info[1], target_node, link_info[3]);
@ -8212,6 +8245,17 @@ LGraphNode.prototype.executeAction = function(action)
this.ds.toCanvasContext(ctx); this.ds.toCanvasContext(ctx);
//render BG //render BG
if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color )
{
ctx.fillStyle = this.clear_background_color;
ctx.fillRect(
this.visible_area[0],
this.visible_area[1],
this.visible_area[2],
this.visible_area[3]
);
}
if ( if (
this.background_image && this.background_image &&
this.ds.scale > 0.5 && this.ds.scale > 0.5 &&
@ -12274,7 +12318,7 @@ LGraphNode.prototype.executeAction = function(action)
var aProps = LiteGraph.availableCanvasOptions; var aProps = LiteGraph.availableCanvasOptions;
aProps.sort(); aProps.sort();
for(pI in aProps){ for(var pI in aProps){
var pX = aProps[pI]; var pX = aProps[pI];
panel.addWidget( "boolean", pX, graphcanvas[pX], {key: pX, on: "True", off: "False"}, fUpdate); panel.addWidget( "boolean", pX, graphcanvas[pX], {key: pX, on: "True", off: "False"}, fUpdate);
} }

View File

@ -362,8 +362,20 @@ class ComfyApp {
if (n && n.onDragDrop && (await n.onDragDrop(event))) { if (n && n.onDragDrop && (await n.onDragDrop(event))) {
return; return;
} }
// Dragging from Chrome->Firefox there is a file but its a bmp, so ignore that
if (event.dataTransfer.files.length && event.dataTransfer.files[0].type !== "image/bmp") {
await this.handleFile(event.dataTransfer.files[0]); await this.handleFile(event.dataTransfer.files[0]);
} else {
// Try loading the first URI in the transfer list
const validTypes = ["text/uri-list", "text/x-moz-url"];
const match = [...event.dataTransfer.types].find((t) => validTypes.find(v => t === v));
if (match) {
const uri = event.dataTransfer.getData(match)?.split("\n")?.[0];
if (uri) {
await this.handleFile(await (await fetch(uri)).blob());
}
}
}
}); });
// Always clear over node on drag leave // Always clear over node on drag leave
@ -937,6 +949,11 @@ class ComfyApp {
widget.value = widget.value.slice(7); widget.value = widget.value.slice(7);
} }
} }
if (widget.name == "control_after_generate") {
if (widget.value == true) {
widget.value = "randomize";
}
}
} }
} }
} }
@ -1090,7 +1107,7 @@ class ComfyApp {
importA1111(this.graph, pngInfo.parameters); importA1111(this.graph, pngInfo.parameters);
} }
} }
} else if (file.type === "application/json" || file.name.endsWith(".json")) { } else if (file.type === "application/json" || file.name?.endsWith(".json")) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = () => { reader.onload = () => {
this.loadGraphData(JSON.parse(reader.result)); this.loadGraphData(JSON.parse(reader.result));

View File

@ -10,37 +10,54 @@ function getNumberDefaults(inputData, defaultStep) {
return { val: defaultVal, config: { min, max, step: 10.0 * step } }; return { val: defaultVal, config: { min, max, step: 10.0 * step } };
} }
export function addRandomizeWidget(node, targetWidget, name, defaultValue = false) { export function addValueControlWidget(node, targetWidget, defaultValue = "randomize", values) {
const randomize = node.addWidget("toggle", name, defaultValue, function (v) {}, { const valueControl = node.addWidget("combo", "control_after_generate", defaultValue, function (v) { }, {
on: "enabled", values: ["fixed", "increment", "decrement", "randomize"],
off: "disabled",
serialize: false, // Don't include this in prompt. serialize: false, // Don't include this in prompt.
}); });
valueControl.afterQueued = () => {
randomize.afterQueued = () => { var v = valueControl.value;
if (randomize.value) {
const min = targetWidget.options?.min; let min = targetWidget.options.min;
let max = targetWidget.options?.max; let max = targetWidget.options.max;
if (min != null || max != null) { // limit to something that javascript can handle
if (max) {
// limit max to something that javascript can handle
max = Math.min(1125899906842624, max); max = Math.min(1125899906842624, max);
min = Math.max(-1125899906842624, min);
let range = (max - min) / (targetWidget.options.step / 10);
//adjust values based on valueControl Behaviour
switch (v) {
case "fixed":
break;
case "increment":
targetWidget.value += targetWidget.options.step / 10;
break;
case "decrement":
targetWidget.value -= targetWidget.options.step / 10;
break;
case "randomize":
targetWidget.value = Math.floor(Math.random() * range) * (targetWidget.options.step / 10) + min;
default:
break;
} }
targetWidget.value = Math.floor(Math.random() * ((max ?? 9999999999) - (min ?? 0) + 1) + (min ?? 0)); /*check if values are over or under their respective
} else { * ranges and set them to min or max.*/
targetWidget.value = Math.floor(Math.random() * 1125899906842624); if (targetWidget.value < min)
targetWidget.value = min;
if (targetWidget.value > max)
targetWidget.value = max;
} }
} return valueControl;
}; };
return randomize;
}
function seedWidget(node, inputName, inputData) { function seedWidget(node, inputName, inputData) {
const seed = ComfyWidgets.INT(node, inputName, inputData); const seed = ComfyWidgets.INT(node, inputName, inputData);
const randomize = addRandomizeWidget(node, seed.widget, "Random seed after every gen", true); const seedControl = addValueControlWidget(node, seed.widget, "randomize");
seed.widget.linkedWidgets = [randomize]; seed.widget.linkedWidgets = [seedControl];
return { widget: seed, randomize }; return seed;
} }
const MultilineSymbol = Symbol(); const MultilineSymbol = Symbol();

View File

@ -1,6 +1,13 @@
:root { :root {
--fg-color: #000; --fg-color: #000;
--bg-color: #fff; --bg-color: #fff;
--comfy-menu-bg: #353535;
--comfy-input-bg: #222;
--input-text: #ddd;
--descrip-text: #999;
--drag-text: #ccc;
--error-text: #ff4444;
--border-color: #4e4e4e;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@ -25,8 +32,8 @@ body {
} }
.comfy-multiline-input { .comfy-multiline-input {
background-color: var(--bg-color); background-color: var(--comfy-input-bg);
color: var(--fg-color); color: var(--input-text);
overflow: hidden; overflow: hidden;
overflow-y: auto; overflow-y: auto;
padding: 2px; padding: 2px;
@ -39,8 +46,8 @@ body {
position: fixed; /* Stay in place */ position: fixed; /* Stay in place */
z-index: 100; /* Sit on top */ z-index: 100; /* Sit on top */
padding: 30px 30px 10px 30px; padding: 30px 30px 10px 30px;
background-color: #353535; /* Modal background */ background-color: var(--comfy-menu-bg); /* Modal background */
color: #ff4444; color: var(--error-text);
box-shadow: 0px 0px 20px #888888; box-shadow: 0px 0px 20px #888888;
border-radius: 10px; border-radius: 10px;
top: 50%; top: 50%;
@ -82,8 +89,8 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
color: #999; color: var(--descrip-text);
background-color: #353535; background-color: var(--comfy-menu-bg);
font-family: sans-serif; font-family: sans-serif;
padding: 10px; padding: 10px;
border-radius: 0 8px 8px 8px; border-radius: 0 8px 8px 8px;
@ -103,7 +110,7 @@ body {
.comfy-menu-btns button { .comfy-menu-btns button {
font-size: 10px; font-size: 10px;
width: 50%; width: 50%;
color: #999 !important; color: var(--descrip-text) !important;
} }
.comfy-menu > button { .comfy-menu > button {
@ -114,10 +121,10 @@ body {
.comfy-menu-btns button, .comfy-menu-btns button,
.comfy-menu .comfy-list button, .comfy-menu .comfy-list button,
.comfy-modal button{ .comfy-modal button{
color: #ddd; color: var(--input-text);
background-color: #222; background-color: var(--comfy-input-bg);
border-radius: 8px; border-radius: 8px;
border-color: #4e4e4e; border-color: var(--border-color);
border-style: solid; border-style: solid;
margin-top: 2px; margin-top: 2px;
} }
@ -136,7 +143,7 @@ body {
font-size: 12px; font-size: 12px;
font-family: sans-serif; font-family: sans-serif;
letter-spacing: 2px; letter-spacing: 2px;
color: #cccccc; color: var(--drag-text);
text-shadow: 1px 0 1px black; text-shadow: 1px 0 1px black;
position: absolute; position: absolute;
top: 0; top: 0;
@ -152,7 +159,7 @@ body {
} }
.comfy-list { .comfy-list {
color: #999; color: var(--descrip-text);
background-color: #333; background-color: #333;
margin-bottom: 10px; margin-bottom: 10px;
border-color: #4e4e4e; border-color: #4e4e4e;
@ -163,7 +170,7 @@ body {
overflow-y: scroll; overflow-y: scroll;
max-height: 100px; max-height: 100px;
min-height: 25px; min-height: 25px;
background-color: #222; background-color: var(--comfy-input-bg);
padding: 5px; padding: 5px;
} }
@ -206,16 +213,16 @@ button.comfy-queue-btn {
.comfy-modal.comfy-manage-templates { .comfy-modal.comfy-manage-templates {
text-align: center; text-align: center;
font-family: sans-serif; font-family: sans-serif;
color: #999; color: var(--descrip-text);
z-index: 99; z-index: 99;
} }
.comfy-modal input, .comfy-modal input,
.comfy-modal select { .comfy-modal select {
color: #ddd; color: var(--input-text);
background-color: #222; background-color: var(--comfy-input-bg);
border-radius: 8px; border-radius: 8px;
border-color: #4e4e4e; border-color: var(--border-color);
border-style: solid; border-style: solid;
font-size: inherit; font-size: inherit;
} }
@ -240,7 +247,7 @@ button.comfy-queue-btn {
.graphdialog .name { .graphdialog .name {
font-size: 14px; font-size: 14px;
font-family: sans-serif; font-family: sans-serif;
color: #999999; color: var(--descrip-text);
} }
.graphdialog button { .graphdialog button {
@ -251,10 +258,10 @@ button.comfy-queue-btn {
} }
.graphdialog input, .graphdialog textarea, .graphdialog select { .graphdialog input, .graphdialog textarea, .graphdialog select {
background-color: #222; background-color: var(--comfy-input-bg);
border: 2px solid; border: 2px solid;
border-color: #444444; border-color: var(--border-color);
color: #ddd; color: var(--input-text);
border-radius: 12px 0 0 12px; border-radius: 12px 0 0 12px;
} }