mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-10 13:32:36 +08:00
Initial changes for litegraph.ts compat
This commit is contained in:
parent
d7b3b0f8c1
commit
c13f148df9
@ -1,5 +1,6 @@
|
|||||||
import {app} from "../../scripts/app.js";
|
import {app} from "../../scripts/app.js";
|
||||||
import {$el} from "../../scripts/ui.js";
|
import {$el} from "../../scripts/ui.js";
|
||||||
|
import { LiteGraph, LGraphCanvas } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Manage color palettes
|
// Manage color palettes
|
||||||
|
|
||||||
@ -341,8 +342,8 @@ app.registerExtension({
|
|||||||
if (colorPalette.colors) {
|
if (colorPalette.colors) {
|
||||||
// Sets the colors of node slots and links
|
// 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(LGraphCanvas.DEFAULT_CONNECTION_COLORS_BY_TYPE, colorPalette.colors.node_slot);
|
||||||
Object.assign(LGraphCanvas.link_type_colors, colorPalette.colors.node_slot);
|
Object.assign(app.canvas.link_type_colors, colorPalette.colors.node_slot);
|
||||||
}
|
}
|
||||||
// Sets the colors of the LiteGraph objects
|
// Sets the colors of the LiteGraph objects
|
||||||
if (colorPalette.colors.litegraph_base) {
|
if (colorPalette.colors.litegraph_base) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {app} from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Adds filtering to combo context menus
|
// Adds filtering to combo context menus
|
||||||
|
|
||||||
@ -141,7 +142,7 @@ const ext = {
|
|||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
LiteGraph.ContextMenu.prototype = ctxMenu.prototype;
|
// LiteGraph.ContextMenu.prototype = ctxMenu.prototype;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Inverts the scrolling of context menus
|
// Inverts the scrolling of context menus
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { LiteGraph, LINK_RENDER_MODE_NAMES } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
const id = "Comfy.LinkRenderMode";
|
const id = "Comfy.LinkRenderMode";
|
||||||
const ext = {
|
const ext = {
|
||||||
@ -9,7 +10,7 @@ const ext = {
|
|||||||
name: "Link Render Mode",
|
name: "Link Render Mode",
|
||||||
defaultValue: 2,
|
defaultValue: 2,
|
||||||
type: "combo",
|
type: "combo",
|
||||||
options: LiteGraph.LINK_RENDER_MODES.map((m, i) => ({
|
options: LINK_RENDER_MODE_NAMES.map((m, i) => ({
|
||||||
value: i,
|
value: i,
|
||||||
text: m,
|
text: m,
|
||||||
selected: i == app.canvas.links_render_mode,
|
selected: i == app.canvas.links_render_mode,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||||
|
import { LGraphCanvas } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Adds the ability to save and add multiple nodes as a template
|
// Adds the ability to save and add multiple nodes as a template
|
||||||
// To save:
|
// To save:
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import {app} from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import {ComfyWidgets} from "../../scripts/widgets.js";
|
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||||
|
import { ComfyGraphNode } from "../../scripts/graphNode.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Node that add notes to your project
|
// Node that add notes to your project
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.NoteNode",
|
name: "Comfy.NoteNode",
|
||||||
registerCustomNodes() {
|
registerCustomNodes() {
|
||||||
class NoteNode {
|
class NoteNode extends ComfyGraphNode {
|
||||||
color=LGraphCanvas.node_colors.yellow.color;
|
color=LGraphCanvas.node_colors.yellow.color;
|
||||||
bgcolor=LGraphCanvas.node_colors.yellow.bgcolor;
|
bgcolor=LGraphCanvas.node_colors.yellow.bgcolor;
|
||||||
groupcolor = LGraphCanvas.node_colors.yellow.groupcolor;
|
groupcolor = LGraphCanvas.node_colors.yellow.groupcolor;
|
||||||
@ -19,22 +22,18 @@ app.registerExtension({
|
|||||||
|
|
||||||
this.serialize_widgets = true;
|
this.serialize_widgets = true;
|
||||||
this.isVirtualNode = true;
|
this.isVirtualNode = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load default visibility
|
// Load default visibility
|
||||||
|
|
||||||
LiteGraph.registerNodeType(
|
LiteGraph.registerNodeType({
|
||||||
"Note",
|
class: NoteNode,
|
||||||
Object.assign(NoteNode, {
|
|
||||||
title_mode: LiteGraph.NORMAL_TITLE,
|
title_mode: LiteGraph.NORMAL_TITLE,
|
||||||
|
type: "Note",
|
||||||
title: "Note",
|
title: "Note",
|
||||||
collapsable: true,
|
collapsable: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
NoteNode.category = "utils";
|
NoteNode.category = "utils";
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { ComfyGraphNode } from "../../scripts/graphNode.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Node that allows you to redirect connections for cleaner graphs
|
// Node that allows you to redirect connections for cleaner graphs
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.RerouteNode",
|
name: "Comfy.RerouteNode",
|
||||||
registerCustomNodes() {
|
registerCustomNodes() {
|
||||||
class RerouteNode {
|
class RerouteNode extends ComfyGraphNode {
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!this.properties) {
|
if (!this.properties) {
|
||||||
this.properties = {};
|
this.properties = {};
|
||||||
@ -109,7 +111,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const displayType = inputType || outputType || "*";
|
const displayType = inputType || outputType || "*";
|
||||||
const color = LGraphCanvas.link_type_colors[displayType];
|
const color = app.canvas.link_type_colors[displayType];
|
||||||
|
|
||||||
// Update the types of each node
|
// Update the types of each node
|
||||||
for (const node of updateNodes) {
|
for (const node of updateNodes) {
|
||||||
@ -219,14 +221,13 @@ app.registerExtension({
|
|||||||
// Load default visibility
|
// Load default visibility
|
||||||
RerouteNode.setDefaultTextVisibility(!!localStorage["Comfy.RerouteNode.DefaultVisibility"]);
|
RerouteNode.setDefaultTextVisibility(!!localStorage["Comfy.RerouteNode.DefaultVisibility"]);
|
||||||
|
|
||||||
LiteGraph.registerNodeType(
|
LiteGraph.registerNodeType({
|
||||||
"Reroute",
|
class: RerouteNode,
|
||||||
Object.assign(RerouteNode, {
|
|
||||||
title_mode: LiteGraph.NO_TITLE,
|
title_mode: LiteGraph.NO_TITLE,
|
||||||
|
type: "Reroute",
|
||||||
title: "Reroute",
|
title: "Reroute",
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
RerouteNode.category = "utils";
|
RerouteNode.category = "utils";
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { app } from "../../scripts/app.js";
|
|||||||
|
|
||||||
// Use widget values and dates in output filenames
|
// Use widget values and dates in output filenames
|
||||||
|
|
||||||
|
/*
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.SaveImageExtraOutput",
|
name: "Comfy.SaveImageExtraOutput",
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
@ -98,3 +99,4 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Adds defaults for quickly adding nodes with middle click on the input/output
|
// Adds defaults for quickly adding nodes with middle click on the input/output
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
// Shift + drag/resize to snap to grid
|
// Shift + drag/resize to snap to grid
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { ComfyWidgets, addValueControlWidget } from "../../scripts/widgets.js";
|
import { ComfyWidgets, addValueControlWidget } from "../../scripts/widgets.js";
|
||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
|
import { LiteGraph } from "../../lib/litegraph.core.js"
|
||||||
|
|
||||||
const CONVERTED_TYPE = "converted-widget";
|
const CONVERTED_TYPE = "converted-widget";
|
||||||
const VALID_TYPES = ["STRING", "combo", "number", "BOOLEAN"];
|
const VALID_TYPES = ["STRING", "combo", "number", "BOOLEAN"];
|
||||||
@ -95,8 +96,8 @@ app.registerExtension({
|
|||||||
name: "Comfy.WidgetInputs",
|
name: "Comfy.WidgetInputs",
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
// Add menu options to conver to/from widgets
|
// Add menu options to conver to/from widgets
|
||||||
const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
const origGetExtraMenuOptions = nodeType.class.prototype.getExtraMenuOptions;
|
||||||
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
nodeType.class.prototype.getExtraMenuOptions = function (_, options) {
|
||||||
const r = origGetExtraMenuOptions ? origGetExtraMenuOptions.apply(this, arguments) : undefined;
|
const r = origGetExtraMenuOptions ? origGetExtraMenuOptions.apply(this, arguments) : undefined;
|
||||||
|
|
||||||
if (this.widgets) {
|
if (this.widgets) {
|
||||||
@ -131,8 +132,8 @@ app.registerExtension({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// On initial configure of nodes hide all converted widgets
|
// On initial configure of nodes hide all converted widgets
|
||||||
const origOnConfigure = nodeType.prototype.onConfigure;
|
const origOnConfigure = nodeType.class.prototype.onConfigure;
|
||||||
nodeType.prototype.onConfigure = function () {
|
nodeType.class.prototype.onConfigure = function () {
|
||||||
const r = origOnConfigure ? origOnConfigure.apply(this, arguments) : undefined;
|
const r = origOnConfigure ? origOnConfigure.apply(this, arguments) : undefined;
|
||||||
|
|
||||||
if (this.inputs) {
|
if (this.inputs) {
|
||||||
@ -161,9 +162,9 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Double click a widget input to automatically attach a primitive
|
// Double click a widget input to automatically attach a primitive
|
||||||
const origOnInputDblClick = nodeType.prototype.onInputDblClick;
|
const origOnInputDblClick = nodeType.class.prototype.onInputDblClick;
|
||||||
const ignoreDblClick = Symbol();
|
const ignoreDblClick = Symbol();
|
||||||
nodeType.prototype.onInputDblClick = function (slot) {
|
nodeType.class.prototype.onInputDblClick = function (slot) {
|
||||||
const r = origOnInputDblClick ? origOnInputDblClick.apply(this, arguments) : undefined;
|
const r = origOnInputDblClick ? origOnInputDblClick.apply(this, arguments) : undefined;
|
||||||
|
|
||||||
const input = this.inputs[slot];
|
const input = this.inputs[slot];
|
||||||
@ -403,12 +404,11 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LiteGraph.registerNodeType(
|
LiteGraph.registerNodeType({
|
||||||
"PrimitiveNode",
|
class: PrimitiveNode,
|
||||||
Object.assign(PrimitiveNode, {
|
type: "PrimitiveNode",
|
||||||
title: "Primitive",
|
title: "Primitive",
|
||||||
})
|
});
|
||||||
);
|
|
||||||
PrimitiveNode.category = "utils";
|
PrimitiveNode.category = "utils";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,12 +7,10 @@
|
|||||||
<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" />
|
||||||
<link rel="stylesheet" type="text/css" href="./user.css" />
|
<link rel="stylesheet" type="text/css" href="./user.css" />
|
||||||
<script type="text/javascript" src="./lib/litegraph.core.js"></script>
|
|
||||||
<script type="text/javascript" src="./lib/litegraph.extensions.js" defer></script>
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { app } from "./scripts/app.js";
|
import { app } from "./scripts/app.js";
|
||||||
await app.setup();
|
|
||||||
window.app = app;
|
window.app = app;
|
||||||
|
await app.setup();
|
||||||
window.graph = app.graph;
|
window.graph = app.graph;
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Changes the background color of the canvas.
|
|
||||||
*
|
|
||||||
* @method updateBackground
|
|
||||||
* @param {image} String
|
|
||||||
* @param {clearBackgroundColor} String
|
|
||||||
* @
|
|
||||||
*/
|
|
||||||
LGraphCanvas.prototype.updateBackground = function (image, clearBackgroundColor) {
|
|
||||||
this._bg_img = new Image();
|
|
||||||
this._bg_img.name = image;
|
|
||||||
this._bg_img.src = image;
|
|
||||||
this._bg_img.onload = () => {
|
|
||||||
this.draw(true, true);
|
|
||||||
};
|
|
||||||
this.background_image = image;
|
|
||||||
|
|
||||||
this.clear_background = true;
|
|
||||||
this.clear_background_color = clearBackgroundColor;
|
|
||||||
this._pattern = null
|
|
||||||
}
|
|
||||||
@ -4,6 +4,10 @@ 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, getLatentMetadata } from "./pnginfo.js";
|
import { getPngMetadata, importA1111, getLatentMetadata } from "./pnginfo.js";
|
||||||
|
import { LiteGraph } from "../lib/litegraph.core.js"
|
||||||
|
import ComfyGraph from "./graph.js";
|
||||||
|
import ComfyGraphCanvas from "./graphCanvas.js";
|
||||||
|
import { ComfyBackendNode } from "./graphNode.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("types/comfy").ComfyExtension} ComfyExtension
|
* @typedef {import("types/comfy").ComfyExtension} ComfyExtension
|
||||||
@ -690,270 +694,6 @@ export class ComfyApp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle mouse
|
|
||||||
*
|
|
||||||
* Move group by header
|
|
||||||
*/
|
|
||||||
#addProcessMouseHandler() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
const origProcessMouseDown = LGraphCanvas.prototype.processMouseDown;
|
|
||||||
LGraphCanvas.prototype.processMouseDown = function(e) {
|
|
||||||
const res = origProcessMouseDown.apply(this, arguments);
|
|
||||||
|
|
||||||
this.selected_group_moving = false;
|
|
||||||
|
|
||||||
if (this.selected_group && !this.selected_group_resizing) {
|
|
||||||
var font_size =
|
|
||||||
this.selected_group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
|
|
||||||
var height = font_size * 1.4;
|
|
||||||
|
|
||||||
// Move group by header
|
|
||||||
if (LiteGraph.isInsideRectangle(e.canvasX, e.canvasY, this.selected_group.pos[0], this.selected_group.pos[1], this.selected_group.size[0], height)) {
|
|
||||||
this.selected_group_moving = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
const origProcessMouseMove = LGraphCanvas.prototype.processMouseMove;
|
|
||||||
LGraphCanvas.prototype.processMouseMove = function(e) {
|
|
||||||
const orig_selected_group = this.selected_group;
|
|
||||||
|
|
||||||
if (this.selected_group && !this.selected_group_resizing && !this.selected_group_moving) {
|
|
||||||
this.selected_group = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = origProcessMouseMove.apply(this, arguments);
|
|
||||||
|
|
||||||
if (orig_selected_group && !this.selected_group_resizing && !this.selected_group_moving) {
|
|
||||||
this.selected_group = orig_selected_group;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle keypress
|
|
||||||
*
|
|
||||||
* Ctrl + M mute/unmute selected nodes
|
|
||||||
*/
|
|
||||||
#addProcessKeyHandler() {
|
|
||||||
const self = this;
|
|
||||||
const origProcessKey = LGraphCanvas.prototype.processKey;
|
|
||||||
LGraphCanvas.prototype.processKey = function(e) {
|
|
||||||
const res = origProcessKey.apply(this, arguments);
|
|
||||||
|
|
||||||
if (res === false) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.graph) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var block_default = false;
|
|
||||||
|
|
||||||
if (e.target.localName == "input") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.type == "keydown") {
|
|
||||||
// Ctrl + M mute/unmute
|
|
||||||
if (e.keyCode == 77 && e.ctrlKey) {
|
|
||||||
if (this.selected_nodes) {
|
|
||||||
for (var i in this.selected_nodes) {
|
|
||||||
if (this.selected_nodes[i].mode === 2) { // never
|
|
||||||
this.selected_nodes[i].mode = 0; // always
|
|
||||||
} else {
|
|
||||||
this.selected_nodes[i].mode = 2; // never
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block_default = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode == 66 && e.ctrlKey) {
|
|
||||||
if (this.selected_nodes) {
|
|
||||||
for (var i in this.selected_nodes) {
|
|
||||||
if (this.selected_nodes[i].mode === 4) { // never
|
|
||||||
this.selected_nodes[i].mode = 0; // always
|
|
||||||
} else {
|
|
||||||
this.selected_nodes[i].mode = 4; // never
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block_default = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.graph.change();
|
|
||||||
|
|
||||||
if (block_default) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws group header bar
|
|
||||||
*/
|
|
||||||
#addDrawGroupsHandler() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
const origDrawGroups = LGraphCanvas.prototype.drawGroups;
|
|
||||||
LGraphCanvas.prototype.drawGroups = function(canvas, ctx) {
|
|
||||||
if (!this.graph) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var groups = this.graph._groups;
|
|
||||||
|
|
||||||
ctx.save();
|
|
||||||
ctx.globalAlpha = 0.7 * this.editor_alpha;
|
|
||||||
|
|
||||||
for (var i = 0; i < groups.length; ++i) {
|
|
||||||
var group = groups[i];
|
|
||||||
|
|
||||||
if (!LiteGraph.overlapBounding(this.visible_area, group._bounding)) {
|
|
||||||
continue;
|
|
||||||
} //out of the visible area
|
|
||||||
|
|
||||||
ctx.fillStyle = group.color || "#335";
|
|
||||||
ctx.strokeStyle = group.color || "#335";
|
|
||||||
var pos = group._pos;
|
|
||||||
var size = group._size;
|
|
||||||
ctx.globalAlpha = 0.25 * this.editor_alpha;
|
|
||||||
ctx.beginPath();
|
|
||||||
var font_size =
|
|
||||||
group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
|
|
||||||
ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], font_size * 1.4);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.globalAlpha = this.editor_alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
const res = origDrawGroups.apply(this, arguments);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws node highlights (executing, drag drop) and progress bar
|
|
||||||
*/
|
|
||||||
#addDrawNodeHandler() {
|
|
||||||
const origDrawNodeShape = LGraphCanvas.prototype.drawNodeShape;
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
LGraphCanvas.prototype.drawNodeShape = function (node, ctx, size, fgcolor, bgcolor, selected, mouse_over) {
|
|
||||||
const res = origDrawNodeShape.apply(this, arguments);
|
|
||||||
|
|
||||||
const nodeErrors = self.lastNodeErrors?.[node.id];
|
|
||||||
|
|
||||||
let color = null;
|
|
||||||
let lineWidth = 1;
|
|
||||||
if (node.id === +self.runningNodeId) {
|
|
||||||
color = "#0f0";
|
|
||||||
} else if (self.dragOverNode && node.id === self.dragOverNode.id) {
|
|
||||||
color = "dodgerblue";
|
|
||||||
}
|
|
||||||
else if (nodeErrors?.errors) {
|
|
||||||
color = "red";
|
|
||||||
lineWidth = 2;
|
|
||||||
}
|
|
||||||
else if (self.lastExecutionError && +self.lastExecutionError.node_id === node.id) {
|
|
||||||
color = "#f0f";
|
|
||||||
lineWidth = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (color) {
|
|
||||||
const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE;
|
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
ctx.globalAlpha = 0.8;
|
|
||||||
ctx.beginPath();
|
|
||||||
if (shape == LiteGraph.BOX_SHAPE)
|
|
||||||
ctx.rect(-6, -6 - LiteGraph.NODE_TITLE_HEIGHT, 12 + size[0] + 1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT);
|
|
||||||
else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed))
|
|
||||||
ctx.roundRect(
|
|
||||||
-6,
|
|
||||||
-6 - LiteGraph.NODE_TITLE_HEIGHT,
|
|
||||||
12 + size[0] + 1,
|
|
||||||
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
|
||||||
this.round_radius * 2
|
|
||||||
);
|
|
||||||
else if (shape == LiteGraph.CARD_SHAPE)
|
|
||||||
ctx.roundRect(
|
|
||||||
-6,
|
|
||||||
-6 - LiteGraph.NODE_TITLE_HEIGHT,
|
|
||||||
12 + size[0] + 1,
|
|
||||||
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
|
||||||
[this.round_radius * 2, this.round_radius * 2, 2, 2]
|
|
||||||
);
|
|
||||||
else if (shape == LiteGraph.CIRCLE_SHAPE)
|
|
||||||
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI * 2);
|
|
||||||
ctx.strokeStyle = color;
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.strokeStyle = fgcolor;
|
|
||||||
ctx.globalAlpha = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.progress && node.id === +self.runningNodeId) {
|
|
||||||
ctx.fillStyle = "green";
|
|
||||||
ctx.fillRect(0, 0, size[0] * (self.progress.value / self.progress.max), 6);
|
|
||||||
ctx.fillStyle = bgcolor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlight inputs that failed validation
|
|
||||||
if (nodeErrors) {
|
|
||||||
ctx.lineWidth = 2;
|
|
||||||
ctx.strokeStyle = "red";
|
|
||||||
for (const error of nodeErrors.errors) {
|
|
||||||
if (error.extra_info && error.extra_info.input_name) {
|
|
||||||
const inputIndex = node.findInputSlot(error.extra_info.input_name)
|
|
||||||
if (inputIndex !== -1) {
|
|
||||||
let pos = node.getConnectionPos(true, inputIndex);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(pos[0] - node.pos[0], pos[1] - node.pos[1], 12, 0, 2 * Math.PI, false)
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const origDrawNode = LGraphCanvas.prototype.drawNode;
|
|
||||||
LGraphCanvas.prototype.drawNode = function (node, ctx) {
|
|
||||||
var editor_alpha = this.editor_alpha;
|
|
||||||
var old_color = node.bgcolor;
|
|
||||||
|
|
||||||
if (node.mode === 2) { // never
|
|
||||||
this.editor_alpha = 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.mode === 4) { // never
|
|
||||||
node.bgcolor = "#FF00FF";
|
|
||||||
this.editor_alpha = 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = origDrawNode.apply(this, arguments);
|
|
||||||
|
|
||||||
this.editor_alpha = editor_alpha;
|
|
||||||
node.bgcolor = old_color;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles updates from the API socket
|
* Handles updates from the API socket
|
||||||
*/
|
*/
|
||||||
@ -1056,11 +796,8 @@ export class ComfyApp {
|
|||||||
canvasEl.tabIndex = "1";
|
canvasEl.tabIndex = "1";
|
||||||
document.body.prepend(canvasEl);
|
document.body.prepend(canvasEl);
|
||||||
|
|
||||||
this.#addProcessMouseHandler();
|
this.graph = new ComfyGraph();
|
||||||
this.#addProcessKeyHandler();
|
const canvas = (this.canvas = new ComfyGraphCanvas(canvasEl, this.graph));
|
||||||
|
|
||||||
this.graph = new LGraph();
|
|
||||||
const canvas = (this.canvas = new LGraphCanvas(canvasEl, this.graph));
|
|
||||||
this.ctx = canvasEl.getContext("2d");
|
this.ctx = canvasEl.getContext("2d");
|
||||||
|
|
||||||
LiteGraph.release_link_on_empty_shows_menu = true;
|
LiteGraph.release_link_on_empty_shows_menu = true;
|
||||||
@ -1106,8 +843,6 @@ export class ComfyApp {
|
|||||||
// Save current workflow automatically
|
// Save current workflow automatically
|
||||||
setInterval(() => localStorage.setItem("workflow", JSON.stringify(this.graph.serialize())), 1000);
|
setInterval(() => localStorage.setItem("workflow", JSON.stringify(this.graph.serialize())), 1000);
|
||||||
|
|
||||||
this.#addDrawNodeHandler();
|
|
||||||
this.#addDrawGroupsHandler();
|
|
||||||
this.#addApiUpdateHandlers();
|
this.#addApiUpdateHandlers();
|
||||||
this.#addDropHandler();
|
this.#addDropHandler();
|
||||||
this.#addPasteHandler();
|
this.#addPasteHandler();
|
||||||
@ -1130,74 +865,92 @@ export class ComfyApp {
|
|||||||
async registerNodesFromDefs(defs) {
|
async registerNodesFromDefs(defs) {
|
||||||
await this.#invokeExtensionsAsync("addCustomNodeDefs", defs);
|
await this.#invokeExtensionsAsync("addCustomNodeDefs", defs);
|
||||||
|
|
||||||
|
// // Generate list of known widgets
|
||||||
|
// const widgets = Object.assign(
|
||||||
|
// {},
|
||||||
|
// ComfyWidgets,
|
||||||
|
// ...(await this.#invokeExtensionsAsync("getCustomWidgets")).filter(Boolean)
|
||||||
|
// );
|
||||||
|
|
||||||
// Generate list of known widgets
|
// Generate list of known widgets
|
||||||
const widgets = Object.assign(
|
const customWidgets = (await app.#invokeExtensionsAsync("getCustomWidgets")).filter(Boolean);
|
||||||
{},
|
ComfyWidgets.customWidgets = customWidgets;
|
||||||
ComfyWidgets,
|
|
||||||
...(await this.#invokeExtensionsAsync("getCustomWidgets")).filter(Boolean)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Register a node for each definition
|
// Register a node for each definition
|
||||||
for (const nodeId in defs) {
|
for (const nodeId in defs) {
|
||||||
const nodeData = defs[nodeId];
|
const nodeData = defs[nodeId];
|
||||||
const node = Object.assign(
|
|
||||||
function ComfyNode() {
|
|
||||||
var inputs = nodeData["input"]["required"];
|
|
||||||
if (nodeData["input"]["optional"] != undefined){
|
|
||||||
inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"])
|
|
||||||
}
|
|
||||||
const config = { minWidth: 1, minHeight: 1 };
|
|
||||||
for (const inputName in inputs) {
|
|
||||||
const inputData = inputs[inputName];
|
|
||||||
const type = inputData[0];
|
|
||||||
|
|
||||||
if(inputData[1]?.forceInput) {
|
const ctor = class extends ComfyBackendNode {
|
||||||
this.addInput(inputName, type);
|
constructor(title) {
|
||||||
} else {
|
super(title, nodeId, nodeData);
|
||||||
if (Array.isArray(type)) {
|
|
||||||
// Enums
|
|
||||||
Object.assign(config, widgets.COMBO(this, inputName, inputData, app) || {});
|
|
||||||
} else if (`${type}:${inputName}` in widgets) {
|
|
||||||
// Support custom widgets by Type:Name
|
|
||||||
Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData, app) || {});
|
|
||||||
} else if (type in widgets) {
|
|
||||||
// Standard type widgets
|
|
||||||
Object.assign(config, widgets[type](this, inputName, inputData, app) || {});
|
|
||||||
} else {
|
|
||||||
// Node connection inputs
|
|
||||||
this.addInput(inputName, type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const o in nodeData["output"]) {
|
const node = {
|
||||||
const output = nodeData["output"][o];
|
class: ctor,
|
||||||
const outputName = nodeData["output_name"][o] || output;
|
|
||||||
const outputShape = nodeData["output_is_list"][o] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE ;
|
|
||||||
this.addOutput(outputName, output, { shape: outputShape });
|
|
||||||
}
|
|
||||||
|
|
||||||
const s = this.computeSize();
|
|
||||||
s[0] = Math.max(config.minWidth, s[0] * 1.5);
|
|
||||||
s[1] = Math.max(config.minHeight, s[1]);
|
|
||||||
this.size = s;
|
|
||||||
this.serialize_widgets = true;
|
|
||||||
|
|
||||||
app.#invokeExtensionsAsync("nodeCreated", this);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: nodeData.display_name || nodeData.name,
|
title: nodeData.display_name || nodeData.name,
|
||||||
comfyClass: nodeData.name,
|
desc: `ComfyNode: ${nodeId}`
|
||||||
}
|
}
|
||||||
);
|
|
||||||
node.prototype.comfyClass = nodeData.name;
|
|
||||||
|
|
||||||
this.#addNodeContextMenuHandler(node);
|
// const node = Object.assign(
|
||||||
this.#addDrawBackgroundHandler(node, app);
|
// function ComfyNode() {
|
||||||
this.#addNodeKeyHandler(node);
|
// var inputs = nodeData["input"]["required"];
|
||||||
|
// if (nodeData["input"]["optional"] != undefined){
|
||||||
|
// inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"])
|
||||||
|
// }
|
||||||
|
// const config = { minWidth: 1, minHeight: 1 };
|
||||||
|
// for (const inputName in inputs) {
|
||||||
|
// const inputData = inputs[inputName];
|
||||||
|
// const type = inputData[0];
|
||||||
|
|
||||||
|
// if(inputData[1]?.forceInput) {
|
||||||
|
// this.addInput(inputName, type);
|
||||||
|
// } else {
|
||||||
|
// if (Array.isArray(type)) {
|
||||||
|
// // Enums
|
||||||
|
// Object.assign(config, widgets.COMBO(this, inputName, inputData, app) || {});
|
||||||
|
// } else if (`${type}:${inputName}` in widgets) {
|
||||||
|
// // Support custom widgets by Type:Name
|
||||||
|
// Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData, app) || {});
|
||||||
|
// } else if (type in widgets) {
|
||||||
|
// // Standard type widgets
|
||||||
|
// Object.assign(config, widgets[type](this, inputName, inputData, app) || {});
|
||||||
|
// } else {
|
||||||
|
// // Node connection inputs
|
||||||
|
// this.addInput(inputName, type);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (const o in nodeData["output"]) {
|
||||||
|
// const output = nodeData["output"][o];
|
||||||
|
// const outputName = nodeData["output_name"][o] || output;
|
||||||
|
// const outputShape = nodeData["output_is_list"][o] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE ;
|
||||||
|
// this.addOutput(outputName, output, { shape: outputShape });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const s = this.computeSize();
|
||||||
|
// s[0] = Math.max(config.minWidth, s[0] * 1.5);
|
||||||
|
// s[1] = Math.max(config.minHeight, s[1]);
|
||||||
|
// this.size = s;
|
||||||
|
// this.serialize_widgets = true;
|
||||||
|
|
||||||
|
// app.#invokeExtensionsAsync("nodeCreated", this);
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: nodeData.display_name || nodeData.name,
|
||||||
|
// comfyClass: nodeData.name,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// node.prototype.comfyClass = nodeData.name;
|
||||||
|
node.type = nodeId;
|
||||||
|
|
||||||
|
// this.#addNodeContextMenuHandler(node);
|
||||||
|
// this.#addDrawBackgroundHandler(node, app);
|
||||||
|
// this.#addNodeKeyHandler(node);
|
||||||
|
|
||||||
await this.#invokeExtensionsAsync("beforeRegisterNodeDef", node, nodeData);
|
await this.#invokeExtensionsAsync("beforeRegisterNodeDef", node, nodeData);
|
||||||
LiteGraph.registerNodeType(nodeId, node);
|
LiteGraph.registerNodeType(node);
|
||||||
node.category = nodeData.category;
|
node.category = nodeData.category;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
web/scripts/graph.js
Normal file
5
web/scripts/graph.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { LGraph } from "../lib/litegraph.core.js"
|
||||||
|
|
||||||
|
export default class ComfyGraph extends LGraph {
|
||||||
|
|
||||||
|
}
|
||||||
236
web/scripts/graphCanvas.js
Normal file
236
web/scripts/graphCanvas.js
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
import { LGraphCanvas } from "../lib/litegraph.core.js"
|
||||||
|
|
||||||
|
export default class ComfyGraphCanvas extends LGraphCanvas {
|
||||||
|
processKey(e) {
|
||||||
|
const res = super.processKey(e);
|
||||||
|
|
||||||
|
if (res === false) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.graph) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var block_default = false;
|
||||||
|
|
||||||
|
if (e.target.localName == "input") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.type == "keydown") {
|
||||||
|
// Ctrl + M mute/unmute
|
||||||
|
if (e.keyCode == 77 && e.ctrlKey) {
|
||||||
|
if (this.selected_nodes) {
|
||||||
|
for (var i in this.selected_nodes) {
|
||||||
|
if (this.selected_nodes[i].mode === 2) { // never
|
||||||
|
this.selected_nodes[i].mode = 0; // always
|
||||||
|
} else {
|
||||||
|
this.selected_nodes[i].mode = 2; // never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_default = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.keyCode == 66 && e.ctrlKey) {
|
||||||
|
if (this.selected_nodes) {
|
||||||
|
for (var i in this.selected_nodes) {
|
||||||
|
if (this.selected_nodes[i].mode === 4) { // never
|
||||||
|
this.selected_nodes[i].mode = 0; // always
|
||||||
|
} else {
|
||||||
|
this.selected_nodes[i].mode = 4; // never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_default = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.graph.change();
|
||||||
|
|
||||||
|
if (block_default) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
processMouseDown(e) {
|
||||||
|
const res = super.processMouseDown(e);
|
||||||
|
|
||||||
|
this.selected_group_moving = false;
|
||||||
|
|
||||||
|
if (this.selected_group && !this.selected_group_resizing) {
|
||||||
|
var font_size =
|
||||||
|
this.selected_group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
|
||||||
|
var height = font_size * 1.4;
|
||||||
|
|
||||||
|
// Move group by header
|
||||||
|
if (LiteGraph.isInsideRectangle(e.canvasX, e.canvasY, this.selected_group.pos[0], this.selected_group.pos[1], this.selected_group.size[0], height)) {
|
||||||
|
this.selected_group_moving = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
processMouseMove(e) {
|
||||||
|
const orig_selected_group = this.selected_group;
|
||||||
|
|
||||||
|
if (this.selected_group && !this.selected_group_resizing && !this.selected_group_moving) {
|
||||||
|
this.selected_group = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = super.processMouseMove(e);
|
||||||
|
|
||||||
|
if (orig_selected_group && !this.selected_group_resizing && !this.selected_group_moving) {
|
||||||
|
this.selected_group = orig_selected_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws group header bar
|
||||||
|
*/
|
||||||
|
drawGroups(canvas, ctx) {
|
||||||
|
if (!this.graph) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = this.graph._groups;
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalAlpha = 0.7 * this.editor_alpha;
|
||||||
|
|
||||||
|
for (var i = 0; i < groups.length; ++i) {
|
||||||
|
var group = groups[i];
|
||||||
|
|
||||||
|
if (!LiteGraph.overlapBounding(this.visible_area, group._bounding)) {
|
||||||
|
continue;
|
||||||
|
} //out of the visible area
|
||||||
|
|
||||||
|
ctx.fillStyle = group.color || "#335";
|
||||||
|
ctx.strokeStyle = group.color || "#335";
|
||||||
|
var pos = group._pos;
|
||||||
|
var size = group._size;
|
||||||
|
ctx.globalAlpha = 0.25 * this.editor_alpha;
|
||||||
|
ctx.beginPath();
|
||||||
|
var font_size =
|
||||||
|
group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
|
||||||
|
ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], font_size * 1.4);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.globalAlpha = this.editor_alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
const res = super.drawGroups(canvas, ctx);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws node highlights (executing, drag drop) and progress bar
|
||||||
|
*/
|
||||||
|
drawNodeShape(node, ctx, size, fgcolor, bgcolor, selected, mouse_over) {
|
||||||
|
const res = super.drawNodeShape(node, ctx, size, fgcolor, bgcolor, selected, mouse_over);
|
||||||
|
|
||||||
|
const nodeErrors = self.lastNodeErrors?.[node.id];
|
||||||
|
|
||||||
|
let color = null;
|
||||||
|
let lineWidth = 1;
|
||||||
|
if (node.id === +self.runningNodeId) {
|
||||||
|
color = "#0f0";
|
||||||
|
} else if (self.dragOverNode && node.id === self.dragOverNode.id) {
|
||||||
|
color = "dodgerblue";
|
||||||
|
}
|
||||||
|
else if (nodeErrors?.errors) {
|
||||||
|
color = "red";
|
||||||
|
lineWidth = 2;
|
||||||
|
}
|
||||||
|
else if (self.lastExecutionError && +self.lastExecutionError.node_id === node.id) {
|
||||||
|
color = "#f0f";
|
||||||
|
lineWidth = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color) {
|
||||||
|
const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE;
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
ctx.globalAlpha = 0.8;
|
||||||
|
ctx.beginPath();
|
||||||
|
if (shape == LiteGraph.BOX_SHAPE)
|
||||||
|
ctx.rect(-6, -6 - LiteGraph.NODE_TITLE_HEIGHT, 12 + size[0] + 1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT);
|
||||||
|
else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed))
|
||||||
|
ctx.roundRect(
|
||||||
|
-6,
|
||||||
|
-6 - LiteGraph.NODE_TITLE_HEIGHT,
|
||||||
|
12 + size[0] + 1,
|
||||||
|
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
||||||
|
this.round_radius * 2
|
||||||
|
);
|
||||||
|
else if (shape == LiteGraph.CARD_SHAPE)
|
||||||
|
ctx.roundRect(
|
||||||
|
-6,
|
||||||
|
-6 - LiteGraph.NODE_TITLE_HEIGHT,
|
||||||
|
12 + size[0] + 1,
|
||||||
|
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
||||||
|
[this.round_radius * 2, this.round_radius * 2, 2, 2]
|
||||||
|
);
|
||||||
|
else if (shape == LiteGraph.CIRCLE_SHAPE)
|
||||||
|
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI * 2);
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.strokeStyle = fgcolor;
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.progress && node.id === +self.runningNodeId) {
|
||||||
|
ctx.fillStyle = "green";
|
||||||
|
ctx.fillRect(0, 0, size[0] * (self.progress.value / self.progress.max), 6);
|
||||||
|
ctx.fillStyle = bgcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight inputs that failed validation
|
||||||
|
if (nodeErrors) {
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = "red";
|
||||||
|
for (const error of nodeErrors.errors) {
|
||||||
|
if (error.extra_info && error.extra_info.input_name) {
|
||||||
|
const inputIndex = node.findInputSlot(error.extra_info.input_name)
|
||||||
|
if (inputIndex !== -1) {
|
||||||
|
let pos = node.getConnectionPos(true, inputIndex);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(pos[0] - node.pos[0], pos[1] - node.pos[1], 12, 0, 2 * Math.PI, false)
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawNode(node, ctx) {
|
||||||
|
var editor_alpha = this.editor_alpha;
|
||||||
|
var old_color = node.bgcolor;
|
||||||
|
|
||||||
|
if (node.mode === 2) { // never
|
||||||
|
this.editor_alpha = 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.mode === 4) { // never
|
||||||
|
node.bgcolor = "#FF00FF";
|
||||||
|
this.editor_alpha = 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = super.drawNode(node, ctx);
|
||||||
|
|
||||||
|
this.editor_alpha = editor_alpha;
|
||||||
|
node.bgcolor = old_color;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
86
web/scripts/graphNode.js
Normal file
86
web/scripts/graphNode.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { LGraphNode, LGraphCanvas, BuiltInSlotType, BuiltInSlotShape } from "../lib/litegraph.core.js"
|
||||||
|
import { ComfyWidgets } from "./widgets.js";
|
||||||
|
import { iterateNodeDefOutputs, iterateNodeDefInputs } from "./nodeDef.js";
|
||||||
|
|
||||||
|
export class ComfyGraphNode extends LGraphNode {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// comfy class -> input name -> input config
|
||||||
|
const defaultInputConfigs = {}
|
||||||
|
|
||||||
|
export class ComfyBackendNode extends ComfyGraphNode {
|
||||||
|
constructor(title, comfyClass, nodeDef) {
|
||||||
|
super(title)
|
||||||
|
this.type = comfyClass; // XXX: workaround dependency in LGraphNode.addInput()
|
||||||
|
this.displayName = nodeDef.display_name;
|
||||||
|
this.comfyNodeDef = nodeDef;
|
||||||
|
this.comfyClass = comfyClass;
|
||||||
|
this.isBackendNode = true;
|
||||||
|
|
||||||
|
const color = LGraphCanvas.node_colors["yellow"];
|
||||||
|
if (this.color == null)
|
||||||
|
this.color = color.color
|
||||||
|
if (this.bgColor == null)
|
||||||
|
this.bgColor = color.bgColor
|
||||||
|
|
||||||
|
this.#setup(nodeDef)
|
||||||
|
|
||||||
|
// if (nodeDef.output_node) {
|
||||||
|
// this.addOutput("OUTPUT", BuiltInSlotType.EVENT, { color_off: "rebeccapurple", color_on: "rebeccapurple" });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
get isOutputNode() {
|
||||||
|
return this.comfyNodeDef.output_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setup(nodeDef) {
|
||||||
|
defaultInputConfigs[this.type] = {}
|
||||||
|
|
||||||
|
const widgets = Object.assign({}, ComfyWidgets, ComfyWidgets.customWidgets);
|
||||||
|
|
||||||
|
for (const [inputName, inputData] of iterateNodeDefInputs(nodeDef)) {
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
const [type, opts] = inputData;
|
||||||
|
|
||||||
|
if (opts?.forceInput) {
|
||||||
|
if (Array.isArray(type)) {
|
||||||
|
throw new Error(`Can't have forceInput set to true for an enum type! ${type}`)
|
||||||
|
}
|
||||||
|
this.addInput(inputName, type);
|
||||||
|
} else {
|
||||||
|
if (Array.isArray(type)) {
|
||||||
|
// Enums
|
||||||
|
Object.assign(config, widgets.COMBO(this, inputName, inputData) || {});
|
||||||
|
} else if (`${type}:${inputName}` in widgets) {
|
||||||
|
// Support custom ComfyWidgets by Type:Name
|
||||||
|
Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData) || {});
|
||||||
|
} else if (type in widgets) {
|
||||||
|
// Standard type ComfyWidgets
|
||||||
|
Object.assign(config, widgets[type](this, inputName, inputData) || {});
|
||||||
|
} else {
|
||||||
|
// Node connection inputs (backend)
|
||||||
|
this.addInput(inputName, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("widgetNodeType" in config)
|
||||||
|
ComfyBackendNode.defaultInputConfigs[this.type][inputName] = config.config
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const output of iterateNodeDefOutputs(nodeDef)) {
|
||||||
|
const outputShape = output.is_list ? BuiltInSlotShape.GRID_SHAPE : BuiltInSlotShape.CIRCLE_SHAPE;
|
||||||
|
this.addOutput(output.name, output.type, { shape: outputShape });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serialize_widgets = false;
|
||||||
|
// app.#invokeExtensionsAsync("nodeCreated", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onExecuted(outputData) {
|
||||||
|
// console.warn("onExecuted outputs", outputData)
|
||||||
|
// this.triggerSlot(0, outputData)
|
||||||
|
// }
|
||||||
|
}
|
||||||
26
web/scripts/nodeDef.js
Normal file
26
web/scripts/nodeDef.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { ComfyWidgets } from "./widgets.js"
|
||||||
|
import { range } from "./utils.js"
|
||||||
|
|
||||||
|
export function isBackendNodeDefInputType(inputName, type) {
|
||||||
|
const widgets = Object.assign({}, ComfyWidgets, ComfyWidgets.customWidgets);
|
||||||
|
return !Array.isArray(type) && !(type in ComfyWidgets) && !(`${type}:${inputName}` in ComfyWidgets);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function iterateNodeDefInputs(def) {
|
||||||
|
var inputs = def.input.required || {}
|
||||||
|
if (def.input.optional != null) {
|
||||||
|
inputs = Object.assign({}, def.input.required, def.input.optional)
|
||||||
|
}
|
||||||
|
return Object.entries(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function iterateNodeDefOutputs(def) {
|
||||||
|
const outputCount = def.output ? def.output.length : 0;
|
||||||
|
return range(outputCount).map(i => {
|
||||||
|
return {
|
||||||
|
type: def.output[i],
|
||||||
|
name: def.output_name[i] || def.output[i],
|
||||||
|
is_list: def.output_is_list[i],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
3
web/scripts/utils.js
Normal file
3
web/scripts/utils.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function range(size, startAt = 0) {
|
||||||
|
return [...Array(size).keys()].map(i => i + startAt);
|
||||||
|
}
|
||||||
@ -157,7 +157,7 @@ function addMultilineWidget(node, name, opts, app) {
|
|||||||
// Calculate it here instead
|
// Calculate it here instead
|
||||||
computeSize(node.size);
|
computeSize(node.size);
|
||||||
}
|
}
|
||||||
const visible = app.canvas.ds.scale > 0.5 && this.type === "customtext";
|
const visible = window.app.canvas.ds.scale > 0.5 && this.type === "customtext";
|
||||||
const margin = 10;
|
const margin = 10;
|
||||||
const elRect = ctx.canvas.getBoundingClientRect();
|
const elRect = ctx.canvas.getBoundingClientRect();
|
||||||
const transform = new DOMMatrix()
|
const transform = new DOMMatrix()
|
||||||
@ -176,7 +176,7 @@ function addMultilineWidget(node, name, opts, app) {
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
background: (!node.color)?'':node.color,
|
background: (!node.color)?'':node.color,
|
||||||
color: (!node.color)?'':'white',
|
color: (!node.color)?'':'white',
|
||||||
zIndex: app.graph._nodes.indexOf(node),
|
zIndex: window.app.graph._nodes.indexOf(node),
|
||||||
});
|
});
|
||||||
this.inputEl.hidden = !visible;
|
this.inputEl.hidden = !visible;
|
||||||
},
|
},
|
||||||
@ -195,11 +195,11 @@ function addMultilineWidget(node, name, opts, app) {
|
|||||||
|
|
||||||
node.addCustomWidget(widget);
|
node.addCustomWidget(widget);
|
||||||
|
|
||||||
app.canvas.onDrawBackground = function () {
|
window.app.canvas.onDrawBackground = function () {
|
||||||
// Draw node isnt fired once the node is off the screen
|
// Draw node isnt fired once the node is off the screen
|
||||||
// if it goes off screen quickly, the input may not be removed
|
// if it goes off screen quickly, the input may not be removed
|
||||||
// this shifts it off screen so it can be moved back if the node is visible.
|
// this shifts it off screen so it can be moved back if the node is visible.
|
||||||
for (let n in app.graph._nodes) {
|
for (let n in window.app.graph._nodes) {
|
||||||
n = graph._nodes[n];
|
n = graph._nodes[n];
|
||||||
for (let w in n.widgets) {
|
for (let w in n.widgets) {
|
||||||
let wid = n.widgets[w];
|
let wid = n.widgets[w];
|
||||||
@ -251,7 +251,7 @@ function addMultilineWidget(node, name, opts, app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isSlider(display, app) {
|
function isSlider(display, app) {
|
||||||
if (app.ui.settings.getSettingValue("Comfy.DisableSliders")) {
|
if (window.app.ui.settings.getSettingValue("Comfy.DisableSliders")) {
|
||||||
return "number"
|
return "number"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ export const ComfyWidgets = {
|
|||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
node.imgs = [img];
|
node.imgs = [img];
|
||||||
app.graph.setDirtyCanvas(true);
|
window.app.graph.setDirtyCanvas(true);
|
||||||
};
|
};
|
||||||
let folder_separator = name.lastIndexOf("/");
|
let folder_separator = name.lastIndexOf("/");
|
||||||
let subfolder = "";
|
let subfolder = "";
|
||||||
@ -329,7 +329,7 @@ export const ComfyWidgets = {
|
|||||||
subfolder = name.substring(0, folder_separator);
|
subfolder = name.substring(0, folder_separator);
|
||||||
name = name.substring(folder_separator + 1);
|
name = name.substring(folder_separator + 1);
|
||||||
}
|
}
|
||||||
img.src = api.apiURL(`/view?filename=${name}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}`);
|
img.src = api.apiURL(`/view?filename=${name}&type=input&subfolder=${subfolder}${window.app.getPreviewFormatParam()}`);
|
||||||
node.setSizeForImage?.();
|
node.setSizeForImage?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,3 +457,5 @@ export const ComfyWidgets = {
|
|||||||
return { widget: uploadWidget };
|
return { widget: uploadWidget };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const customWidgets = []
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user