Pull in actual litegraph.js types, replacing rgthree-comfy version.

This commit is contained in:
rgthree
2025-03-28 22:48:42 -04:00
parent cb36126240
commit d10b7c87f4
80 changed files with 1185 additions and 2674 deletions
+7 -1
View File
@@ -4,13 +4,19 @@
"requires": true,
"packages": {
"": {
"name": "rgthree-comfy",
"devDependencies": {
"@comfyorg/litegraph": "^0.11.3",
"prettier": "^3.3.3",
"sass": "^1.77.8",
"typescript": "^5.5.4"
}
},
"node_modules/@comfyorg/litegraph": {
"version": "0.11.3",
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.11.3.tgz",
"integrity": "sha512-OEZJMFbC4WwsIW+w42MA8cwMY/Uu+OL5tQfvdhrlIj51G/TZv1yvU7tNPeu0gZM24Vm5rzF9IxXF8aajD9BIlw==",
"dev": true
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+2 -1
View File
@@ -2,7 +2,8 @@
"devDependencies": {
"prettier": "^3.3.3",
"typescript": "^5.5.4",
"sass": "^1.77.8"
"sass": "^1.77.8",
"@comfyorg/litegraph": "^0.11.3"
},
"scripts": {
"build": "./__build__.py || python .\\__build__.py"
+1 -1
View File
@@ -1,4 +1,4 @@
import type { INodeInputSlot, INodeOutputSlot, LLink } from "typings/litegraph.js";
import type { INodeInputSlot, INodeOutputSlot, LLink } from "@litegraph/litegraph.js";
import type { ComfyApp, ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import { app } from "scripts/app.js";
@@ -1,4 +1,3 @@
import type { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import type {
Vector2,
LLink,
@@ -6,11 +5,12 @@ import type {
INodeOutputSlot,
LGraphNode as TLGraphNode,
IWidget,
} from "typings/litegraph.js";
ISlotType,
} from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { rgthree } from "./rgthree.js";
import {app} from "scripts/app.js";
import {RgthreeBaseVirtualNode} from "./base_node.js";
import {rgthree} from "./rgthree.js";
import {
PassThroughFollowing,
@@ -21,6 +21,8 @@ import {
getConnectedOutputNodes,
getConnectedOutputNodesAndFilterPassThroughs,
} from "./utils.js";
import {ConnectByTypeOptions} from "@litegraph/LGraphNode.js";
import {TWidgetType, IWidgetOptions} from "@litegraph/types/widgets.js";
/**
* A Virtual Node that allows any node's output to connect to it.
@@ -47,7 +49,7 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
}
override clone() {
const cloned = super.clone();
const cloned = super.clone()!;
// Copying to clipboard (and also, creating node templates) work by cloning nodes and, for some
// reason, it manually manipulates the cloned data. So, we want to keep the present input slots
// so if it's pasted/templatized the data is correct. Otherwise, clear the inputs and so the new
@@ -83,7 +85,7 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
* Ensures we have at least one empty input at the end, returns true if changes were made, or false
* if no changes were needed.
*/
private stabilizeInputsOutputs() : boolean {
private stabilizeInputsOutputs(): boolean {
let changed = false;
const hasEmptyInput = !this.inputs[this.inputs.length - 1]?.link;
if (!hasEmptyInput) {
@@ -142,7 +144,7 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
* Handles stabilization of linked nodes. To be overridden. Should return true if changes were
* made, or false if no changes were needed.
*/
handleLinkedNodesStabilization(linkedNodes: TLGraphNode[]) : boolean {
handleLinkedNodesStabilization(linkedNodes: TLGraphNode[]): boolean {
linkedNodes; // No-op, but makes overridding in VSCode cleaner.
throw new Error("handleLinkedNodesStabilization should be overridden.");
}
@@ -181,13 +183,13 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
return super.addInput(name, type, extra_info);
}
override addWidget<T extends IWidget>(
type: T["type"],
override addWidget(
type: TWidgetType,
name: string,
value: T["value"],
callback?: T["callback"] | string,
options?: T["options"],
) {
value: string | number | boolean | object,
callback: IWidget["callback"] | string | null,
options?: IWidgetOptions | string,
): IWidget {
(this as any)._tempWidth = this.size[0];
return super.addWidget(type, name, value, callback, options);
}
@@ -286,12 +288,12 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
* If something is dropped on us, just add it to the bottom. onConnectInput should already cancel
* if it's disallowed.
*/
override connectByTypeOutput<T = any>(
slot: string | number,
override connectByTypeOutput(
slot: number | string,
sourceNode: TLGraphNode,
sourceSlotType: string,
optsIn: string,
): T | null {
sourceSlotType: ISlotType,
optsIn?: ConnectByTypeOptions,
): LLink | null {
const lastInput = this.inputs[this.inputs.length - 1];
if (!lastInput?.link && lastInput?.type === "*") {
var sourceSlot = sourceNode.findOutputSlotByType(sourceSlotType, false, true);
@@ -323,23 +325,25 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
// that and instead take the next free one. If that doesn't work, then we'll give it to the old
// method.
const oldLGraphNodeConnectByType = LGraphNode.prototype.connectByType;
LGraphNode.prototype.connectByType = function connectByType<T = any>(
slot: string | number,
sourceNode: TLGraphNode,
sourceSlotType: string,
optsIn: string,
): T | null {
// If we're droppiong on a node, and the last input is free and an "*" type, then connect there
LGraphNode.prototype.connectByType = function connectByType(
slot: number | string,
targetNode: TLGraphNode,
targetSlotType: ISlotType,
optsIn?: ConnectByTypeOptions,
): LLink | null {
// If we're dropping on a node, and the last input is free and an "*" type, then connect there
// first...
if (sourceNode.inputs) {
for (const [index, input] of sourceNode.inputs.entries()) {
if (targetNode.inputs) {
for (const [index, input] of targetNode.inputs.entries()) {
if (!input.link && input.type === "*") {
this.connect(slot, sourceNode, index);
this.connect(slot, targetNode, index);
return null;
}
}
}
return ((oldLGraphNodeConnectByType &&
oldLGraphNodeConnectByType.call(this, slot, sourceNode, sourceSlotType, optsIn)) ||
null) as T;
return (
(oldLGraphNodeConnectByType &&
oldLGraphNodeConnectByType.call(this, slot, targetNode, targetSlotType, optsIn)) ||
null
);
};
+43 -36
View File
@@ -1,26 +1,25 @@
import type { ComfyNodeConstructor, ComfyObjectInfo, NodeMode } from "typings/comfy.js";
import type {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import type {
IWidget,
SerializedLGraphNode,
LGraphNode as TLGraphNode,
LGraphCanvas,
ContextMenuItem,
INodeOutputSlot,
INodeInputSlot,
} from "typings/litegraph.js";
import type { RgthreeBaseServerNodeConstructor, RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
IContextMenuValue,
IFoundSlot,
LGraphEventMode,
} from "@litegraph/litegraph.js";
import type {ISerialisedNode} from "@litegraph/types/serialisation.js";
import type {RgthreeBaseServerNodeConstructor} from "typings/rgthree.js";
import { ComfyWidgets } from "scripts/widgets.js";
import { SERVICE as KEY_EVENT_SERVICE } from "./services/key_events_services.js";
import { app } from "scripts/app.js";
import { LogLevel, rgthree } from "./rgthree.js";
import { addHelpMenuItem } from "./utils.js";
import { RgthreeHelpDialog } from "rgthree/common/dialog.js";
import {ComfyWidgets} from "scripts/widgets.js";
import {SERVICE as KEY_EVENT_SERVICE} from "./services/key_events_services.js";
import {app} from "scripts/app.js";
import {LogLevel, rgthree} from "./rgthree.js";
import {addHelpMenuItem} from "./utils.js";
import {RgthreeHelpDialog} from "rgthree/common/dialog.js";
import {
importIndividualNodesInnerOnDragDrop,
importIndividualNodesInnerOnDragOver,
} from "./feature_import_individual_nodes.js";
import { defineProperty } from "rgthree/common/shared_utils.js";
import {defineProperty} from "rgthree/common/shared_utils.js";
/**
* A base node with standard methods, directly extending the LGraphNode.
@@ -33,9 +32,12 @@ export abstract class RgthreeBaseNode extends LGraphNode {
static exposedActions: string[] = [];
static override title: string = "__NEED_CLASS_TITLE__";
static category = "rgthree";
static override category = "rgthree";
static _category = "rgthree"; // `category` seems to get reset by comfy, so reset to this after.
/** Our constructor ensures there's a widget array, so we get rid of the nullability. */
override widgets!: IWidget[];
/**
* The comfyClass is property ComfyUI and extensions may care about, even through it is only for
* server nodes. RgthreeBaseServerNode below overrides this with the expected value and we just
@@ -58,7 +60,8 @@ export abstract class RgthreeBaseNode extends LGraphNode {
_tempWidth = 0;
/** Private Mode member so we can override the setter/getter and call an `onModeChange`. */
private rgthree_mode: NodeMode;
private rgthree_mode?: LGraphEventMode;
/** An internal bool set when `onConstructed` is run. */
private __constructed__ = false;
/** The help dialog. */
@@ -84,11 +87,11 @@ export abstract class RgthreeBaseNode extends LGraphNode {
this.checkAndRunOnConstructed();
});
defineProperty(this, 'mode', {
defineProperty(this, "mode", {
get: () => {
return this.rgthree_mode;
},
set: (mode: NodeMode) => {
set: (mode: LGraphEventMode) => {
if (this.rgthree_mode != mode) {
const oldMode = this.rgthree_mode;
this.rgthree_mode = mode;
@@ -134,7 +137,7 @@ export abstract class RgthreeBaseNode extends LGraphNode {
return this.__constructed__;
}
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
override configure(info: ISerialisedNode): void {
this.configuring = true;
super.configure(info);
// Fix https://github.com/comfyanonymous/ComfyUI/issues/1448 locally.
@@ -149,17 +152,17 @@ export abstract class RgthreeBaseNode extends LGraphNode {
* Override clone for, at the least, deep-copying properties.
*/
override clone() {
const cloned = super.clone();
// This is whild, but LiteGraph clone doesn't deep clone data, so we will. We'll use structured
// clone, which most browsers in 2022 support, but but we'll check.
if (cloned.properties && !!window.structuredClone) {
const cloned = super.clone()!;
// This is wild, but LiteGraph doesn't deep clone data, so we will. We'll use structured clone,
// which most browsers in 2022 support, but but we'll check.
if (cloned?.properties && !!window.structuredClone) {
cloned.properties = structuredClone(cloned.properties);
}
return cloned;
}
/** When a mode change, we want all connected nodes to match. */
onModeChange(from: NodeMode, to: NodeMode) {
onModeChange(from: LGraphEventMode | undefined, to: LGraphEventMode) {
// Override
}
@@ -175,6 +178,9 @@ export abstract class RgthreeBaseNode extends LGraphNode {
* Guess this doesn't exist in Litegraph...
*/
removeWidget(widgetOrSlot?: IWidget | number) {
if (!this.widgets) {
return;
}
if (typeof widgetOrSlot === "number") {
this.widgets.splice(widgetOrSlot, 1);
} else if (widgetOrSlot) {
@@ -191,23 +197,20 @@ export abstract class RgthreeBaseNode extends LGraphNode {
* it's default logic. This bakes it so child nodes can call this instead (and this doesn't set
* getSlotMenuOptions for all child nodes in case it doesn't exist).
*/
defaultGetSlotMenuOptions(slot: {
input?: INodeInputSlot;
output?: INodeOutputSlot;
}): ContextMenuItem[] | null {
const menu_info: ContextMenuItem[] = [];
defaultGetSlotMenuOptions(slot: IFoundSlot): IContextMenuValue[] {
const menu_info: IContextMenuValue[] = [];
if (slot?.output?.links?.length) {
menu_info.push({ content: "Disconnect Links", slot: slot });
menu_info.push({content: "Disconnect Links", slot});
}
let inputOrOutput = slot.input || slot.output;
if (inputOrOutput) {
if (inputOrOutput.removable) {
menu_info.push(
inputOrOutput.locked ? { content: "Cannot remove" } : { content: "Remove Slot", slot },
inputOrOutput.locked ? {content: "Cannot remove"} : {content: "Remove Slot", slot},
);
}
if (!inputOrOutput.nameLocked) {
menu_info.push({ content: "Rename Slot", slot });
menu_info.push({content: "Rename Slot", slot});
}
}
return menu_info;
@@ -250,7 +253,10 @@ export abstract class RgthreeBaseNode extends LGraphNode {
KEY_EVENT_SERVICE.handleKeyDownOrUp(event);
}
override getExtraMenuOptions(canvas: LGraphCanvas, options: ContextMenuItem[]): void {
override getExtraMenuOptions(
canvas: LGraphCanvas,
options: (IContextMenuValue<unknown> | null)[],
): (IContextMenuValue<unknown> | null)[] {
// Some other extensions override getExtraMenuOptions on the nodeType as it comes through from
// the server, so we can call out to that if we don't have our own.
if (super.getExtraMenuOptions) {
@@ -266,6 +272,7 @@ export abstract class RgthreeBaseNode extends LGraphNode {
if (help) {
addHelpMenuItem(this, help, options);
}
return options;
}
}
@@ -339,7 +346,7 @@ export class RgthreeBaseServerNode extends RgthreeBaseNode {
const WIDGETS = this.getWidgets();
const config: { minWidth: number; minHeight: number; widget?: null | { options: any } } = {
const config: {minWidth: number; minHeight: number; widget?: null | {options: any}} = {
minWidth: 1,
minHeight: 1,
widget: null,
@@ -392,7 +399,7 @@ export class RgthreeBaseServerNode extends RgthreeBaseNode {
const outputShape = nodeData["output_is_list"][o]
? LiteGraph.GRID_SHAPE
: LiteGraph.CIRCLE_SHAPE;
this.addOutput(outputName, output, { shape: outputShape });
this.addOutput(outputName, output, {shape: outputShape});
}
const s = this.computeSize();
+2 -2
View File
@@ -1,4 +1,4 @@
import type { INodeOutputSlot, LGraphNode } from "typings/litegraph.js";
import type { INodeOutputSlot, LGraphNode } from "@litegraph/litegraph.js";
import { rgthree } from "./rgthree.js";
import { BaseAnyInputConnectedNode } from "./base_any_input_connected_node.js";
import {
@@ -25,7 +25,7 @@ export class BaseCollectorNode extends BaseAnyInputConnectedNode {
}
override clone() {
const cloned = super.clone();
const cloned = super.clone()!;
return cloned;
}
+7 -10
View File
@@ -1,9 +1,6 @@
import type { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import type {
LGraphNode as TLGraphNode,
IWidget,
SerializedLGraphNode,
} from "typings/litegraph.js";
import type { LGraphNode, IWidget} from "@litegraph/litegraph.js";
import type { ISerialisedNode } from "@litegraph/types/serialisation.js";
import { BaseAnyInputConnectedNode } from "./base_any_input_connected_node.js";
import { PassThroughFollowing } from "./utils.js";
import { wait } from "rgthree/common/shared_utils.js";
@@ -38,7 +35,7 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
return super.onConstructed();
}
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
override configure(info: ISerialisedNode): void {
// Patch a small issue (~14h) where multiple OPT_CONNECTIONS may have been created.
// https://github.com/rgthree/rgthree-comfy/issues/206
// TODO: This can probably be removed within a few weeks.
@@ -48,7 +45,7 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
super.configure(info);
}
override handleLinkedNodesStabilization(linkedNodes: TLGraphNode[]) {
override handleLinkedNodesStabilization(linkedNodes: LGraphNode[]) {
let changed = false;
for (const [index, node] of linkedNodes.entries()) {
let widget = this.widgets && this.widgets[index];
@@ -70,7 +67,7 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
return changed;
}
private setWidget(widget: IWidget, linkedNode: TLGraphNode, forceValue?: boolean) {
private setWidget(widget: IWidget, linkedNode: LGraphNode, forceValue?: boolean) {
let changed = false;
const value = forceValue == null ? linkedNode.mode === this.modeOn : forceValue;
let name = `Enable ${linkedNode.title}`;
@@ -82,7 +79,7 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode {
(widget as any).doModeChange = (forceValue?: boolean, skipOtherNodeCheck?: boolean) => {
let newValue = forceValue == null ? linkedNode.mode === this.modeOff : forceValue;
if (skipOtherNodeCheck !== true) {
if (newValue && this.properties?.["toggleRestriction"]?.includes(" one")) {
if (newValue && (this.properties?.["toggleRestriction"] as string)?.includes(" one")) {
for (const widget of this.widgets) {
(widget as any).doModeChange(false, true);
}
+24 -22
View File
@@ -1,16 +1,17 @@
import { api } from "scripts/api.js";
import type {
LLink,
IComboWidget,
LGraphNode,
INodeOutputSlot,
INodeInputSlot,
IWidget,
SerializedLGraphNode,
} from "typings/litegraph.js";
import type { ComfyObjectInfo, ComfyGraphNode } from "typings/comfy.js";
import { wait } from "rgthree/common/shared_utils.js";
import { rgthree } from "./rgthree.js";
} from "@litegraph/litegraph.js";
import type {ISerialisedNode} from "@litegraph/types/serialisation.js";
import type {IComboWidget} from "@litegraph/types/widgets.js";
import type {ComfyObjectInfo, ComfyGraphNode} from "typings/comfy.js";
import {api} from "scripts/api.js";
import {wait} from "rgthree/common/shared_utils.js";
import {rgthree} from "./rgthree.js";
/** Wraps a node instance keeping closure without mucking the finicky types. */
export class PowerPrompt {
@@ -18,8 +19,8 @@ export class PowerPrompt {
readonly node: ComfyGraphNode;
readonly promptEl: HTMLTextAreaElement;
nodeData: ComfyObjectInfo;
readonly combos: { [key: string]: IComboWidget } = {};
readonly combosValues: { [key: string]: string[] } = {};
readonly combos: {[key: string]: IComboWidget} = {};
readonly combosValues: {[key: string]: string[]} = {};
boundOnFreshNodeDefs!: (event: CustomEvent) => void;
private configuring = false;
@@ -33,13 +34,13 @@ export class PowerPrompt {
this.nodeData = nodeData;
this.isSimple = this.nodeData.name.includes("Simple");
this.promptEl = (node.widgets[0]! as any).inputEl;
this.promptEl = (node.widgets![0]! as any).inputEl;
this.addAndHandleKeyboardLoraEditWeight();
this.patchNodeRefresh();
const oldConfigure = this.node.configure;
this.node.configure = (info: SerializedLGraphNode) => {
this.node.configure = (info: ISerialisedNode) => {
this.configuring = true;
oldConfigure?.apply(this.node, [info]);
this.configuring = false;
@@ -109,17 +110,18 @@ export class PowerPrompt {
const onPropertyChanged = this.node.onPropertyChanged;
this.node.onPropertyChanged = (property: string, value: any, prevValue: any) => {
onPropertyChanged && onPropertyChanged.call(this, property, value, prevValue);
const v = onPropertyChanged && onPropertyChanged.call(this.node, property, value, prevValue);
if (property === "combos_filter") {
this.refreshCombos(this.nodeData);
}
return v ?? true;
};
// Strip all widgets but prompt (we'll re-add them in refreshCombos)
// this.node.widgets.splice(1);
for (let i = this.node.widgets.length - 1; i >= 0; i--) {
if (this.shouldRemoveServerWidget(this.node.widgets[i]!)) {
this.node.widgets.splice(i, 1);
for (let i = this.node.widgets!.length - 1; i >= 0; i--) {
if (this.shouldRemoveServerWidget(this.node.widgets![i]!)) {
this.node.widgets!.splice(i, 1);
}
}
@@ -187,9 +189,9 @@ export class PowerPrompt {
refreshCombos(nodeData: ComfyObjectInfo) {
this.nodeData = nodeData;
let filter: RegExp | null = null;
if (this.node.properties["combos_filter"]?.trim()) {
if ((this.node.properties["combos_filter"] as string)?.trim()) {
try {
filter = new RegExp(this.node.properties["combos_filter"].trim(), "i");
filter = new RegExp((this.node.properties["combos_filter"] as string).trim(), "i");
} catch (e) {
console.error(`Could not parse "${filter}" for Regular Expression`, e);
filter = null;
@@ -235,7 +237,7 @@ export class PowerPrompt {
} else if (key.includes("lora")) {
this.insertSelectionText(`<lora:${selected}:1.0>`);
}
this.combos[key]!.value = values[0];
this.combos[key]!.value = values[0]!;
});
}
},
@@ -243,7 +245,7 @@ export class PowerPrompt {
values,
serialize: true, // Don't include this in prompt.
},
);
) as IComboWidget;
(this.combos[key]! as any).oldComputeSize = this.combos[key]!.computeSize;
let node = this.node;
this.combos[key]!.computeSize = function (width: number) {
@@ -251,16 +253,16 @@ export class PowerPrompt {
width,
LiteGraph.NODE_WIDGET_HEIGHT,
];
if (this === node.widgets[node.widgets.length - 1]) {
if (this === node.widgets![node.widgets!.length - 1]) {
size[1] += 10;
}
return size;
};
}
this.combos[key]!.options!.values = values;
this.combos[key]!.value = values[0];
this.combos[key]!.value = values[0]!;
} else if (!shouldShow && this.combos[key]) {
this.node.widgets.splice(this.node.widgets.indexOf(this.combos[key]!), 1);
this.node.widgets!.splice(this.node.widgets!.indexOf(this.combos[key]!), 1);
delete this.combos[key];
}
} else if (key.startsWith("values")) {
+17 -19
View File
@@ -1,15 +1,12 @@
import { app } from "scripts/app.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { SERVICE as KEY_EVENT_SERVICE } from "./services/key_events_services.js";
import { NodeTypesString } from "./constants.js";
import type {
LGraph,
LGraphCanvas,
INumberWidget,
LGraphNode,
Vector2,
} from "typings/litegraph.js";
import { getClosestOrSelf, queryOne } from "rgthree/common/utils_dom.js";
import type {LGraph, LGraphCanvas, LGraphNode} from "@litegraph/litegraph.js";
import type {Point} from "@litegraph/interfaces.js";
import type {CanvasMouseEvent} from "@litegraph/types/events.js";
import {app} from "scripts/app.js";
import {RgthreeBaseVirtualNode} from "./base_node.js";
import {SERVICE as KEY_EVENT_SERVICE} from "./services/key_events_services.js";
import {NodeTypesString} from "./constants.js";
import {getClosestOrSelf, queryOne} from "rgthree/common/utils_dom.js";
/**
* A bookmark node. Can be placed anywhere in the workflow, and given a shortcut key that will
@@ -61,7 +58,7 @@ export class Bookmark extends RgthreeBaseVirtualNode {
y: 8,
},
);
this.addWidget<INumberWidget>("number", "zoom", 1, (value: number) => {}, {
this.addWidget("number", "zoom", 1, (value: number) => {}, {
y: 8 + LiteGraph.NODE_WIDGET_HEIGHT + 4,
max: 2,
min: 0.5,
@@ -79,7 +76,7 @@ export class Bookmark extends RgthreeBaseVirtualNode {
// }
get shortcutKey(): string {
return this.widgets[0]?.value?.toLocaleLowerCase() ?? "";
return (this.widgets[0]?.value as string)?.toLocaleLowerCase() ?? "";
}
override onAdded(graph: LGraph): void {
@@ -90,15 +87,15 @@ export class Bookmark extends RgthreeBaseVirtualNode {
KEY_EVENT_SERVICE.removeEventListener("keydown", this.keypressBound as EventListener);
}
onKeypress(event: CustomEvent<{ originalEvent: KeyboardEvent }>) {
onKeypress(event: CustomEvent<{originalEvent: KeyboardEvent}>) {
const originalEvent = event.detail.originalEvent;
const target = (originalEvent.target as HTMLElement)!;
if (getClosestOrSelf(target, 'input,textarea,[contenteditable="true"]')) {
return;
}
// Only the shortcut keys are held down, otionally including "shift".
if (KEY_EVENT_SERVICE.areOnlyKeysDown(this.widgets[0]!.value, true)) {
// Only the shortcut keys are held down, optionally including "shift".
if (KEY_EVENT_SERVICE.areOnlyKeysDown(this.widgets[0]!.value as string, true)) {
this.canvasToBookmark();
originalEvent.preventDefault();
originalEvent.stopPropagation();
@@ -108,9 +105,9 @@ export class Bookmark extends RgthreeBaseVirtualNode {
/**
* Called from LiteGraph's `processMouseDown` after it would invoke the input box for the
* shortcut_key, so we check if it exists and then add our own event listener so we can track the
* keys down for the user.
* keys down for the user. Note, blocks drag if the return is truthy.
*/
override onMouseDown(event: MouseEvent, pos: Vector2, graphCanvas: LGraphCanvas): void {
override onMouseDown(event: CanvasMouseEvent, pos: Point, graphCanvas: LGraphCanvas): boolean {
const input = queryOne<HTMLInputElement>(".graphdialog > input.value");
if (input && input.value === this.widgets[0]?.value) {
input.addEventListener("keydown", (e) => {
@@ -121,6 +118,7 @@ export class Bookmark extends RgthreeBaseVirtualNode {
input.value = Object.keys(KEY_EVENT_SERVICE.downKeys).join(" + ");
});
}
return false;
}
canvasToBookmark() {
+8 -7
View File
@@ -1,7 +1,8 @@
import { app } from "scripts/app.js";
import { BaseNodeModeChanger } from "./base_node_mode_changer.js";
import { NodeTypesString } from "./constants.js";
import type { LGraphNode } from "typings/litegraph.js";
import type {LGraphNode} from "@litegraph/litegraph.js";
import {app} from "scripts/app.js";
import {BaseNodeModeChanger} from "./base_node_mode_changer.js";
import {NodeTypesString} from "./constants.js";
const MODE_BYPASS = 4;
const MODE_ALWAYS = 0;
@@ -23,15 +24,15 @@ class BypasserNode extends BaseNodeModeChanger {
override async handleAction(action: string) {
if (action === "Bypass all") {
for (const widget of this.widgets) {
for (const widget of this.widgets || []) {
this.forceWidgetOff(widget, true);
}
} else if (action === "Enable all") {
for (const widget of this.widgets) {
for (const widget of this.widgets || []) {
this.forceWidgetOn(widget, true);
}
} else if (action === "Toggle all") {
for (const widget of this.widgets) {
for (const widget of this.widgets || []) {
this.forceWidgetToggle(widget, true);
}
}
+37 -32
View File
@@ -4,9 +4,11 @@ import type {
LGraphCanvas as TLGraphCanvas,
LGraphNode as TLGraphNode,
LLink,
} from "typings/litegraph.js";
import type { ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import { app } from "scripts/app.js";
ISlotType,
} from "@litegraph/litegraph.js";
import type {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import {app} from "scripts/app.js";
import {
IoDirection,
addConnectionLayoutSupport,
@@ -14,12 +16,13 @@ import {
matchLocalSlotsToServer,
replaceNode,
} from "./utils.js";
import { RgthreeBaseServerNode } from "./base_node.js";
import { SERVICE as KEY_EVENT_SERVICE } from "./services/key_events_services.js";
import { RgthreeBaseServerNodeConstructor } from "typings/rgthree.js";
import { debounce, wait } from "rgthree/common/shared_utils.js";
import { removeUnusedInputsFromEnd } from "./utils_inputs_outputs.js";
import { NodeTypesString } from "./constants.js";
import {RgthreeBaseServerNode} from "./base_node.js";
import {SERVICE as KEY_EVENT_SERVICE} from "./services/key_events_services.js";
import {RgthreeBaseServerNodeConstructor} from "typings/rgthree.js";
import {debounce, wait} from "rgthree/common/shared_utils.js";
import {removeUnusedInputsFromEnd} from "./utils_inputs_outputs.js";
import {NodeTypesString} from "./constants.js";
import {ConnectByTypeOptions} from "@litegraph/LGraphNode.js";
/**
* Takes a non-context node and determins for its input or output slot, if there is a valid
@@ -109,21 +112,19 @@ export class BaseContextNode extends RgthreeBaseServerNode {
ctx.font = oldFont;
}
override connectByType<T = any>(
slot: string | number,
sourceNode: TLGraphNode,
sourceSlotType: string,
optsIn: string,
): T | null {
let canConnect =
super.connectByType &&
super.connectByType.call(this, slot, sourceNode, sourceSlotType, optsIn);
override connectByType(
slot: number | string,
targetNode: TLGraphNode,
targetSlotType: ISlotType,
optsIn?: ConnectByTypeOptions,
): LLink | null {
let canConnect = super.connectByType?.call(this, slot, targetNode, targetSlotType, optsIn);
if (!super.connectByType) {
canConnect = LGraphNode.prototype.connectByType.call(
this,
slot,
sourceNode,
sourceSlotType,
targetNode,
targetSlotType,
optsIn,
);
}
@@ -132,28 +133,32 @@ export class BaseContextNode extends RgthreeBaseServerNode {
// Okay, we've dragged a context and it can't connect.. let's connect all the other nodes.
// Unfortunately, we don't know which are null now, so we'll just connect any that are
// not already connected.
for (const [index, input] of (sourceNode.inputs || []).entries()) {
for (const [index, input] of (targetNode.inputs || []).entries()) {
if (input.link && !ctrlKey) {
continue;
}
const thisOutputSlot = findMatchingIndexByTypeOrName(sourceNode, input, this.outputs);
const thisOutputSlot = findMatchingIndexByTypeOrName(targetNode, input, this.outputs);
if (thisOutputSlot > -1) {
this.connect(thisOutputSlot, sourceNode, index);
this.connect(thisOutputSlot, targetNode, index);
}
}
}
return null;
}
override connectByTypeOutput<T = any>(
slot: string | number,
override connectByTypeOutput(
slot: number | string,
sourceNode: TLGraphNode,
sourceSlotType: string,
optsIn: string,
): T | null {
let canConnect =
super.connectByTypeOutput &&
super.connectByTypeOutput.call(this, slot, sourceNode, sourceSlotType, optsIn);
sourceSlotType: ISlotType,
optsIn?: ConnectByTypeOptions,
): LLink | null {
let canConnect = super.connectByTypeOutput?.call(
this,
slot,
sourceNode,
sourceSlotType,
optsIn,
);
if (!super.connectByType) {
canConnect = LGraphNode.prototype.connectByTypeOutput.call(
this,
@@ -426,7 +431,7 @@ const contextNodes = [
ContextMergeNode,
ContextMergeBigNode,
];
const contextTypeToServerDef: { [type: string]: ComfyObjectInfo } = {};
const contextTypeToServerDef: {[type: string]: ComfyObjectInfo} = {};
function fixBadConfigs(node: ContextNode) {
// Dumb mistake, but let's fix our mispelling. This will probably need to stay in perpetuity to
+8 -7
View File
@@ -1,9 +1,10 @@
import { app } from "scripts/app.js";
import { ComfyWidgets } from "scripts/widgets.js";
import type { LGraphNode as TLGraphNode } from "typings/litegraph.js";
import type { ComfyApp, ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import { addConnectionLayoutSupport } from "./utils.js";
import { rgthree } from "./rgthree.js";
import type {LGraphNode as TLGraphNode} from "@litegraph/litegraph.js";
import type {ComfyApp, ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import {app} from "scripts/app.js";
import {ComfyWidgets} from "scripts/widgets.js";
import {addConnectionLayoutSupport} from "./utils.js";
import {rgthree} from "./rgthree.js";
let hasShownAlertForUpdatingInt = false;
@@ -22,7 +23,7 @@ app.registerExtension({
(this as any).showValueWidget = ComfyWidgets["STRING"](
this,
"output",
["STRING", { multiline: true }],
["STRING", {multiline: true}],
app,
).widget;
(this as any).showValueWidget.inputEl!.readOnly = true;
+14 -9
View File
@@ -1,3 +1,13 @@
import type {
IContextMenuValue,
IFoundSlot,
INodeInputSlot,
INodeOutputSlot,
ISlotType,
LGraphNode,
LLink,
} from "@litegraph/litegraph.js";
import {app} from "scripts/app.js";
import {
IoDirection,
@@ -12,7 +22,6 @@ import {
} from "./services/context_service.js";
import {NodeTypesString} from "./constants.js";
import {removeUnusedInputsFromEnd} from "./utils_inputs_outputs.js";
import {INodeInputSlot, INodeOutputSlot, INodeSlot, LGraphNode, LLink} from "typings/litegraph.js";
import {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import {DynamicContextNodeBase} from "./dynamic_context_base.js";
import {SERVICE as CONFIG_SERVICE} from "./services/config_service.js";
@@ -40,11 +49,11 @@ export class DynamicContextNode extends DynamicContextNodeBase {
}
override onConnectionsChange(
type: number,
type: ISlotType,
slotIndex: number,
isConnected: boolean,
link: LLink,
ioSlot: INodeSlot,
link: LLink | null | undefined,
ioSlot: INodeInputSlot | INodeOutputSlot,
): void {
super.onConnectionsChange?.call(this, type, slotIndex, isConnected, link, ioSlot);
if (this.configuring) {
@@ -225,11 +234,7 @@ export class DynamicContextNode extends DynamicContextNodeBase {
}
}
override getSlotMenuOptions(slot: {
slot: number;
input?: INodeInputSlot | undefined;
output?: INodeOutputSlot | undefined;
}) {
override getSlotMenuOptions(slot: IFoundSlot): IContextMenuValue[] {
const editable = this.isOwnedInput(slot.input!.name) && this.type !== "*";
return [
{
+3 -3
View File
@@ -1,4 +1,4 @@
import type {INodeInputSlot} from "typings/litegraph.js";
import type {INodeInputSlot} from "@litegraph/litegraph.js";
import {BaseContextNode} from "./context.js";
import {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
@@ -19,7 +19,7 @@ const REGEX_EMPTY_INPUT = /^\+\s*$/;
export type InputLike = {
name: string;
type: string | -1;
type: number | string;
label?: string;
link: number | null;
removable?: boolean;
@@ -96,7 +96,7 @@ export class DynamicContextNodeBase extends BaseContextNode {
}
}
override clone() {
const cloned = super.clone();
const cloned = super.clone()! as DynamicContextNodeBase;
while (cloned.inputs.length > 1) {
cloned.removeInput(cloned.inputs.length - 1);
}
+3 -9
View File
@@ -1,5 +1,5 @@
import type {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import type {INodeSlot, LGraphNode, LLink, LGraphCanvas} from "typings/litegraph.js";
import type {INodeSlot, LGraphNode, LLink, LGraphCanvas, INodeInputSlot, INodeOutputSlot, ISlotType} from "@litegraph/litegraph.js";
import {app} from "scripts/app.js";
import {DynamicContextNodeBase, InputLike} from "./dynamic_context_base.js";
@@ -65,14 +65,8 @@ class DynamicContextSwitchNode extends DynamicContextNodeBase {
this.scheduleHardRefresh();
}
override onConnectionsChange(
type: number,
slotIndex: number,
isConnected: boolean,
link: LLink,
ioSlot: INodeSlot,
): void {
super.onConnectionsChange?.call(this, type, slotIndex, isConnected, link, ioSlot);
override onConnectionsChange(type: ISlotType, slotIndex: number, isConnected: boolean, link: LLink | null | undefined, inputOrOutput: INodeInputSlot | INodeOutputSlot): void {
super.onConnectionsChange?.call(this, type, slotIndex, isConnected, link, inputOrOutput);
if (this.configuring) {
return;
}
+49 -39
View File
@@ -1,13 +1,14 @@
import type { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import type { ComfyApp, ComfyWidget } from "typings/comfy.js";
import type { IWidget, LGraph, LGraphNode, SerializedLGraphNode } from "typings/litegraph.js";
import type { RgthreeBaseNode } from "./base_node.js";
import type {LGraph, LGraphNode} from "@litegraph/litegraph.js";
import type {IButtonWidget, IComboWidget, IWidget} from "@litegraph/types/widgets.js";
import type {ComfyApp} from "typings/comfy.js";
import type {RgthreeBaseNode} from "./base_node.js";
import { app } from "scripts/app.js";
import { BaseAnyInputConnectedNode } from "./base_any_input_connected_node.js";
import { NodeTypesString } from "./constants.js";
import { addMenuItem } from "./utils.js";
import { rgthree } from "./rgthree.js";
import {app} from "scripts/app.js";
import {BaseAnyInputConnectedNode} from "./base_any_input_connected_node.js";
import {NodeTypesString} from "./constants.js";
import {addMenuItem} from "./utils.js";
import {rgthree} from "./rgthree.js";
import {ISerialisedNode} from "@litegraph/types/serialisation.js";
const MODE_ALWAYS = 0;
const MODE_MUTE = 2;
@@ -27,12 +28,12 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
readonly logger = rgthree.newLogSession("[FastActionsButton]");
static "@buttonText" = { type: "string" };
static "@buttonText" = {type: "string"};
static "@shortcutModifier" = {
type: "combo",
values: ["ctrl", "alt", "shift"],
};
static "@shortcutKey" = { type: "string" };
static "@shortcutKey" = {type: "string"};
static collapsible = false;
@@ -40,9 +41,9 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
override serialize_widgets = true;
readonly buttonWidget: IWidget;
readonly buttonWidget: IButtonWidget;
readonly widgetToData = new Map<IWidget, { comfy?: ComfyApp; node?: LGraphNode }>();
readonly widgetToData = new Map<IWidget, {comfy?: ComfyApp; node?: LGraphNode}>();
readonly nodeIdtoFunctionCache = new Map<number, string>();
readonly keypressBound;
@@ -50,6 +51,12 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
private executingFromShortcut = false;
override properties!: BaseAnyInputConnectedNode["properties"] & {
buttonText: string;
shortcutModifier: string;
shortcutKey: string;
};
constructor(title?: string) {
super(title);
this.properties["buttonText"] = "🎬 Action!";
@@ -58,12 +65,12 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
this.buttonWidget = this.addWidget(
"button",
this.properties["buttonText"],
null,
"",
() => {
this.executeConnectedNodes();
},
{ serialize: false },
);
{serialize: false},
) as IButtonWidget;
this.keypressBound = this.onKeypress.bind(this);
this.keyupBound = this.onKeyup.bind(this);
@@ -71,7 +78,7 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
/** When we're given data to configure, like from a PNG or JSON. */
override configure(info: SerializedLGraphNode<LGraphNode>): void {
override configure(info: ISerialisedNode): void {
super.configure(info);
// Since we add the widgets dynamically, we need to wait to set their values
// with a short timeout.
@@ -79,7 +86,7 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
if (info.widgets_values) {
for (let [index, value] of info.widgets_values.entries()) {
if (index > 0) {
if (value.startsWith("comfy_action:")) {
if (typeof value === "string" && value.startsWith("comfy_action:")) {
value = value.replace("comfy_action:", "");
this.addComfyActionWidget(index, value);
}
@@ -93,7 +100,7 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
override clone() {
const cloned = super.clone();
const cloned = super.clone()!;
cloned.properties["buttonText"] = "🎬 Action!";
cloned.properties["shortcutKey"] = "";
return cloned;
@@ -149,14 +156,14 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
this.executingFromShortcut = false;
}
override onPropertyChanged(property: string, value: any, _prevValue: any): boolean | void {
if (property == "buttonText") {
override onPropertyChanged(property: string, value: unknown, prevValue?: unknown) {
if (property == "buttonText" && typeof value === "string") {
this.buttonWidget.name = value;
}
if (property == "shortcutKey") {
value = value.trim();
this.properties["shortcutKey"] = (value && value[0].toLowerCase()) || "";
if (property == "shortcutKey" && typeof value === "string") {
this.properties["shortcutKey"] = value.trim()[0]?.toLowerCase() ?? "";
}
return true;
}
override handleLinkedNodesStabilization(linkedNodes: LGraphNode[]) {
@@ -212,10 +219,10 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
widget = this.addWidget("combo", node.title, "None", "", {
values: ["None", "Mute", "Bypass", "Enable", ...exposedActions],
});
(widget as ComfyWidget).serializeValue = async (_node: LGraphNode, _index: number) => {
widget.serializeValue = async (_node: LGraphNode, _index: number) => {
return widget?.value;
};
this.widgetToData.set(widget, { node });
this.widgetToData.set(widget, {node});
changed = true;
}
}
@@ -250,7 +257,7 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
continue;
}
const action = widget.value;
const { comfy, node } = this.widgetToData.get(widget) ?? {};
const {comfy, node} = this.widgetToData.get(widget) ?? {};
if (comfy) {
if (action === "Queue Prompt") {
await comfy.queuePrompt(0);
@@ -267,6 +274,9 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
// If there's a handleAction, always call it.
if ((node as RgthreeBaseNode).handleAction) {
if (typeof action !== 'string') {
throw new Error('Fast Actions Button action should be a string: ' + action);
}
await (node as RgthreeBaseNode).handleAction(action);
}
app.graph.change();
@@ -285,24 +295,24 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
"Comfy Action",
"None",
() => {
if (widget.value.startsWith("MOVE ")) {
if (String(widget.value).startsWith("MOVE ")) {
this.widgets.push(this.widgets.splice(this.widgets.indexOf(widget), 1)[0]!);
widget.value = (widget as any)["lastValue_"];
} else if (widget.value.startsWith("REMOVE ")) {
widget.value = String(widget.rgthree_lastValue);
} else if (String(widget.value).startsWith("REMOVE ")) {
this.removeWidget(widget);
}
(widget as any)["lastValue_"] = widget.value;
widget.rgthree_lastValue = widget.value;
},
{
values: ["None", "Queue Prompt", "REMOVE Comfy Action", "MOVE to end"],
},
);
(widget as any)["lastValue_"] = value;
) as IComboWidget;
widget.rgthree_lastValue = value;
(widget as ComfyWidget).serializeValue = async (_node: LGraphNode, _index: number) => {
widget.serializeValue = async (_node: LGraphNode, _index: number) => {
return `comfy_app:${widget?.value}`;
};
this.widgetToData.set(widget, { comfy: app });
this.widgetToData.set(widget, {comfy: app});
if (slot != null) {
this.widgets.splice(slot, 0, this.widgets.splice(this.widgets.indexOf(widget), 1)[0]!);
@@ -310,11 +320,11 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
return widget;
}
override onSerialize(o: SerializedLGraphNode) {
super.onSerialize && super.onSerialize(o);
for (let [index, value] of (o.widgets_values || []).entries()) {
override onSerialize(serialised: ISerialisedNode) {
super.onSerialize?.(serialised);
for (let [index, value] of (serialised.widgets_values || []).entries()) {
if (this.widgets[index]?.name === "Comfy Action") {
o.widgets_values![index] = `comfy_action:${value}`;
serialised.widgets_values![index] = `comfy_action:${value}`;
}
}
}
+3 -2
View File
@@ -1,4 +1,5 @@
import { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import type { Size } from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import { NodeTypesString } from "./constants.js";
import { BaseFastGroupsModeChanger } from "./fast_groups_muter.js";
@@ -31,7 +32,7 @@ app.registerExtension({
},
loadedGraphNode(node: FastGroupsBypasser) {
if (node.type == FastGroupsBypasser.title) {
node.tempSize = [...node.size];
node.tempSize = [...node.size] as Size;
}
},
});
+30 -15
View File
@@ -1,17 +1,21 @@
import type {
LGraphNode,
LGraph as TLGraph,
LGraphCanvas as TLGraphCanvas,
Vector2,
Size,
} from "@litegraph/litegraph.js";
import type { ISerialisedNode } from "@litegraph/types/serialisation.js";
import type { IBooleanWidget } from "@litegraph/types/widgets";
import type { Point } from "@litegraph/interfaces.js";
import { app } from "scripts/app.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { NodeTypesString } from "./constants.js";
import {
type LGraphNode,
type LGraph as TLGraph,
LGraphCanvas as TLGraphCanvas,
Vector2,
SerializedLGraphNode,
IWidget,
} from "typings/litegraph.js";
import { SERVICE as FAST_GROUPS_SERVICE } from "./services/fast_groups_service.js";
import { drawNodeWidget, fitString } from "./utils_canvas.js";
import { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import { CanvasMouseEvent } from "@litegraph/types/events.js";
const PROPERTY_SORT = "sort";
const PROPERTY_SORT_CUSTOM_ALPHA = "customSortAlphabet";
@@ -49,6 +53,15 @@ export abstract class BaseFastGroupsModeChanger extends RgthreeBaseVirtualNode {
};
static "@customSortAlphabet" = { type: "string" };
override properties!: RgthreeBaseVirtualNode["properties"] & {
[PROPERTY_MATCH_COLORS]: string;
[PROPERTY_MATCH_TITLE]: string;
[PROPERTY_SHOW_NAV]: boolean;
[PROPERTY_SORT]: string;
[PROPERTY_SORT_CUSTOM_ALPHA]: string;
[PROPERTY_RESTRICTION]: string;
};
static "@toggleRestriction" = {
type: "combo",
values: ["default", "max one", "always one"],
@@ -69,7 +82,7 @@ export abstract class BaseFastGroupsModeChanger extends RgthreeBaseVirtualNode {
return super.onConstructed();
}
override configure(info: SerializedLGraphNode<LGraphNode>): void {
override configure(info: ISerialisedNode): void {
// Patch a small issue (~14h) where multiple OPT_CONNECTIONS may have been created.
// https://github.com/rgthree/rgthree-comfy/issues/206
// TODO: This can probably be removed within a few weeks.
@@ -187,9 +200,11 @@ export abstract class BaseFastGroupsModeChanger extends RgthreeBaseVirtualNode {
if (!widget) {
// When we add a widget, litegraph is going to mess up the size, so we
// store it so we can retrieve it in computeSize. Hacky..
this.tempSize = [...this.size];
widget = this.addCustomWidget<IWidget<boolean>>({
this.tempSize = [...this.size] as Size;
widget = this.addCustomWidget<IBooleanWidget>({
name: "RGTHREE_TOGGLE_AND_NAV",
type: 'toggle',
y: 0,
label: "",
value: false,
disabled: false,
@@ -271,10 +286,10 @@ export abstract class BaseFastGroupsModeChanger extends RgthreeBaseVirtualNode {
}
}
},
serializeValue(serializedNode: SerializedLGraphNode, widgetIndex: number) {
serializeValue(serializedNode: ISerialisedNode, widgetIndex: number) {
return this.value;
},
mouse(event: PointerEvent, pos: Vector2, node: LGraphNode) {
mouse(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode) {
if (event.type == "pointerdown") {
if (
node.properties?.[PROPERTY_SHOW_NAV] !== false &&
@@ -496,7 +511,7 @@ app.registerExtension({
},
loadedGraphNode(node: LGraphNode) {
if (node.type == FastGroupsMuter.title) {
(node as FastGroupsMuter).tempSize = [...node.size];
(node as FastGroupsMuter).tempSize = [...node.size] as Point;
}
},
});
+6 -6
View File
@@ -2,10 +2,10 @@ import type {
LGraphCanvas as TLGraphCanvas,
LGraphGroup as TLGraphGroup,
LGraph as TLGraph,
AdjustedMouseEvent,
Vector2,
} from "typings/litegraph.js";
} from "@litegraph/litegraph.js";
import type {AdjustedMouseCustomEvent} from "typings/rgthree.js";
import type { CanvasMouseEvent } from "@litegraph/types/events.js";
import {app} from "scripts/app.js";
import {rgthree} from "./rgthree.js";
@@ -29,7 +29,7 @@ function getToggles() {
/**
* Determines if the user clicked on an fast header icon.
*/
function clickedOnToggleButton(e: AdjustedMouseEvent, group: TLGraphGroup): string | null {
function clickedOnToggleButton(e: CanvasMouseEvent, group: TLGraphGroup): string | null {
const toggles = getToggles();
const pos = group.pos;
const size = group.size;
@@ -139,7 +139,7 @@ app.registerExtension({
if (
!CONFIG_SERVICE.getFeatureValue("group_header_fast_toggle.enabled") ||
!rgthree.lastAdjustedMouseEvent
!rgthree.lastCanvasMouseEvent
) {
return;
}
@@ -150,8 +150,8 @@ app.registerExtension({
// Default to hover if not always.
if (CONFIG_SERVICE.getFeatureValue("group_header_fast_toggle.show") !== "always") {
const hoverGroup = graph.getGroupOnPos(
rgthree.lastAdjustedMouseEvent.canvasX,
rgthree.lastAdjustedMouseEvent.canvasY,
rgthree.lastCanvasMouseEvent.canvasX,
rgthree.lastCanvasMouseEvent.canvasY,
);
groups = hoverGroup ? [hoverGroup] : [];
} else {
+28 -25
View File
@@ -1,15 +1,16 @@
import {
LGraphCanvas,
LGraphNode,
Vector2,
} from "@litegraph/litegraph.js";
import type { CanvasMouseEvent } from "@litegraph/types/events.js";
import type { ISerialisedNode } from "@litegraph/types/serialisation.js";
import type { ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import { app } from "scripts/app.js";
import { api } from "scripts/api.js";
import { RgthreeBaseServerNode } from "./base_node.js";
import { NodeTypesString } from "./constants.js";
import { ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import {
AdjustedMouseEvent,
LGraphCanvas,
LGraphNode,
SerializedLGraphNode,
Vector2,
} from "typings/litegraph.js";
import { addConnectionLayoutSupport } from "./utils.js";
import {
RgthreeBaseHitAreas,
@@ -17,6 +18,7 @@ import {
RgthreeBaseWidgetBounds,
} from "./utils_widgets.js";
import { measureText } from "./utils_canvas.js";
import { Point, Size } from "@litegraph/interfaces.js";
type ComfyImageServerData = { filename: string; type: string; subfolder: string };
type ComfyImageData = { name: string; selected: boolean; url: string; img?: HTMLImageElement };
@@ -103,12 +105,12 @@ export class RgthreeImageComparer extends RgthreeBaseServerNode {
}
}
override onSerialize(o: SerializedLGraphNode) {
super.onSerialize && super.onSerialize(o);
for (let [index, widget_value] of (o.widgets_values || []).entries()) {
override onSerialize(serialised: ISerialisedNode) {
super.onSerialize && super.onSerialize(serialised);
for (let [index, widget_value] of (serialised.widgets_values || []).entries()) {
if (this.widgets[index]?.name === "rgthree_comparer") {
o.widgets_values![index] = (
this.widgets[index] as RgthreeImageComparerWidget
serialised.widgets_values![index] = (
this.widgets[index] as unknown as RgthreeImageComparerWidget
).value.images.map((d) => {
d = { ...d };
delete d.img;
@@ -145,26 +147,27 @@ export class RgthreeImageComparer extends RgthreeBaseServerNode {
}
}
override onMouseDown(event: MouseEvent, pos: Vector2, graphCanvas: LGraphCanvas): void {
super.onMouseDown?.(event, pos, graphCanvas);
override onMouseDown(event: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): boolean {
super.onMouseDown?.(event, pos, canvas);
this.setIsPointerDown(true);
return false;
}
override onMouseEnter(event: MouseEvent, pos: Vector2, graphCanvas: LGraphCanvas): void {
super.onMouseEnter?.(event, pos, graphCanvas);
override onMouseEnter(event: CanvasMouseEvent): void {
super.onMouseEnter?.(event);
this.setIsPointerDown(!!app.canvas.pointer_is_down);
this.isPointerOver = true;
}
override onMouseLeave(event: MouseEvent, pos: Vector2, graphCanvas: LGraphCanvas): void {
super.onMouseLeave?.(event, pos, graphCanvas);
override onMouseLeave(event: CanvasMouseEvent): void {
super.onMouseLeave?.(event);
this.setIsPointerDown(false);
this.isPointerOver = false;
}
override onMouseMove(event: MouseEvent, pos: Vector2, graphCanvas: LGraphCanvas): void {
super.onMouseMove?.(event, pos, graphCanvas);
this.pointerOverPos = [...pos];
override onMouseMove(event: MouseEvent, pos: Point, canvas: LGraphCanvas): void {
super.onMouseMove?.(event, pos, canvas);
this.pointerOverPos = [...pos] as Point;
this.imageIndex = this.pointerOverPos[0] > this.size[0] / 2 ? 1 : 0;
}
@@ -364,7 +367,7 @@ class RgthreeImageComparerWidget extends RgthreeBaseWidget<RgthreeImageComparerW
}
private onSelectionDown(
event: AdjustedMouseEvent,
event: CanvasMouseEvent,
pos: Vector2,
node: LGraphNode,
bounds?: RgthreeBaseWidgetBounds,
@@ -387,7 +390,7 @@ class RgthreeImageComparerWidget extends RgthreeBaseWidget<RgthreeImageComparerW
if (!image?.img?.naturalWidth || !image?.img?.naturalHeight) {
return;
}
let [nodeWidth, nodeHeight] = this.node.size;
let [nodeWidth, nodeHeight] = this.node.size as [number, number];
const imageAspect = image?.img.naturalWidth / image?.img.naturalHeight;
let height = nodeHeight - y;
const widgetAspect = nodeWidth / height;
@@ -452,7 +455,7 @@ class RgthreeImageComparerWidget extends RgthreeBaseWidget<RgthreeImageComparerW
return [width, 20];
}
serializeValue(serializedNode: SerializedLGraphNode, widgetIndex: number) {
override serializeValue(node: LGraphNode, index: number): RgthreeImageComparerWidgetValue | Promise<RgthreeImageComparerWidgetValue> {
const v = [];
for (const data of this._value.images) {
// Remove the img since it can't serialize.
+8 -6
View File
@@ -1,8 +1,10 @@
import { app } from "scripts/app.js";
import type { ComfyApp, ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import { RgthreeBaseServerNode } from "./base_node.js";
import { LGraph, LGraphNode, SerializedLGraphNode } from "typings/litegraph.js";
import { NodeTypesString } from "./constants.js";
import type {LGraph} from "@litegraph/litegraph.js";
import type {ComfyApp, ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import type {ISerialisedNode} from "@litegraph/types/serialisation.js";
import {app} from "scripts/app.js";
import {RgthreeBaseServerNode} from "./base_node.js";
import {NodeTypesString} from "./constants.js";
class ImageInsetCrop extends RgthreeBaseServerNode {
static override title = NodeTypesString.IMAGE_INSET_CROP;
@@ -26,7 +28,7 @@ class ImageInsetCrop extends RgthreeBaseServerNode {
this.setWidgetStep();
}
override configure(info: SerializedLGraphNode<LGraphNode>): void {
override configure(info: ISerialisedNode): void {
super.configure(info);
this.setWidgetStep();
}
+22 -17
View File
@@ -1,13 +1,13 @@
import { app } from "scripts/app.js";
import { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { NodeTypesString } from "./constants.js";
import type {
LGraphCanvas as TLGraphCanvas,
LGraphNode,
AdjustedMouseEvent,
Vector2,
} from "typings/litegraph.js";
} from "@litegraph/litegraph.js";
import type { CanvasMouseEvent } from "@litegraph/types/events.js";
import { app } from "scripts/app.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { NodeTypesString } from "./constants.js";
import { rgthree } from "./rgthree.js";
/**
@@ -32,6 +32,16 @@ export class Label extends RgthreeBaseVirtualNode {
static "@padding" = { type: "number" };
static "@borderRadius" = { type: "number" };
override properties!: RgthreeBaseVirtualNode["properties"] & {
fontSize: number;
fontFamily: string;
fontColor: string;
textAlign: string;
backgroundColor: string;
padding: number;
borderRadius: number;
};
override resizable = false;
constructor(title = Label.title) {
@@ -92,7 +102,7 @@ export class Label extends RgthreeBaseVirtualNode {
ctx.restore();
}
override onDblClick(event: AdjustedMouseEvent, pos: Vector2, canvas: TLGraphCanvas) {
override onDblClick(event: CanvasMouseEvent, pos: Vector2, canvas: TLGraphCanvas) {
// Since everything we can do here is in the properties, let's pop open the properties panel.
LGraphCanvas.active_canvas.showShowNodePanel(this);
}
@@ -153,7 +163,7 @@ export class Label extends RgthreeBaseVirtualNode {
*/
const oldDrawNode = LGraphCanvas.prototype.drawNode;
LGraphCanvas.prototype.drawNode = function (node: LGraphNode, ctx: CanvasRenderingContext2D) {
if (node.constructor === Label) {
if (node.constructor === Label.prototype.constructor) {
// These get set very aggressively; maybe an extension is doing it. We'll just clear them out
// each time.
(node as Label).bgcolor = "transparent";
@@ -175,18 +185,13 @@ LGraphCanvas.prototype.drawNode = function (node: LGraphNode, ctx: CanvasRenderi
* click).
*/
const oldGetNodeOnPos = LGraph.prototype.getNodeOnPos;
LGraph.prototype.getNodeOnPos = function <T extends LGraphNode>(
x: number,
y: number,
nodes_list?: LGraphNode[],
margin?: number,
) {
LGraph.prototype.getNodeOnPos = function(x: number, y: number, nodes_list?: LGraphNode[]) {
if (
// processMouseDown always passes in the nodes_list
nodes_list &&
rgthree.processingMouseDown &&
rgthree.lastAdjustedMouseEvent?.type.includes("down") &&
rgthree.lastAdjustedMouseEvent?.which === 1
rgthree.lastCanvasMouseEvent?.type.includes("down") &&
rgthree.lastCanvasMouseEvent?.which === 1
) {
// Using the same logic from LGraphCanvas processMouseDown, let's see if we consider this a
// double click.
@@ -195,7 +200,7 @@ LGraph.prototype.getNodeOnPos = function <T extends LGraphNode>(
nodes_list = [...nodes_list].filter((n) => !(n instanceof Label) || !n.flags?.pinned);
}
}
return oldGetNodeOnPos.apply(this, [x, y, nodes_list, margin]) as T | null;
return oldGetNodeOnPos.apply(this, [x, y, nodes_list]);
};
// Register the extension.
+19 -18
View File
@@ -1,10 +1,11 @@
import { app } from "scripts/app.js";
import type {
ContextMenuItem,
LGraphNode,
ContextMenu,
IContextMenuOptions,
} from "typings/litegraph.js";
IContextMenuValue,
} from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import { rgthree } from "./rgthree.js";
import { SERVICE as CONFIG_SERVICE } from "./services/config_service.js";
@@ -22,7 +23,7 @@ app.registerExtension({
const existingContextMenu = LiteGraph.ContextMenu;
// @ts-ignore: TypeScript doesn't like this override.
LiteGraph.ContextMenu = function (values: ContextMenuItem[], options: IContextMenuOptions) {
LiteGraph.ContextMenu = function (values: IContextMenuValue[], options: IContextMenuOptions<string, {rgthree_doNotNest: boolean}>) {
const threshold = CONFIG_SERVICE.getConfigValue("features.menu_auto_nest.threshold", 20);
const enabled = CONFIG_SERVICE.getConfigValue("features.menu_auto_nest.subdirs", false);
@@ -34,7 +35,7 @@ app.registerExtension({
}
// If there's a rgthree_originalCallback, then we're nested and don't need to check things
// we only expect on the first nesting.
if (!options.parentMenu?.options.rgthree_originalCallback) {
if (!(options.parentMenu?.options as any)?.rgthree_originalCallback) {
// On first context menu, we require a callback and a flat list of options as strings.
if (!options?.callback) {
incompatible = `Skipping context menu auto nesting b/c a callback was expected.`;
@@ -53,16 +54,16 @@ app.registerExtension({
return existingContextMenu.apply(this as any, [...arguments] as any);
}
const folders: { [key: string]: ContextMenuItem[] } = {};
const specialOps: ContextMenuItem[] = [];
const folderless: ContextMenuItem[] = [];
const folders: { [key: string]: IContextMenuValue[] } = {};
const specialOps: IContextMenuValue[] = [];
const folderless: IContextMenuValue[] = [];
for (const value of values) {
if (!value) {
folderless.push(value);
continue;
}
const newValue = typeof value === "string" ? { content: value } : Object.assign({}, value);
newValue.rgthree_originalValue = value.rgthree_originalValue || value;
(newValue as any).rgthree_originalValue = (value as any).rgthree_originalValue || value;
const valueContent = newValue.content || '';
const splitBy = valueContent.indexOf("/") > -1 ? "/" : "\\";
const valueSplit = valueContent.split(splitBy);
@@ -81,24 +82,24 @@ app.registerExtension({
const foldersCount = Object.values(folders).length;
if (foldersCount > 0) {
// Propogate the original callback down through the options.
options.rgthree_originalCallback =
options.rgthree_originalCallback ||
options.parentMenu?.options.rgthree_originalCallback ||
(options as any).rgthree_originalCallback =
(options as any).rgthree_originalCallback ||
(options.parentMenu?.options as any)?.rgthree_originalCallback ||
options.callback;
const oldCallback = options.rgthree_originalCallback;
const oldCallback = (options as any)?.rgthree_originalCallback;
options.callback = undefined;
const newCallback = (
item: ContextMenuItem,
item: IContextMenuValue,
options: IContextMenuOptions,
event: MouseEvent,
parentMenu: ContextMenu | undefined,
node: LGraphNode,
) => {
oldCallback?.(item?.rgthree_originalValue!, options, event, undefined, node);
oldCallback?.((item as any)?.rgthree_originalValue!, options, event, undefined, node);
};
const [n, v] = logger.infoParts(`Nested folders found (${foldersCount}).`);
console[n]?.(...v);
const newValues: ContextMenuItem[] = [];
const newValues: IContextMenuValue[] = [];
for (const [folderName, folderValues] of Object.entries(folders)) {
newValues.push({
content: `📁 ${folderName}`,
@@ -114,7 +115,7 @@ app.registerExtension({
},
});
}
values = ([] as ContextMenuItem[]).concat(
values = ([] as IContextMenuValue[]).concat(
specialOps.map((f) => {
if (typeof f === "string") {
f = { content: f };
@@ -136,7 +137,7 @@ app.registerExtension({
options.scale = Math.max(app.canvas.ds?.scale || 1, 1);
}
const oldCtrResponse = existingContextMenu.call(this as any, values, options);
const oldCtrResponse = existingContextMenu.call(this as any, values, options as any);
// For some reason, LiteGraph calls submenus with "this.constructor" which no longer allows
// us to continue building deep nesting, as well as skips many other extensions (even
// ComfyUI's core extensions like translations) from working on submenus. It also removes
+12 -10
View File
@@ -1,13 +1,14 @@
import { app } from "scripts/app.js";
import type { LGraphCanvas, ContextMenuItem } from "typings/litegraph.js";
import type { ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import type {IContextMenuValue, LGraphCanvas} from "@litegraph/litegraph.js";
import type {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import {app} from "scripts/app.js";
const clipboardSupportedPromise = new Promise<boolean>(async (resolve) => {
try {
// MDN says to check this, but it doesn't work in Mozilla... however, in secure contexts
// (localhost included), it's given by default if the user has it flagged.. so we should be
// able to check in the latter ClipboardItem too.
const result = await navigator.permissions.query({ name: "clipboard-write" } as any);
const result = await navigator.permissions.query({name: "clipboard-write"} as any);
resolve(result.state === "granted");
return;
} catch (e) {
@@ -15,7 +16,7 @@ const clipboardSupportedPromise = new Promise<boolean>(async (resolve) => {
if (!navigator.clipboard.write) {
throw new Error();
}
new ClipboardItem({ "image/png": new Blob([], { type: "image/png" }) });
new ClipboardItem({"image/png": new Blob([], {type: "image/png"})});
resolve(true);
return;
} catch (e) {
@@ -36,16 +37,16 @@ app.registerExtension({
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function (
canvas: LGraphCanvas,
options: ContextMenuItem[],
) {
getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined;
options: (IContextMenuValue<unknown> | null)[],
): (IContextMenuValue<unknown> | null)[] {
options = getExtraMenuOptions?.call(this, canvas, options) ?? options;
// If we already have a copy image somehow, then let's skip ours.
if (this.imgs?.length) {
let img =
this.imgs[this.imageIndex || 0] || this.imgs[this.overIndex || 0] || this.imgs[0];
const foundIdx = options.findIndex((option) => option?.content?.includes("Copy Image"));
if (img && foundIdx === -1) {
const menuItem: ContextMenuItem = {
const menuItem: IContextMenuValue = {
content: "Copy Image (rgthree)",
callback: () => {
const canvas = document.createElement("canvas");
@@ -54,7 +55,7 @@ app.registerExtension({
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
canvas.toBlob((blob) => {
navigator.clipboard.write([new ClipboardItem({ "image/png": blob! })]);
navigator.clipboard.write([new ClipboardItem({"image/png": blob!})]);
});
},
};
@@ -66,6 +67,7 @@ app.registerExtension({
}
}
}
return options;
};
}
}
+19 -18
View File
@@ -1,16 +1,14 @@
import { app } from "scripts/app.js";
import type {
LGraphCanvas as TLGraphCanvas,
ContextMenuItem,
LGraphNode,
} from "typings/litegraph.js";
import type { ComfyNodeConstructor, ComfyObjectInfo } from "typings/comfy.js";
import { rgthree } from "./rgthree.js";
import { getOutputNodes } from "./utils.js";
import { SERVICE as CONFIG_SERVICE } from "./services/config_service.js";
import type {IContextMenuValue, LGraphCanvas as TLGraphCanvas} from "@litegraph/litegraph.js";
import type {ComfyNodeConstructor, ComfyObjectInfo} from "typings/comfy.js";
import {app} from "scripts/app.js";
import {rgthree} from "./rgthree.js";
import {getOutputNodes} from "./utils.js";
import {SERVICE as CONFIG_SERVICE} from "./services/config_service.js";
function showQueueNodesMenuIfOutputNodesAreSelected(existingOptions: ContextMenuItem[]) {
function showQueueNodesMenuIfOutputNodesAreSelected(
existingOptions: (IContextMenuValue<unknown> | null)[],
) {
if (CONFIG_SERVICE.getConfigValue("features.menu_queue_selected_nodes") === false) {
return;
}
@@ -30,15 +28,17 @@ function showQueueNodesMenuIfOutputNodesAreSelected(existingOptions: ContextMenu
existingOptions.splice(idx, 0, menuItem);
}
function showQueueGroupNodesMenuIfGroupIsSelected(existingOptions: ContextMenuItem[]) {
function showQueueGroupNodesMenuIfGroupIsSelected(
existingOptions: (IContextMenuValue<unknown> | null)[],
) {
if (CONFIG_SERVICE.getConfigValue("features.menu_queue_selected_nodes") === false) {
return;
}
const group =
rgthree.lastAdjustedMouseEvent &&
rgthree.lastCanvasMouseEvent &&
app.graph.getGroupOnPos(
rgthree.lastAdjustedMouseEvent.canvasX,
rgthree.lastAdjustedMouseEvent.canvasY,
rgthree.lastCanvasMouseEvent.canvasX,
rgthree.lastCanvasMouseEvent.canvasY,
);
const outputNodes = group && getOutputNodes(group._nodes);
@@ -68,11 +68,12 @@ app.registerExtension({
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function (
canvas: TLGraphCanvas,
options: ContextMenuItem[],
) {
getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined;
options: (IContextMenuValue<unknown> | null)[],
): (IContextMenuValue<unknown> | null)[] {
options = getExtraMenuOptions?.call(this, canvas, options) ?? options;
showQueueNodesMenuIfOutputNodesAreSelected(options);
showQueueGroupNodesMenuIfGroupIsSelected(options);
return options;
};
},
+1 -1
View File
@@ -1,7 +1,7 @@
import { app } from "scripts/app.js";
import { BaseNodeModeChanger } from "./base_node_mode_changer.js";
import { NodeTypesString } from "./constants.js";
import type { LGraphNode } from "typings/litegraph.js";
import type { LGraphNode } from "@litegraph/litegraph.js";
const MODE_MUTE = 2;
const MODE_ALWAYS = 0;
+25 -18
View File
@@ -1,19 +1,22 @@
import { app } from "scripts/app.js";
import type {
LLink,
LGraph,
ContextMenuItem,
LGraphCanvas,
SerializedLGraphNode,
LGraphNode as TLGraphNode,
IContextMenuOptions,
ContextMenu,
} from "typings/litegraph.js";
import { addConnectionLayoutSupport } from "./utils.js";
import { wait } from "rgthree/common/shared_utils.js";
import { ComfyWidgets } from "scripts/widgets.js";
import { BaseCollectorNode } from "./base_node_collector.js";
import { NodeTypesString } from "./constants.js";
IContextMenuValue,
Size,
} from "@litegraph/litegraph.js";
import type {ISerialisedNode} from "@litegraph/types/serialisation.js";
import type {Point} from "@litegraph/interfaces";
import {app} from "scripts/app.js";
import {addConnectionLayoutSupport} from "./utils.js";
import {wait} from "rgthree/common/shared_utils.js";
import {ComfyWidgets} from "scripts/widgets.js";
import {BaseCollectorNode} from "./base_node_collector.js";
import {NodeTypesString} from "./constants.js";
/**
* The Collector Node. Takes any number of inputs as connections for nodes and collects them into
@@ -36,7 +39,7 @@ class CollectorNode extends BaseCollectorNode {
return super.onConstructed();
}
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
override configure(info: ISerialisedNode) {
// Patch a small issue (~14h) where multiple OPT_CONNECTIONS may have been created.
// https://github.com/rgthree/rgthree-comfy/issues/206
// TODO: This can probably be removed within a few weeks.
@@ -58,7 +61,7 @@ class CombinerNode extends CollectorNode {
const note = ComfyWidgets["STRING"](
this,
"last_seed",
["STRING", { multiline: true }],
["STRING", {multiline: true}],
app,
).widget;
note.inputEl!.value =
@@ -69,11 +72,14 @@ class CombinerNode extends CollectorNode {
note.inputEl!.style.fontStyle = "italic";
note.inputEl!.style.opacity = "0.8";
this.getExtraMenuOptions = (_: LGraphCanvas, options: ContextMenuItem[]) => {
this.getExtraMenuOptions = (
canvas: LGraphCanvas,
options: (IContextMenuValue<unknown> | null)[],
): (IContextMenuValue<unknown> | null)[] => {
options.splice(options.length - 1, 0, {
content: "‼️ Update to Node Collector",
callback: (
_value: ContextMenuItem,
_value: IContextMenuValue,
_options: IContextMenuOptions,
_event: MouseEvent,
_parentMenu: ContextMenu | undefined,
@@ -82,10 +88,11 @@ class CombinerNode extends CollectorNode {
updateCombinerToCollector(this);
},
});
return options;
};
}
override configure(info: SerializedLGraphNode) {
override configure(info: ISerialisedNode) {
super.configure(info);
if (this.title != CombinerNode.title && !this.title.startsWith("‼️")) {
this.title = "‼️ " + this.title;
@@ -104,9 +111,9 @@ async function updateCombinerToCollector(node: TLGraphNode) {
newNode.title = node.title.replace("‼️ ", "");
}
// Port the position, size, and properties from the old node.
newNode.pos = [...node.pos];
newNode.size = [...node.size];
newNode.properties = { ...node.properties };
newNode.pos = [...node.pos] as Point;
newNode.size = [...node.size] as Size;
newNode.properties = {...node.properties};
// We now collect the links data, inputs and outputs, of the old node since these will be
// lost when we remove it.
const links: any[] = [];
@@ -115,7 +122,7 @@ async function updateCombinerToCollector(node: TLGraphNode) {
const link: LLink = (app.graph as LGraph).links[linkId]!;
if (!link) continue;
const targetNode = app.graph.getNodeById(link.target_id);
links.push({ node: newNode, slot: index, targetNode, targetSlot: link.target_slot });
links.push({node: newNode, slot: index, targetNode, targetSlot: link.target_slot});
}
}
for (const [index, input] of node.inputs.entries()) {
+12 -11
View File
@@ -1,14 +1,14 @@
import { app } from "scripts/app.js";
import type {
INodeInputSlot,
INodeOutputSlot,
LGraphCanvas,
LGraphEventMode,
LGraphNode,
LLink,
SerializedLGraphNode,
Vector2,
} from "typings/litegraph.js";
import type { NodeMode } from "typings/comfy.js";
} from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import {
PassThroughFollowing,
addConnectionLayoutSupport,
@@ -20,6 +20,7 @@ import { BaseCollectorNode } from "./base_node_collector.js";
import { NodeTypesString, stripRgthree } from "./constants.js";
import { fitString } from "./utils_canvas.js";
import { rgthree } from "./rgthree.js";
import { ISerialisedNode } from "@litegraph/types/serialisation.js";
const MODE_ALWAYS = 0;
const MODE_MUTE = 2;
@@ -97,7 +98,7 @@ class NodeModeRelay extends BaseCollectorNode {
return super.onConstructed();
}
override onModeChange(from: NodeMode, to: NodeMode) {
override onModeChange(from: LGraphEventMode | undefined, to: LGraphEventMode) {
super.onModeChange(from, to);
// If we aren't connected to anything, then we'll use our mode to relay when it changes.
if (this.inputs.length <= 1 && !this.isInputConnected(0) && this.isAnyOutputConnected()) {
@@ -108,7 +109,7 @@ class NodeModeRelay extends BaseCollectorNode {
}
}
override configure(info: SerializedLGraphNode<LGraphNode>): void {
override configure(info: ISerialisedNode): void {
// Patch a small issue (~14h) where multiple OPT_CONNECTIONS may have been created.
// https://github.com/rgthree/rgthree-comfy/issues/206
// TODO: This can probably be removed within a few weeks.
@@ -194,7 +195,7 @@ class NodeModeRelay extends BaseCollectorNode {
-1,
this.inputsPassThroughFollowing,
);
let mode: NodeMode | -99 | null = undefined;
let mode: LGraphEventMode | -99 | undefined = undefined;
for (const inputNode of inputNodes) {
// If we haven't set our mode to be, then let's set it. Otherwise, mode will stick if it
// remains constant, otherwise, if we hit an ALWAYS, then we'll unmute all repeaters and
@@ -206,7 +207,7 @@ class NodeModeRelay extends BaseCollectorNode {
} else if (inputNode.mode === MODE_ALWAYS || mode === MODE_ALWAYS) {
mode = MODE_ALWAYS;
} else {
mode = null;
mode = undefined;
}
}
@@ -219,11 +220,11 @@ class NodeModeRelay extends BaseCollectorNode {
/**
* Sends the mode to the repeater, checking to see if we're modifying our mode.
*/
private dispatchModeToRepeater(mode?: NodeMode | -99 | null) {
private dispatchModeToRepeater(mode?: LGraphEventMode | -99 | null) {
if (mode != null) {
const propertyVal = this.properties?.[MODE_TO_PROPERTY.get(mode) || ""];
const newMode = OPTION_TO_MODE.get(propertyVal);
mode = (newMode !== null ? newMode : mode) as NodeMode | -99;
const newMode = OPTION_TO_MODE.get(propertyVal as string);
mode = (newMode !== null ? newMode : mode) as LGraphEventMode | -99;
if (mode !== null && mode !== MODE_NOTHING) {
if (this.outputs?.length) {
const outputNodes = getConnectedOutputNodesAndFilterPassThroughs(this);
+9 -9
View File
@@ -1,22 +1,22 @@
import { app } from "scripts/app.js";
import { BaseCollectorNode } from "./base_node_collector.js";
import { NodeTypesString, stripRgthree } from "./constants.js";
import type {
INodeInputSlot,
INodeOutputSlot,
LGraphEventMode,
LGraphGroup,
LGraphNode,
LLink,
SerializedLGraphNode,
} from "typings/litegraph.js";
} from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import { BaseCollectorNode } from "./base_node_collector.js";
import { NodeTypesString, stripRgthree } from "./constants.js";
import {
PassThroughFollowing,
addConnectionLayoutSupport,
getConnectedInputNodesAndFilterPassThroughs,
getConnectedOutputNodesAndFilterPassThroughs,
} from "./utils.js";
import { NodeMode } from "typings/comfy.js";
import { ISerialisedNode } from "@litegraph/types/serialisation.js";
class NodeModeRepeater extends BaseCollectorNode {
override readonly inputsPassThroughFollowing: PassThroughFollowing = PassThroughFollowing.ALL;
@@ -42,7 +42,7 @@ class NodeModeRepeater extends BaseCollectorNode {
return super.onConstructed();
}
override configure(info: SerializedLGraphNode<LGraphNode>): void {
override configure(info: ISerialisedNode): void {
// Patch a small issue (~14h) where multiple OPT_CONNECTIONS may have been created.
// https://github.com/rgthree/rgthree-comfy/issues/206
// TODO: This can probably be removed within a few weeks.
@@ -159,7 +159,7 @@ class NodeModeRepeater extends BaseCollectorNode {
}
/** When a mode change, we want all connected nodes to match except for connected relays. */
override onModeChange(from: NodeMode, to: NodeMode) {
override onModeChange(from: LGraphEventMode | undefined, to: LGraphEventMode) {
super.onModeChange(from, to);
const linkedNodes = getConnectedInputNodesAndFilterPassThroughs(this).filter(
(node) => node.type !== NodeTypesString.NODE_MODE_RELAY,
+77 -77
View File
@@ -1,18 +1,21 @@
import { app } from "scripts/app.js";
import type {
ContextMenuItem,
LGraphNode as TLGraphNode,
IWidget,
LGraphCanvas,
SerializedLGraphNode,
Vector2,
AdjustedMouseEvent,
} from "typings/litegraph.js";
import type { ComfyObjectInfo, ComfyNodeConstructor } from "typings/comfy.js";
import { RgthreeBaseServerNode } from "./base_node.js";
import { rgthree } from "./rgthree.js";
import { addConnectionLayoutSupport } from "./utils.js";
import { NodeTypesString } from "./constants.js";
IContextMenuValue,
IFoundSlot,
} from "@litegraph/litegraph.js";
import type {CanvasMouseEvent} from "@litegraph/types/events.js";
import type {ISerialisedNode} from "@litegraph/types/serialisation.js";
import type {ICustomWidget} from "@litegraph/types/widgets";
import type {RgthreeModelInfo} from "typings/rgthree.js";
import type {ComfyObjectInfo, ComfyNodeConstructor} from "typings/comfy.js";
import {app} from "scripts/app.js";
import {RgthreeBaseServerNode} from "./base_node.js";
import {rgthree} from "./rgthree.js";
import {addConnectionLayoutSupport} from "./utils.js";
import {NodeTypesString} from "./constants.js";
import {
drawInfoIcon,
drawNumberWidgetPart,
@@ -27,12 +30,11 @@ import {
RgthreeBetterButtonWidget,
RgthreeDividerWidget,
} from "./utils_widgets.js";
import { rgthreeApi } from "rgthree/common/rgthree_api.js";
import { showLoraChooser } from "./utils_menu.js";
import { moveArrayItem, removeArrayItem } from "rgthree/common/shared_utils.js";
import { RgthreeLoraInfoDialog } from "./dialog_info.js";
import type { RgthreeModelInfo } from "typings/rgthree.js";
import { LORA_INFO_SERVICE } from "rgthree/common/model_info_service.js";
import {rgthreeApi} from "rgthree/common/rgthree_api.js";
import {showLoraChooser} from "./utils_menu.js";
import {moveArrayItem, removeArrayItem} from "rgthree/common/shared_utils.js";
import {RgthreeLoraInfoDialog} from "./dialog_info.js";
import {LORA_INFO_SERVICE} from "rgthree/common/model_info_service.js";
// import { RgthreePowerLoraChooserDialog } from "./dialog_power_lora_chooser.js";
const PROP_LABEL_SHOW_STRENGTHS = "Show Strengths";
@@ -62,7 +64,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
private loraWidgetsCounter = 0;
/** Keep track of the spacer, new lora widgets will go before it when it exists. */
private widgetButtonSpacer: IWidget | null = null;
private widgetButtonSpacer: ICustomWidget | null = null;
constructor(title = NODE_CLASS.title) {
super(title);
@@ -78,7 +80,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
* added in `onNodeCreated`, letting `super.configure` and do nothing, then create our lora
* widgets and, finally, add back in our default widgets.
*/
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
override configure(info: ISerialisedNode): void {
while (this.widgets?.length) this.removeWidget(0);
this.widgetButtonSpacer = null;
super.configure(info);
@@ -86,9 +88,9 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
(this as any)._tempWidth = this.size[0];
(this as any)._tempHeight = this.size[1];
for (const widgetValue of info.widgets_values || []) {
if (widgetValue?.lora !== undefined) {
if ((widgetValue as PowerLoraLoaderWidgetValue)?.lora !== undefined) {
const widget = this.addNewLoraWidget();
widget.value = { ...widgetValue };
widget.value = {...(widgetValue as PowerLoraLoaderWidgetValue)};
}
}
this.addNonLoraWidgets();
@@ -120,6 +122,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
if (this.widgetButtonSpacer) {
moveArrayItem(this.widgets, widget, this.widgets.indexOf(this.widgetButtonSpacer));
}
return widget;
}
@@ -127,25 +130,23 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
private addNonLoraWidgets() {
moveArrayItem(
this.widgets,
this.addCustomWidget(
new RgthreeDividerWidget({ marginTop: 4, marginBottom: 0, thickness: 0 }),
),
this.addCustomWidget(new RgthreeDividerWidget({marginTop: 4, marginBottom: 0, thickness: 0})),
0,
);
moveArrayItem(this.widgets, this.addCustomWidget(new PowerLoraLoaderHeaderWidget()), 1);
this.widgetButtonSpacer = this.addCustomWidget(
new RgthreeDividerWidget({ marginTop: 4, marginBottom: 0, thickness: 0 }),
new RgthreeDividerWidget({marginTop: 4, marginBottom: 0, thickness: 0}),
);
this.addCustomWidget(
new RgthreeBetterButtonWidget(
"➕ Add Lora",
(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) => {
(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) => {
rgthreeApi.getLoras().then((loras) => {
showLoraChooser(
event as PointerEvent,
(value: ContextMenuItem | string) => {
event as MouseEvent,
(value: IContextMenuValue | string) => {
if (typeof value === "string") {
if (value.includes("Power Lora Chooser")) {
// new RgthreePowerLoraChooserDialog().show();
@@ -206,7 +207,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
}
// Only care about lora widget clicks.
if (lastWidget?.name?.startsWith("lora_")) {
return { widget: lastWidget, output: { type: "LORA WIDGET" } };
return {widget: lastWidget, output: {type: "LORA WIDGET"}};
}
}
return slot;
@@ -216,7 +217,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
* Working with the overridden `getSlotInPosition` above, this method checks if the passed in
* option is actually a widget from it and then hijacks the context menu all together.
*/
override getSlotMenuOptions(slot: any): ContextMenuItem[] | null {
override getSlotMenuOptions(slot: IFoundSlot) {
// Oddly, LiteGraph doesn't call back into our node with a custom menu (even though it let's us
// define a custom menu to begin with... wtf?). So, we'll return null so the default is not
// triggered and then we'll just show one ourselves because.. yea.
@@ -225,7 +226,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
const index = this.widgets.indexOf(widget);
const canMoveUp = !!this.widgets[index - 1]?.name?.startsWith("lora_");
const canMoveDown = !!this.widgets[index + 1]?.name?.startsWith("lora_");
const menuItems: ContextMenuItem[] = [
const menuItems: (IContextMenuValue|null)[] = [
{
content: `ℹ️ Show Info`,
callback: () => {
@@ -260,15 +261,14 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
},
},
];
let canvas = app.canvas as LGraphCanvas;
new LiteGraph.ContextMenu(
menuItems,
{ title: "LORA WIDGET", event: rgthree.lastAdjustedMouseEvent! },
canvas.getCanvasWindow(),
{title: "LORA WIDGET", event: rgthree.lastCanvasMouseEvent!}
);
return null;
// [🤮] ComfyUI doesn't have a possible return type as falsy, even though the impl skips the
// menu when the return is falsy. Casting as any.
return undefined as any;
}
return this.defaultGetSlotMenuOptions(slot);
}
@@ -296,7 +296,7 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
let allOff = true;
for (const widget of this.widgets) {
if (widget.name?.startsWith("lora_")) {
const on = widget.value?.on;
const on = (widget.value as any)?.on;
allOn = allOn && on === true;
allOff = allOff && on === false;
if (!allOn && !allOff) {
@@ -314,8 +314,8 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
const allOn = this.allLorasState();
const toggledTo = !allOn ? true : false;
for (const widget of this.widgets) {
if (widget.name?.startsWith("lora_")) {
widget.value.on = toggledTo;
if (widget.name?.startsWith("lora_") && (widget.value as any)?.on != null) {
(widget.value as any).on = toggledTo;
}
}
}
@@ -371,13 +371,13 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
* The PowerLoraLoaderHeaderWidget that renders a toggle all switch, as well as some title info
* (more necessary for the double model & clip strengths to label them).
*/
class PowerLoraLoaderHeaderWidget extends RgthreeBaseWidget<{ type: string }> {
class PowerLoraLoaderHeaderWidget extends RgthreeBaseWidget<{type: string}> {
private showModelAndClip: boolean | null = null;
value = { type: "PowerLoraLoaderHeaderWidget" };
value = {type: "PowerLoraLoaderHeaderWidget"};
protected override hitAreas: RgthreeBaseHitAreas<"toggle"> = {
toggle: { bounds: [0, 0] as Vector2, onDown: this.onToggleDown },
toggle: {bounds: [0, 0] as Vector2, onDown: this.onToggleDown},
};
constructor(name: string = "PowerLoraLoaderHeaderWidget") {
@@ -408,7 +408,7 @@ class PowerLoraLoaderHeaderWidget extends RgthreeBaseWidget<{ type: string }> {
const midY = posY + height * 0.5;
let posX = 10;
ctx.save();
this.hitAreas.toggle.bounds = drawTogglePart(ctx, { posX, posY, height, value: allLoraState });
this.hitAreas.toggle.bounds = drawTogglePart(ctx, {posX, posY, height, value: allLoraState});
if (!lowQuality) {
posX += this.hitAreas.toggle.bounds[1] + innerMargin;
@@ -437,7 +437,7 @@ class PowerLoraLoaderHeaderWidget extends RgthreeBaseWidget<{ type: string }> {
/**
* Handles a pointer down on the toggle's defined hit area.
*/
onToggleDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onToggleDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
(node as RgthreePowerLoraLoader).toggleAllLoras();
this.cancelMouseDown();
return true;
@@ -482,19 +482,19 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
| "strengthTwoInc"
| "strengthTwoAny"
> = {
toggle: { bounds: [0, 0] as Vector2, onDown: this.onToggleDown },
lora: { bounds: [0, 0] as Vector2, onDown: this.onLoraDown },
toggle: {bounds: [0, 0] as Vector2, onDown: this.onToggleDown},
lora: {bounds: [0, 0] as Vector2, onDown: this.onLoraDown},
// info: { bounds: [0, 0] as Vector2, onDown: this.onInfoDown },
strengthDec: { bounds: [0, 0] as Vector2, onDown: this.onStrengthDecDown },
strengthVal: { bounds: [0, 0] as Vector2, onUp: this.onStrengthValUp },
strengthInc: { bounds: [0, 0] as Vector2, onDown: this.onStrengthIncDown },
strengthAny: { bounds: [0, 0] as Vector2, onMove: this.onStrengthAnyMove },
strengthDec: {bounds: [0, 0] as Vector2, onDown: this.onStrengthDecDown},
strengthVal: {bounds: [0, 0] as Vector2, onUp: this.onStrengthValUp},
strengthInc: {bounds: [0, 0] as Vector2, onDown: this.onStrengthIncDown},
strengthAny: {bounds: [0, 0] as Vector2, onMove: this.onStrengthAnyMove},
strengthTwoDec: { bounds: [0, 0] as Vector2, onDown: this.onStrengthTwoDecDown },
strengthTwoVal: { bounds: [0, 0] as Vector2, onUp: this.onStrengthTwoValUp },
strengthTwoInc: { bounds: [0, 0] as Vector2, onDown: this.onStrengthTwoIncDown },
strengthTwoAny: { bounds: [0, 0] as Vector2, onMove: this.onStrengthTwoAnyMove },
strengthTwoDec: {bounds: [0, 0] as Vector2, onDown: this.onStrengthTwoDecDown},
strengthTwoVal: {bounds: [0, 0] as Vector2, onUp: this.onStrengthTwoValUp},
strengthTwoInc: {bounds: [0, 0] as Vector2, onDown: this.onStrengthTwoIncDown},
strengthTwoAny: {bounds: [0, 0] as Vector2, onMove: this.onStrengthTwoAnyMove},
};
constructor(name: string) {
@@ -512,7 +512,7 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
this._value = v;
// In case widgets are messed up, we can correct course here.
if (typeof this._value !== "object") {
this._value = { ...DEFAULT_LORA_WIDGET_DATA };
this._value = {...DEFAULT_LORA_WIDGET_DATA};
if (this.showModelAndClip) {
this._value.strengthTwo = this._value.strength;
}
@@ -562,10 +562,10 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
let posX = margin;
// Draw the background.
drawRoundedRectangle(ctx, { posX, posY, height, width: node.size[0] - margin * 2 });
drawRoundedRectangle(ctx, {posX, posY, height, width: node.size[0] - margin * 2});
// Draw the toggle
this.hitAreas.toggle.bounds = drawTogglePart(ctx, { posX, posY, height, value: this.value.on });
this.hitAreas.toggle.bounds = drawTogglePart(ctx, {posX, posY, height, value: this.value.on});
posX += this.hitAreas.toggle.bounds[1] + innerMargin;
// If low quality, then we're done rendering.
@@ -586,8 +586,8 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
let rposX = node.size[0] - margin - innerMargin - innerMargin;
const strengthValue = this.showModelAndClip
? this.value.strengthTwo ?? 1
: this.value.strength ?? 1;
? (this.value.strengthTwo ?? 1)
: (this.value.strength ?? 1);
let textColor: string | undefined = undefined;
if (this.loraInfo?.strengthMax != null && strengthValue > this.loraInfo?.strengthMax) {
@@ -673,8 +673,8 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
ctx.restore();
}
serializeValue(serializedNode: SerializedLGraphNode, widgetIndex: number) {
const v = { ...this.value };
override serializeValue(node: TLGraphNode, index: number): PowerLoraLoaderWidgetValue | Promise<PowerLoraLoaderWidgetValue> {
const v = {...this.value};
// Never send the second value to the backend if we're not showing it, otherwise, let's just
// make sure it's not null.
if (!this.showModelAndClip) {
@@ -686,18 +686,18 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
return v;
}
onToggleDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onToggleDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.value.on = !this.value.on;
this.cancelMouseDown(); // Clear the down since we handle it.
return true;
}
onInfoDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onInfoDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.showLoraInfoDialog();
}
onLoraDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
showLoraChooser(event, (value: ContextMenuItem) => {
onLoraDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
showLoraChooser(event, (value: IContextMenuValue) => {
if (typeof value === "string") {
this.value.lora = value;
this.loraInfo = null;
@@ -708,28 +708,28 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
this.cancelMouseDown();
}
onStrengthDecDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthDecDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.stepStrength(-1, false);
}
onStrengthIncDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthIncDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.stepStrength(1, false);
}
onStrengthTwoDecDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthTwoDecDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.stepStrength(-1, true);
}
onStrengthTwoIncDown(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthTwoIncDown(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.stepStrength(1, true);
}
onStrengthAnyMove(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthAnyMove(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.doOnStrengthAnyMove(event, false);
}
onStrengthTwoAnyMove(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthTwoAnyMove(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.doOnStrengthAnyMove(event, true);
}
private doOnStrengthAnyMove(event: AdjustedMouseEvent, isTwo = false) {
private doOnStrengthAnyMove(event: CanvasMouseEvent, isTwo = false) {
if (event.deltaX) {
let prop: "strengthTwo" | "strength" = isTwo ? "strengthTwo" : "strength";
this.haveMouseMovedStrength = true;
@@ -737,22 +737,22 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
}
}
onStrengthValUp(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthValUp(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.doOnStrengthValUp(event, false);
}
onStrengthTwoValUp(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode) {
onStrengthTwoValUp(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode) {
this.doOnStrengthValUp(event, true);
}
private doOnStrengthValUp(event: AdjustedMouseEvent, isTwo = false) {
private doOnStrengthValUp(event: CanvasMouseEvent, isTwo = false) {
if (this.haveMouseMovedStrength) return;
let prop: "strengthTwo" | "strength" = isTwo ? "strengthTwo" : "strength";
const canvas = app.canvas as LGraphCanvas;
canvas.prompt("Value", this.value[prop], (v: string) => (this.value[prop] = Number(v)), event);
}
override onMouseUp(event: AdjustedMouseEvent, pos: Vector2, node: TLGraphNode): boolean | void {
override onMouseUp(event: CanvasMouseEvent, pos: Vector2, node: TLGraphNode): boolean | void {
super.onMouseUp(event, pos, node);
this.haveMouseMovedStrength = false;
}
@@ -762,7 +762,7 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget<PowerLoraLoaderWidgetValue
return;
}
const infoDialog = new RgthreeLoraInfoDialog(this.value.lora).show();
infoDialog.addEventListener("close", ((e: CustomEvent<{ dirty: boolean }>) => {
infoDialog.addEventListener("close", ((e: CustomEvent<{dirty: boolean}>) => {
if (e.detail.dirty) {
this.getLoraInfo(true);
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { app } from "scripts/app.js";
import type { LGraphNode as TLGraphNode } from "typings/litegraph.js";
import type { LGraphNode as TLGraphNode } from "@litegraph/litegraph.js";
import type {
ComfyApp,
ComfyObjectInfo,
+1 -1
View File
@@ -1,4 +1,4 @@
import type { LGraphNode } from "typings/litegraph.js";
import type { LGraphNode } from "@litegraph/litegraph.js";
import type { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import { app } from "scripts/app.js";
+123 -89
View File
@@ -1,4 +1,17 @@
import { app } from "scripts/app.js";
import type {
Vector2,
LLink,
LGraphCanvas as TLGraphCanvas,
LGraph as TLGraph,
INodeInputSlot,
INodeOutputSlot,
LGraphNode as TLGraphNode,
} from "@litegraph/litegraph.js";
import type {LinkDirection} from "@litegraph/types/globalEnums.js";
import type {ISerialisedNode} from "@litegraph/types/serialisation.js";
import type {Point, Size} from "@litegraph/interfaces.js";
import {app} from "scripts/app.js";
import {
getWidgetConfig,
mergeIfValid,
@@ -6,18 +19,8 @@ import {
// @ts-ignore
} from "../../extensions/core/widgetInputs.js";
// @ts-ignore
import { rgthreeConfig } from "rgthree/config.js";
import { rgthree } from "./rgthree.js";
import type {
Vector2,
LLink,
LGraphCanvas as TLGraphCanvas,
LGraph as TLGraph,
SerializedLGraphNode,
INodeInputSlot,
INodeOutputSlot,
LGraphNode as TLGraphNode,
} from "typings/litegraph.js";
import {rgthreeConfig} from "rgthree/config.js";
import {rgthree} from "./rgthree.js";
import {
IoDirection,
LAYOUT_CLOCKWISE,
@@ -30,10 +33,10 @@ import {
setConnectionsLayout,
waitForCanvas,
} from "./utils.js";
import { SERVICE as KEY_EVENT_SERVICE } from "./services/key_events_services.js";
import { wait } from "rgthree/common/shared_utils.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { NodeTypesString } from "./constants.js";
import {SERVICE as KEY_EVENT_SERVICE} from "./services/key_events_services.js";
import {wait} from "rgthree/common/shared_utils.js";
import {RgthreeBaseVirtualNode} from "./base_node.js";
import {NodeTypesString} from "./constants.js";
const CONFIG_REROUTE = rgthreeConfig?.["nodes"]?.["reroute"] || {};
@@ -79,6 +82,7 @@ type FastRerouteEntryCtx = {
output?: INodeOutputSlot;
slot: number;
pos: Vector2;
direction?: LinkDirection;
};
type FastRerouteEntry = {
@@ -94,7 +98,7 @@ type FastRerouteEntry = {
class RerouteService {
private isFastLinking = false;
private handledNewRerouteKeypress = false;
private connectingData: FastRerouteEntryCtx|null = null;
private connectingData: FastRerouteEntryCtx | null = null;
private fastReroutesHistory: FastRerouteEntry[] = [];
private handleLinkingKeydownBound = this.handleLinkingKeydown.bind(this);
@@ -116,7 +120,7 @@ class RerouteService {
// With the new UI released in August 2024, ComfyUI changed LiteGraph's code, removing
// connecting_node, connecting_node, connecting_node, and connecting_node properties and instead
// using an array of connecting_links. We can try to accomodate both for a while.
const canvasProperty = true ? 'connecting_links' : 'connecting_node';
const canvasProperty = true ? "connecting_links" : "connecting_node";
(canvas as any)[`_${canvasProperty}`];
const thisService = this;
Object.defineProperty(canvas, canvasProperty, {
@@ -162,7 +166,10 @@ class RerouteService {
private stoppingLinking() {
this.isFastLinking = false;
this.fastReroutesHistory = [];
KEY_EVENT_SERVICE.removeEventListener("keydown", this.handleLinkingKeydownBound as EventListener);
KEY_EVENT_SERVICE.removeEventListener(
"keydown",
this.handleLinkingKeydownBound as EventListener,
);
KEY_EVENT_SERVICE.removeEventListener("keyup", this.handleLinkingKeyupBound as EventListener);
}
@@ -197,9 +204,13 @@ class RerouteService {
}
}
private getConnectingData() : FastRerouteEntryCtx {
private getConnectingData(): FastRerouteEntryCtx {
const oldCanvas = app.canvas as any;
if (oldCanvas.connecting_node && oldCanvas.connecting_slot != null && oldCanvas.connecting_pos?.length) {
if (
oldCanvas.connecting_node &&
oldCanvas.connecting_slot != null &&
oldCanvas.connecting_pos?.length
) {
return {
node: oldCanvas.connecting_node,
input: oldCanvas.connecting_input,
@@ -214,10 +225,10 @@ class RerouteService {
const link = canvas.connecting_links[0]!;
return {
node: link.node,
input: link.input,
output: link.output,
input: link.input ?? undefined,
output: link.output ?? undefined,
slot: link.slot,
pos: [...link.pos],
pos: [...link.pos] as Point,
};
}
throw new Error("Error, handling linking keydown, but there's no link.");
@@ -225,7 +236,11 @@ class RerouteService {
private setCanvasConnectingData(ctx: FastRerouteEntryCtx) {
const oldCanvas = app.canvas as any;
if (oldCanvas.connecting_node && oldCanvas.connecting_slot != null && oldCanvas.connecting_pos?.length) {
if (
oldCanvas.connecting_node &&
oldCanvas.connecting_slot != null &&
oldCanvas.connecting_pos?.length
) {
oldCanvas.connecting_node = ctx.node;
oldCanvas.connecting_input = ctx.input;
oldCanvas.connecting_output = ctx.output;
@@ -242,6 +257,19 @@ class RerouteService {
link.slot = ctx.slot;
link.pos = ctx.pos;
}
// const newCanvas = app.canvas as unknown as TypedLGraphCanvas;
// if (newCanvas.linkConnector.renderLinks?.length) {
// newCanvas.linkConnector.reset();
// newCanvas.linkConnector.dragNewFromOutput(app.graph as any, ctx.node as any, ctx.output as any);
// }
// const link = newCanvas.linkConnector.renderLinks[0]! as any;
// link.node = ctx.node;
// link.fromSlot = ctx.output || ctx.input;
// link.fromSlotIndex = ctx.slot;
// link.fromPos = ctx.pos;
// link.fromDirection = ctx.direction || 4;
// }
}
/**
@@ -274,9 +302,9 @@ class RerouteService {
let newPos = canvas.convertEventToCanvasOffset({
clientX: Math.round(canvas.last_mouse_position[0] / 10) * 10,
clientY: Math.round(canvas.last_mouse_position[1] / 10) * 10,
});
} as MouseEvent);
entry.node.pos = newPos;
canvas.graph.add(entry.node);
canvas.graph!.add(entry.node);
canvas.selectNode(entry.node);
// Find out which direction we're generally moving.
@@ -315,12 +343,16 @@ class RerouteService {
data.output = entry.node.outputs[0]!;
data.slot = 0;
data.pos = entry.node.getConnectionPos(false, 0);
data.direction =
layout[0] === "Top" ? 2 : layout[0] === "Bottom" ? 1 : layout[0] === "Left" ? 4 : 3;
} else {
entry.node.connect(0, data.node, data.slot);
data.node = entry.node;
data.input = entry.node.inputs[0]!;
data.slot = 0;
data.pos = entry.node.getConnectionPos(true, 0);
data.direction =
layout[1] === "Top" ? 2 : layout[1] === "Bottom" ? 1 : layout[1] === "Left" ? 4 : 3;
}
this.setCanvasConnectingData(data);
entry.current = {...this.connectingData};
@@ -385,9 +417,9 @@ class RerouteNode extends RgthreeBaseVirtualNode {
/** Shortcuts defined in the config. */
private shortcuts = {
rotate: { keys: CONFIG_KEY_ROTATE, state: false },
connection_input: { keys: CONFIG_KEY_CXN_INPUT, state: false },
connection_output: { keys: CONFIG_KEY_CXN_OUTPUT, state: false },
rotate: {keys: CONFIG_KEY_ROTATE, state: false},
connection_input: {keys: CONFIG_KEY_CXN_INPUT, state: false},
connection_output: {keys: CONFIG_KEY_CXN_OUTPUT, state: false},
resize: {
keys: CONFIG_KEY_RESIZE,
state: false,
@@ -410,7 +442,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
}
override onConstructed(): boolean {
this.setResizable(this.properties["resizable"] ?? configResizable);
this.setResizable(!!(this.properties["resizable"] ?? configResizable));
this.size = RerouteNode.size; // Starting size.
this.addInput("", "*");
this.addOutput("", "*");
@@ -418,7 +450,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
return super.onConstructed();
}
override configure(info: SerializedLGraphNode) {
override configure(info: ISerialisedNode): void {
// Patch a small issue (~14h) where multiple OPT_CONNECTIONS may have been created.
// https://github.com/rgthree/rgthree-comfy/issues/206
// TODO: This can probably be removed within a few weeks.
@@ -430,7 +462,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
}
super.configure(info);
this.configuring = true;
this.setResizable(this.properties["resizable"] ?? configResizable);
this.setResizable(!!(this.properties["resizable"] ?? configResizable));
this.applyNodeSize();
this.configuring = false;
}
@@ -441,7 +473,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
}
override clone() {
const cloned = super.clone();
const cloned = super.clone()!;
cloned.inputs[0]!.type = "*";
cloned.outputs[0]!.type = "*";
return cloned;
@@ -508,13 +540,17 @@ class RerouteNode extends RgthreeBaseVirtualNode {
}
/** Finds the input slot; since we only ever have one, this is always 0. */
override findInputSlot(name: string): number {
return 0;
override findInputSlot<TReturn extends false>(name: string, returnObj?: TReturn): number;
override findInputSlot<TReturn extends true>(name: string, returnObj?: TReturn): INodeInputSlot;
override findInputSlot(name: string, returnObj: boolean = false): number | INodeInputSlot {
return returnObj ? this.inputs[0]! : 0;
}
/** Finds the output slot; since we only ever have one, this is always 0. */
override findOutputSlot(name: string): number {
return 0;
override findOutputSlot<TReturn extends false>(name: string, returnObj?: TReturn): number;
override findOutputSlot<TReturn extends true>(name: string, returnObj?: TReturn): INodeOutputSlot;
override findOutputSlot(name: unknown, returnObj?: unknown): number | INodeOutputSlot {
return returnObj ? this.outputs[0]! : 0;
}
override disconnectOutput(slot: string | number, targetNode?: TLGraphNode | undefined): boolean {
@@ -530,7 +566,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
if (rgthree.replacingReroute != null && this.inputs[0]?.link) {
const graph = app.graph as TLGraph;
const link = graph.links[this.inputs[0].link];
const node = graph.getNodeById(link?.origin_id);
const node = link?.origin_id != null ? graph.getNodeById(link.origin_id) : null;
// We'll also be asked to disconnect when the old one is removed, so we only want to stop a
// disconnect when the connected node is NOT the one being removed/replaced.
if (rgthree.replacingReroute !== node?.id) {
@@ -707,15 +743,16 @@ class RerouteNode extends RgthreeBaseVirtualNode {
for (const l of node.outputs[0]!.links || []) {
const link = app.graph.links[l];
if (link) {
if (link && color) {
link.color = color;
}
}
try {
// For primitive nodes, which look at the widget to dsplay themselves.
// For primitive nodes, which look at the widget to dsplay themselves; we get by with just
// an object with 'name'.
if (outputWidgetConfig && outputWidget && outputType) {
node.inputs[0]!.widget = { name: "value" };
node.inputs[0]!.widget = {name: "value"} as any;
setWidgetConfig(
node.inputs[0],
[outputType ?? displayType, outputWidgetConfig],
@@ -739,7 +776,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
const links = inputNode.outputs[inputNodeOutputSlot]!.links;
for (const l of links || []) {
const link = app.graph.links[l];
if (link) {
if (link && color) {
link.color = color;
}
}
@@ -753,8 +790,8 @@ class RerouteNode extends RgthreeBaseVirtualNode {
* When called, sets the node size, and the properties size, and calls out to `stabilizeLayout`.
*/
override setSize(size: Vector2): void {
const oldSize: Vector2 = [...this.size];
const newSize: Vector2 = [...size];
const oldSize = [...this.size] as Size;
const newSize = [...size] as Size;
super.setSize(newSize);
this.properties["size"] = [...this.size];
this.stabilizeLayout(oldSize, newSize);
@@ -768,9 +805,10 @@ class RerouteNode extends RgthreeBaseVirtualNode {
if (newSize[0] === 10 || newSize[1] === 10) {
const props = this.properties;
props["connections_layout"] = props["connections_layout"] || ["Left", "Right"];
const layout = props["connections_layout"];
props["connections_dir"] = props["connections_dir"] || [-1, -1];
const dir = props["connections_dir"];
const layout = props["connections_layout"] as [string, string];
const dir = props["connections_dir"] as [number, number];
if (oldSize[0] > 10 && newSize[0] === 10) {
dir[0] = LiteGraph.DOWN;
@@ -808,10 +846,10 @@ class RerouteNode extends RgthreeBaseVirtualNode {
applyNodeSize() {
this.properties["size"] = this.properties["size"] || RerouteNode.size;
this.properties["size"] = [
Number(this.properties["size"][0]),
Number(this.properties["size"][1]),
Number((this.properties["size"] as Size)[0]),
Number((this.properties["size"] as Size)[1]),
];
this.size = this.properties["size"];
this.size = this.properties["size"] as Size;
app.graph.setDirtyCanvas(true, true);
}
@@ -823,25 +861,21 @@ class RerouteNode extends RgthreeBaseVirtualNode {
const h = this.size[1];
this.properties["connections_layout"] =
this.properties["connections_layout"] || (this as RerouteNode).defaultConnectionsLayout;
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(this.properties["connections_layout"][0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(this.properties["connections_layout"][1]);
const connections_layout = this.properties["connections_layout"] as [string, string];
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[1]);
if (degrees == 90 || degrees === -90) {
if (degrees === -90) {
this.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex - 1) % 4) + 4) % 4];
this.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex - 1) % 4) + 4) % 4];
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex - 1) % 4) + 4) % 4]!;
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex - 1) % 4) + 4) % 4]!;
} else {
this.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 1) % 4) + 4) % 4];
this.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 1) % 4) + 4) % 4];
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 1) % 4) + 4) % 4]!;
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 1) % 4) + 4) % 4]!;
}
} else if (degrees === 180) {
this.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
this.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4]!;
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4]!;
}
this.setSize([h, w]);
}
@@ -871,7 +905,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
let diffY = Math.round((event.clientY - shortcut.initialMousePos[1]) / 10) * 10;
diffX *= shortcut.resizeOnSide[0] === LiteGraph.LEFT ? -1 : 1;
diffY *= shortcut.resizeOnSide[1] === LiteGraph.UP ? -1 : 1;
const oldSize: Vector2 = [...this.size];
const oldSize = [...this.size] as Size;
this.setSize([
Math.max(10, shortcut.initialNodeSize[0] + diffX),
Math.max(10, shortcut.initialNodeSize[1] + diffY),
@@ -896,26 +930,29 @@ class RerouteNode extends RgthreeBaseVirtualNode {
private cycleConnection(ioDir: IoDirection) {
const props = this.properties;
props["connections_layout"] = props["connections_layout"] || ["Left", "Right"];
const connections_layout = this.properties["connections_layout"] as [string, string];
const propIdx = ioDir == IoDirection.INPUT ? 0 : 1;
const oppositeIdx = propIdx ? 0 : 1;
let currentLayout = props["connections_layout"][propIdx];
let oppositeLayout = props["connections_layout"][oppositeIdx];
let currentLayout = connections_layout[propIdx];
let oppositeLayout = connections_layout[oppositeIdx];
if (this.size[0] === 10 || this.size[1] === 10) {
props["connections_dir"] = props["connections_dir"] || [-1, -1];
let currentDir = props["connections_dir"][propIdx] as number;
// let oppositeDir = props["connections_dir"][oppositeIdx];
const connections_dir = this.properties["connections_dir"] as [number, number];
let currentDir = connections_dir[propIdx] as number;
// let oppositeDir = connections_dir[oppositeIdx];
const options: number[] =
this.size[0] === 10
? currentLayout === "Bottom"
? [LiteGraph.DOWN, LiteGraph.RIGHT, LiteGraph.LEFT]
: [LiteGraph.UP, LiteGraph.LEFT, LiteGraph.RIGHT]
: currentLayout === "Right"
? [LiteGraph.RIGHT, LiteGraph.DOWN, LiteGraph.UP]
: [LiteGraph.LEFT, LiteGraph.UP, LiteGraph.DOWN];
? [LiteGraph.RIGHT, LiteGraph.DOWN, LiteGraph.UP]
: [LiteGraph.LEFT, LiteGraph.UP, LiteGraph.DOWN];
let idx = options.indexOf(currentDir);
let next = options[idx + 1] ?? options[0]!;
this.properties["connections_dir"][propIdx] = next;
connections_dir[propIdx] = next;
return;
}
@@ -924,7 +961,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
let idx = LAYOUT_CLOCKWISE.indexOf(next);
next = LAYOUT_CLOCKWISE[idx + 1] ?? LAYOUT_CLOCKWISE[0]!;
} while (next === oppositeLayout);
this.properties["connections_layout"][propIdx] = next;
connections_layout[propIdx] = next;
this.setDirtyCanvas(true, true);
}
@@ -1189,25 +1226,22 @@ addMenuItem(RerouteNode, app, {
} else if (value?.startsWith("Rotate 180°")) {
node.rotate(180);
} else {
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(node.properties["connections_layout"][0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(node.properties["connections_layout"][1]);
const connections_layout = node.properties["connections_layout"] as [string, string];
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[1]);
if (value?.startsWith("Flip Horizontally")) {
if (["Left", "Right"].includes(node.properties["connections_layout"][0])) {
node.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
if (["Left", "Right"].includes(connections_layout[0])) {
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4]!;
}
if (["Left", "Right"].includes(node.properties["connections_layout"][1])) {
node.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
if (["Left", "Right"].includes(connections_layout[1])) {
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4]!;
}
} else if (value?.startsWith("Flip Vertically")) {
if (["Top", "Bottom"].includes(node.properties["connections_layout"][0])) {
node.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
if (["Top", "Bottom"].includes(connections_layout[0])) {
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4]!;
}
if (["Top", "Bottom"].includes(node.properties["connections_layout"][1])) {
node.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
if (["Top", "Bottom"].includes(connections_layout[1])) {
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4]!;
}
}
}
@@ -1218,7 +1252,7 @@ addMenuItem(RerouteNode, app, {
name: "Clone New Reroute...",
subMenuOptions: ["Before", "After"],
callback: async (node, value) => {
const clone = node.clone();
const clone = node.clone()!;
const pos = [...node.pos];
if (value === "Before") {
clone.pos = [pos[0]! - 20, pos[1]! - 20];
+55 -46
View File
@@ -1,14 +1,15 @@
import type {
LGraphCanvas as TLGraphCanvas,
LGraphNode,
SerializedLGraphNode,
serializedLGraph,
ContextMenuItem,
IContextMenuValue,
LGraph as TLGraph,
AdjustedMouseEvent,
IContextMenuOptions,
} from "typings/litegraph.js";
} from "@litegraph/litegraph.js";
import type {CanvasMouseEvent, CanvasPointerExtensions} from "@litegraph/types/events.js";
import type {NodeId} from "@litegraph/LGraphNode.js";
import type {ComfyApiFormat, ComfyApiPrompt, ComfyApp} from "typings/comfy.js";
import type {Bookmark} from "./bookmark.js";
import {app} from "scripts/app.js";
import {api} from "scripts/api.js";
import {SERVICE as CONFIG_SERVICE} from "./services/config_service.js";
@@ -26,7 +27,6 @@ import {
iconStarFilled,
logoRgthree,
} from "rgthree/common/media/svgs.js";
import type {Bookmark} from "./bookmark.js";
import {createElement, query, queryOne} from "rgthree/common/utils_dom.js";
export enum LogLevel {
@@ -223,7 +223,7 @@ class Rgthree extends EventTarget {
private rgthreeCssPromise: Promise<void>;
/** Stores a node id that we will use to queu only that output node (with `queueOutputNode`). */
private queueNodeIds: number[] | null = null;
private queueNodeIds: NodeId[] | null = null;
logger = new LogSession("[rgthree]");
@@ -232,11 +232,11 @@ class Rgthree extends EventTarget {
processingQueue = false;
loadingApiJson = false;
replacingReroute: number | null = null;
replacingReroute: NodeId | null = null;
processingMouseDown = false;
processingMouseUp = false;
processingMouseMove = false;
lastAdjustedMouseEvent: AdjustedMouseEvent | null = null;
lastCanvasMouseEvent: CanvasMouseEvent | null = null;
// Comfy/LiteGraph states so nodes and tell what the hell is going on.
canvasCurrentlyCopyingToClipboard = false;
@@ -275,7 +275,7 @@ class Rgthree extends EventTarget {
});
const updateDebugKeyDown = () => {
elDebugKeydowns.innerText = Object.keys(KEY_EVENT_SERVICE.downKeys).join(" ");
}
};
KEY_EVENT_SERVICE.addEventListener("keydown", updateDebugKeyDown);
KEY_EVENT_SERVICE.addEventListener("keyup", updateDebugKeyDown);
}
@@ -303,15 +303,11 @@ class Rgthree extends EventTarget {
LiteGraph.closeAllContextMenus();
if (e.button == 2) {
const canvas = await waitForCanvas();
new LiteGraph.ContextMenu(
this.getRgthreeContextMenuItems(),
{
title: `<div class="rgthree-contextmenu-item rgthree-contextmenu-title-rgthree-comfy">${logoRgthree} rgthree-comfy</div>`,
left: e.clientX,
top: 5,
},
canvas.getCanvasWindow(),
);
new LiteGraph.ContextMenu(this.getRgthreeIContextMenuValues(), {
title: `<div class="rgthree-contextmenu-item rgthree-contextmenu-title-rgthree-comfy">${logoRgthree} rgthree-comfy</div>`,
left: e.clientX,
top: 5,
});
return;
}
if (e.button == 0) {
@@ -376,7 +372,7 @@ class Rgthree extends EventTarget {
// Overrides LiteGraphs' processMouseDown to both keep state as well as dispatch a custom event.
const processMouseDown = LGraphCanvas.prototype.processMouseDown;
LGraphCanvas.prototype.processMouseDown = function (e: AdjustedMouseEvent) {
LGraphCanvas.prototype.processMouseDown = function (e: PointerEvent) {
rgthree.processingMouseDown = true;
const returnVal = processMouseDown.apply(this, [...arguments] as any);
rgthree.dispatchCustomEvent("on-process-mouse-down", {originalEvent: e});
@@ -388,9 +384,11 @@ class Rgthree extends EventTarget {
// to capture the last `canvasX` and `canvasY` properties, which are not the same as LiteGraph's
// `canvas.last_mouse_position`, unfortunately.
const adjustMouseEvent = LGraphCanvas.prototype.adjustMouseEvent;
LGraphCanvas.prototype.adjustMouseEvent = function (e: PointerEvent) {
LGraphCanvas.prototype.adjustMouseEvent = function <T extends MouseEvent>(
e: T & Partial<CanvasPointerExtensions>,
): asserts e is T & CanvasMouseEvent {
adjustMouseEvent.apply(this, [...arguments] as any);
rgthree.lastAdjustedMouseEvent = e as AdjustedMouseEvent;
rgthree.lastCanvasMouseEvent = e as CanvasMouseEvent;
};
// [🤮] Copying to clipboard clones nodes and then manipulats the linking data manually which
@@ -412,12 +410,15 @@ class Rgthree extends EventTarget {
LGraphCanvas.onGroupAdd = function (...args: any[]) {
const graph = app.graph as TLGraph;
onGroupAdd.apply(this, [...args] as any);
// [🤮] Bad typing here.. especially the last arg; it is LGraphNode but can really be anything
// with pos or size... pity.
LGraphCanvas.onShowPropertyEditor(
{},
null,
null,
null,
graph._groups[graph._groups.length - 1],
{} as any,
null as any,
null as any,
null as any,
//
graph._groups[graph._groups.length - 1]! as unknown as LGraphNode,
);
};
}
@@ -491,7 +492,7 @@ class Rgthree extends EventTarget {
LGraphCanvas.prototype.getCanvasMenuOptions = function (...args: any[]) {
let existingOptions = getCanvasMenuOptions.apply(this, [...args] as any);
const options = [];
const options: (IContextMenuValue | null)[] = [];
options.push(null); // Divider
options.push(null); // Divider
options.push(null); // Divider
@@ -499,7 +500,7 @@ class Rgthree extends EventTarget {
content: logoRgthree + `rgthree-comfy`,
className: "rgthree-contextmenu-item rgthree-contextmenu-main-item-rgthree-comfy",
submenu: {
options: that.getRgthreeContextMenuItems(),
options: that.getRgthreeIContextMenuValues(),
},
});
options.push(null); // Divider
@@ -513,7 +514,9 @@ class Rgthree extends EventTarget {
idx = idx || existingOptions.findIndex((o) => o?.content?.startsWith?.("Arrange ("));
idx = idx || existingOptions.findIndex((o) => !o) + 1;
idx = idx || 3;
existingOptions.splice(idx, 0, ...options);
// [🤮] existingOptions is typed as IContextMenuValue<string> even though it need not be
// a string due to the crazy typing from the original litegraph. oh well.
(existingOptions as (IContextMenuValue | null)[]).splice(idx, 0, ...options);
for (let i = existingOptions.length; i > 0; i--) {
if (existingOptions[i] === null && existingOptions[i + 1] === null) {
existingOptions.splice(i, 1);
@@ -528,7 +531,7 @@ class Rgthree extends EventTarget {
/**
* Returns the standard menu items for an rgthree-comfy context menu.
*/
private getRgthreeContextMenuItems(): ContextMenuItem[] {
private getRgthreeIContextMenuValues(): IContextMenuValue[] {
const [canvas, graph] = [app.canvas as TLGraphCanvas, app.graph as TLGraph];
const selectedNodes = Object.values(canvas.selected_nodes || {});
let rerouteNodes: LGraphNode[] = [];
@@ -553,20 +556,22 @@ class Rgthree extends EventTarget {
className: "rgthree-contextmenu-item",
has_submenu: true,
submenu: {
options: getNodeTypeStrings() as unknown as ContextMenuItem[],
options: getNodeTypeStrings() as unknown as IContextMenuValue[],
callback: (
value: string | ContextMenuItem,
value: string | IContextMenuValue,
options: IContextMenuOptions,
event: MouseEvent,
) => {
const node = LiteGraph.createNode(addRgthree(value as string));
node.pos = [
rgthree.lastAdjustedMouseEvent!.canvasX,
rgthree.lastAdjustedMouseEvent!.canvasY,
];
canvas.graph.add(node);
canvas.selectNode(node);
app.graph.setDirtyCanvas(true, true);
if (node) {
node.pos = [
rgthree.lastCanvasMouseEvent!.canvasX,
rgthree.lastCanvasMouseEvent!.canvasY,
];
canvas.graph!.add(node);
canvas.selectNode(node);
app.graph.setDirtyCanvas(true, true);
}
},
extra: {rgthree_doNotNest: true},
},
@@ -630,7 +635,7 @@ class Rgthree extends EventTarget {
* Wraps an `app.queuePrompt` call setting a specific node id that we will inspect and change the
* serialized graph right before being sent (below, in our `api.queuePrompt` override).
*/
async queueOutputNodes(nodeIds: number[]) {
async queueOutputNodes(nodeIds: NodeId[]) {
try {
this.queueNodeIds = nodeIds;
await app.queuePrompt();
@@ -739,14 +744,16 @@ class Rgthree extends EventTarget {
// Hook into a data load, like from an image or JSON drop-in. This is (currently) used to
// monitor for bad linking data.
const loadGraphData = app.loadGraphData;
app.loadGraphData = function (graph: serializedLGraph) {
// TODO: Pull in real ComfyUI Types.
app.loadGraphData = function (graph: any) {
if (rgthree.monitorLinkTimeout) {
clearTimeout(rgthree.monitorLinkTimeout);
rgthree.monitorLinkTimeout = null;
}
rgthree.clearAllMessages();
// Try to make a copy to use, because ComfyUI's loadGraphData will modify it.
let graphCopy: serializedLGraph | null;
// TODO: Pull in real ComfyUI Types.
let graphCopy: any | null;
try {
graphCopy = JSON.parse(JSON.stringify(graph));
} catch (e) {
@@ -839,10 +846,12 @@ class Rgthree extends EventTarget {
*/
getNodeFromInitialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff(
node: LGraphNode,
): SerializedLGraphNode | null {
// TODO: Pull in real ComfyUI Types.
): any | null {
return (
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff?.nodes?.find(
(n: SerializedLGraphNode) => n.id === node.id,
// TODO: Pull in real ComfyUI Types.
(n: any) => n.id === node.id,
) ?? null
);
}
@@ -986,7 +995,7 @@ class Rgthree extends EventTarget {
}
}
function getBookmarks(): ContextMenuItem[] {
function getBookmarks(): IContextMenuValue[] {
const graph: TLGraph = app.graph;
// Sorts by Title.
+21 -20
View File
@@ -1,25 +1,25 @@
import { app } from "scripts/app.js";
import { ComfyWidgets } from "scripts/widgets.js";
import type {
ContextMenuItem,
IContextMenuOptions,
ContextMenu,
LGraphNode as TLGraphNode,
IWidget,
LGraphCanvas,
SerializedLGraphNode,
} from "typings/litegraph.js";
IContextMenuValue,
} from "@litegraph/litegraph.js";
import type {
ComfyObjectInfo,
ComfyWidget,
ComfyNodeConstructor,
ComfyApiPrompt,
} from "typings/comfy.js";
import type { SerializedNode } from "typings/index.js";
import { app } from "scripts/app.js";
import { ComfyWidgets } from "scripts/widgets.js";
import { RgthreeBaseServerNode } from "./base_node.js";
import { rgthree } from "./rgthree.js";
import { addConnectionLayoutSupport } from "./utils.js";
import { NodeTypesString } from "./constants.js";
import { SerializedNode } from "typings/index.js";
import { ISerialisedNode } from "@litegraph/types/serialisation.js";
const LAST_SEED_BUTTON_LABEL = "♻️ (Use Last Queued Seed)";
@@ -48,7 +48,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
serializedCtx: SeedSerializedCtx = {};
seedWidget!: IWidget;
lastSeedButton!: IWidget;
lastSeedValue: ComfyWidget | null = null;
lastSeedValue: IWidget | null = null;
randMax = 1125899906842624;
// We can have a full range of seeds, including negative. But, for the randomRange we'll
@@ -75,7 +75,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
);
}
override configure(info: SerializedLGraphNode<TLGraphNode>): void {
override configure(info: ISerialisedNode): void {
super.configure(info);
if (this.properties?.["showLastSeed"]) {
this.addLastSeedValue();
@@ -106,26 +106,26 @@ class RgthreeSeed extends RgthreeBaseServerNode {
// Update random values in case seed comes down with different options.
let step = this.seedWidget.options.step || 1;
this.randMax = Math.min(1125899906842624, this.seedWidget.options.max);
this.randMax = Math.min(1125899906842624, this.seedWidget.options.max ?? 0);
// We can have a full range of seeds, including negative. But, for the randomRange we'll
// only generate positives, since that's what folks assume.
this.randMin = Math.max(0, this.seedWidget.options.min);
this.randMin = Math.max(0, this.seedWidget.options.min ?? 0);
this.randomRange = (this.randMax - Math.max(0, this.randMin)) / (step / 10);
this.addWidget(
"button",
"🎲 Randomize Each Time",
null,
'',
() => {
this.seedWidget.value = SPECIAL_SEED_RANDOM;
},
{ serialize: false },
) as ComfyWidget;
);
this.addWidget(
"button",
"🎲 New Fixed Random",
null,
'',
() => {
this.seedWidget.value =
Math.floor(Math.random() * this.randomRange) * (step / 10) + this.randMin;
@@ -136,7 +136,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
this.lastSeedButton = this.addWidget(
"button",
LAST_SEED_BUTTON_LABEL,
null,
'',
() => {
this.seedWidget.value = this.lastSeed != null ? this.lastSeed : this.seedWidget.value;
this.lastSeedButton.name = LAST_SEED_BUTTON_LABEL;
@@ -147,12 +147,12 @@ class RgthreeSeed extends RgthreeBaseServerNode {
this.lastSeedButton.disabled = true;
}
override getExtraMenuOptions(canvas: LGraphCanvas, options: ContextMenuItem[]): void {
override getExtraMenuOptions(canvas: LGraphCanvas, options: IContextMenuValue[]) {
super.getExtraMenuOptions?.apply(this, [...arguments] as any);
options.splice(options.length - 1, 0, {
content: "Show/Hide Last Seed Value",
callback: (
_value: ContextMenuItem,
_value: IContextMenuValue,
_options: IContextMenuOptions,
_event: MouseEvent,
_parentMenu: ContextMenu | undefined,
@@ -166,6 +166,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
}
},
});
return options;
}
addLastSeedValue() {
@@ -175,7 +176,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
"last_seed",
["STRING", { multiline: true }],
app,
).widget;
).widget as unknown as IWidget;
this.lastSeedValue!.inputEl!.readOnly = true;
this.lastSeedValue!.inputEl!.style.fontSize = "0.75rem";
this.lastSeedValue!.inputEl!.style.textAlign = "center";
@@ -185,7 +186,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
removeLastSeedValue() {
if (!this.lastSeedValue) return;
this.lastSeedValue!.inputEl!.remove();
this.widgets.splice(this.widgets.indexOf(this.lastSeedValue as IWidget), 1);
this.widgets.splice(this.widgets.indexOf(this.lastSeedValue), 1);
this.lastSeedValue = null;
this.computeSize();
}
@@ -248,7 +249,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
* There are no sideffects to calling this method.
*/
private getSeedToUse() {
const inputSeed: number = this.seedWidget.value;
const inputSeed = Number(this.seedWidget.value);
let seedToUse: number | null = null;
// If our input seed was a special seed, then handle it.
+1 -1
View File
@@ -3,7 +3,7 @@ import type {DynamicContextNodeBase} from "../dynamic_context_base.js";
import {app} from "scripts/app.js";
import {NodeTypesString} from "../constants.js";
import {getConnectedOutputNodesAndFilterPassThroughs} from "../utils.js";
import {INodeInputSlot, INodeOutputSlot, INodeSlot, LGraphNode} from "typings/litegraph.js";
import {INodeInputSlot, INodeOutputSlot, INodeSlot, LGraphNode} from "@litegraph/litegraph.js";
export let SERVICE: ContextService;
@@ -1,11 +1,12 @@
import { app } from "scripts/app.js";
import type { BaseFastGroupsModeChanger } from "../fast_groups_muter.js";
import {
type LGraph as TLGraph,
type LGraphCanvas as TLGraphCanvas,
import type {
LGraph as TLGraph,
LGraphCanvas as TLGraphCanvas,
LGraphGroup,
Vector4,
} from "typings/litegraph.js";
} from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
/**
* A service that keeps global state that can be shared by multiple FastGroupsMuter or
@@ -96,7 +97,7 @@ class FastGroupsService {
if (!this.cachedNodeBoundings) {
this.cachedNodeBoundings = {};
for (const node of app.graph._nodes) {
this.cachedNodeBoundings[node.id] = node.getBounding();
this.cachedNodeBoundings[Number(node.id)] = node.getBounding() as Vector4;
}
setTimeout(() => {
this.cachedNodeBoundings = null;
@@ -111,11 +112,11 @@ class FastGroupsService {
*/
recomputeInsideNodesForGroup(group: LGraphGroup) {
const cachedBoundings = this.getBoundingsForAllNodes();
const nodes = group.graph._nodes;
const nodes = group.graph!._nodes;
group._nodes.length = 0;
for (const node of nodes) {
const node_bounding = cachedBoundings[node.id];
const node_bounding = cachedBoundings[Number(node.id)];
if (!node_bounding || !LiteGraph.overlapBounding(group._bounding, node_bounding)) {
continue;
}
@@ -134,6 +135,7 @@ class FastGroupsService {
if (
// Don't recalculate nodes if we're moving a group (added by ComfyUI in app.js)
// TODO: This doesn't look available anymore... ?
!canvas.selected_group_moving &&
(!this.groupsUnsorted.length || now - this.msLastUnsorted > this.msThreshold)
) {
+4 -3
View File
@@ -1,7 +1,7 @@
import { app } from "scripts/app.js";
import { NodeTypesString } from "../constants.js";
import { wait } from "rgthree/common/shared_utils.js";
import type { LGraphNode } from "typings/litegraph.js";
import type { LGraphNode } from "@litegraph/litegraph.js";
type addNodeOptions = {
placement?: string;
@@ -19,7 +19,7 @@ export class ComfyUITestEnvironment {
async addNode(nodeString: string, options: addNodeOptions = {}) {
const [canvas, graph] = [app.canvas, app.graph];
const node = LiteGraph.createNode(nodeString);
const node = LiteGraph.createNode(nodeString)!;
let x = 0;
let y = 30;
if (this.lastNode) {
@@ -35,7 +35,7 @@ export class ComfyUITestEnvironment {
y = this.maxY + 50;
}
}
canvas.graph.add(node);
canvas.graph!.add(node);
node.pos = [x, y];
canvas.selectNode(node);
app.graph.setDirtyCanvas(true, true);
@@ -50,6 +50,7 @@ export class ComfyUITestEnvironment {
app.graph.clear();
const nodeConfig = await this.addNode(NodeTypesString.KSAMPLER_CONFIG);
const displayAny = await this.addNode(NodeTypesString.DISPLAY_ANY);
nodeConfig.widgets = nodeConfig.widgets || [];
nodeConfig.widgets[0]!.value = Math.round(Math.random() * 100);
nodeConfig.connect(0, displayAny, 0);
await this.queuePrompt();
@@ -1,12 +1,9 @@
import type {
LiteGraph as TLiteGraph,
LGraphCanvas as TLGraphCanvas,
LGraph as TLGraph,
LGraphNode as TLGraphNode,
Vector2,
LGraphNode,
} from "typings/litegraph.js";
import {rgthree} from "../rgthree.js";
} from "@litegraph/litegraph.js";
import {NodeTypesString} from "../constants.js";
import {wait} from "rgthree/common/shared_utils.js";
import {describe, should, beforeEach, expect, describeRun} from "../testing/runner.js";
@@ -107,6 +104,7 @@ function vertifyInputsStructure(node: LGraphNode, expectedLength: number) {
await beforeEach(async () => {
nodeConfig2 = await env.addNode(NodeTypesString.KSAMPLER_CONFIG, {placement: "start"});
nodeConfig2.widgets = nodeConfig2.widgets || [];
nodeConfig2.widgets[0]!.value = 111;
nodeConfig2.widgets[2]!.value = 11.1;
nodeCtx2 = await env.addNode(NodeTypesString.DYNAMIC_CONTEXT, {placement: "right"});
+23 -17
View File
@@ -2,7 +2,6 @@ import type { ComfyApp, ComfyNodeConstructor, ComfyObjectInfo } from "typings/co
import type {
Vector2,
LGraphCanvas,
ContextMenuItem,
LLink,
LGraph,
IContextMenuOptions,
@@ -11,12 +10,18 @@ import type {
INodeSlot,
INodeInputSlot,
INodeOutputSlot,
} from "typings/litegraph.js";
IContextMenuValue,
ISlotType,
} from "@litegraph/litegraph.js";
import type { Constructor } from "typings/index.js";
import { app } from "scripts/app.js";
import { api } from "scripts/api.js";
import { Resolver, getResolver, wait } from "rgthree/common/shared_utils.js";
import { RgthreeHelpDialog } from "rgthree/common/dialog.js";
import { NodeProperty } from "@litegraph/LGraphNode";
import { LinkDirection } from "@litegraph/types/globalEnums";
import { Point } from "@litegraph/interfaces";
/**
* Override the api.getNodeDefs call to add a hook for refreshing node defs.
@@ -59,7 +64,7 @@ export const LAYOUT_CLOCKWISE = ["Top", "Right", "Bottom", "Left"];
interface MenuConfig {
name: string | ((node: LGraphNode) => string);
property?: string;
prepareValue?: (value: string, node: LGraphNode) => any;
prepareValue?: (value: NodeProperty | undefined, node: LGraphNode) => any;
callback?: (node: LGraphNode, value?: string) => void;
subMenuOptions?: (string | null)[] | ((node: LGraphNode) => (string | null)[]);
}
@@ -73,7 +78,7 @@ export function addMenuItem(
const oldGetExtraMenuOptions = node.prototype.getExtraMenuOptions;
node.prototype.getExtraMenuOptions = function (
canvas: LGraphCanvas,
menuOptions: ContextMenuItem[],
menuOptions: IContextMenuValue[],
) {
oldGetExtraMenuOptions && oldGetExtraMenuOptions.apply(this, [canvas, menuOptions]);
addMenuItemOnExtraMenuOptions(this, config, menuOptions, after);
@@ -125,7 +130,7 @@ export function waitForGraph() {
export function addMenuItemOnExtraMenuOptions(
node: LGraphNode,
config: MenuConfig,
menuOptions: ContextMenuItem[],
menuOptions: (IContextMenuValue | null)[],
after = "Shape",
) {
let idx = menuOptions
@@ -154,7 +159,7 @@ export function addMenuItemOnExtraMenuOptions(
has_submenu: !!subMenuOptions?.length,
isRgthree: true, // Mark it, so we can find it.
callback: (
value: ContextMenuItem,
value: IContextMenuValue,
_options: IContextMenuOptions,
event: MouseEvent,
parentMenu: ContextMenu | undefined,
@@ -167,7 +172,7 @@ export function addMenuItemOnExtraMenuOptions(
event,
parentMenu,
callback: (
subValue: ContextMenuItem,
subValue: IContextMenuValue,
_options: IContextMenuOptions,
_event: MouseEvent,
_parentMenu: ContextMenu | undefined,
@@ -193,7 +198,7 @@ export function addMenuItemOnExtraMenuOptions(
}
config.callback && config.callback(node, value?.content);
},
} as ContextMenuItem);
} as IContextMenuValue);
}
export function addConnectionLayoutSupport(
@@ -210,7 +215,7 @@ export function addConnectionLayoutSupport(
property: "connections_layout",
subMenuOptions: options.map((option) => option[0] + (option[1] ? " -> " + option[1] : "")),
prepareValue: (value, node) => {
const values = value.split(" -> ");
const values = String(value).split(" -> ");
if (!values[1] && !node.outputs?.length) {
values[1] = LAYOUT_LABEL_OPPOSITES[values[0]!]!;
}
@@ -276,7 +281,7 @@ export function getConnectionPosForLayout(
node.properties = node.properties || {};
const layout = node.properties["connections_layout"] ||
(node as any).defaultConnectionsLayout || ["Left", "Right"];
const collapseConnections = node.properties["collapse_connections"] || false;
const collapseConnections = (node.properties["collapse_connections"] as boolean) || false;
const offset = (node.constructor as any).layout_slot_offset ?? LiteGraph.NODE_SLOT_HEIGHT * 0.5;
let side = isInput ? layout[0] : layout[1];
const otherSide = isInput ? layout[1] : layout[0];
@@ -317,8 +322,9 @@ export function getConnectionPosForLayout(
cxn.dir = data[0];
// If we are only 10px tall or wide, then look at connections_dir for the direction.
if ((node.size[0] == 10 || node.size[1] == 10) && node.properties["connections_dir"]) {
cxn.dir = node.properties["connections_dir"][isInput ? 0 : 1]!;
const connections_dir = node.properties["connections_dir"] as [LinkDirection, LinkDirection];
if ((node.size[0] == 10 || node.size[1] == 10) && connections_dir) {
cxn.dir = connections_dir[isInput ? 0 : 1]!;
}
if (side === "Left") {
@@ -397,7 +403,7 @@ function toggleConnectionLabel(cxn: any, hide = true) {
return cxn;
}
export function addHelpMenuItem(node: LGraphNode, content: string, menuOptions: ContextMenuItem[]) {
export function addHelpMenuItem(node: LGraphNode, content: string, menuOptions: (IContextMenuValue | null)[]) {
addMenuItemOnExtraMenuOptions(
node,
{
@@ -675,12 +681,12 @@ export async function replaceNode(
const existingCtor = existingNode.constructor as typeof LGraphNode;
const newNode =
typeof typeOrNewNode === "string" ? LiteGraph.createNode(typeOrNewNode) : typeOrNewNode;
typeof typeOrNewNode === "string" ? LiteGraph.createNode(typeOrNewNode)! : typeOrNewNode;
// Port title (maybe) the position, size, and properties from the old node.
if (existingNode.title != existingCtor.title) {
newNode.title = existingNode.title;
}
newNode.pos = [...existingNode.pos];
newNode.pos = [...existingNode.pos] as Point;
newNode.properties = { ...existingNode.properties };
const oldComputeSize = [...existingNode.computeSize()];
// oldSize to use. If we match the smallest size (computeSize) then don't record and we'll use
@@ -875,7 +881,7 @@ export async function matchLocalSlotsToServer(
// doesn't exist).
if (
nextNode &&
(nextNode.constructor as ComfyNodeConstructor)?.type!.includes("Reroute")
(nextNode.constructor as any)?.type!.includes("Reroute")
) {
(nextNode as any).stabilize && (nextNode as any).stabilize();
}
@@ -916,7 +922,7 @@ export function isValidConnection(ioA?: INodeSlot | null, ioB?: INodeSlot | null
* lists (without users needing to go through and re-create all their nodes one by one).
*/
const oldIsValidConnection = LiteGraph.isValidConnection;
LiteGraph.isValidConnection = function (typeA: string | string[], typeB: string | string[]) {
LiteGraph.isValidConnection = function (typeA: ISlotType, typeB: ISlotType): boolean {
let isValid = oldIsValidConnection.call(LiteGraph, typeA, typeB);
if (!isValid) {
typeA = String(typeA);
+2 -1
View File
@@ -1,5 +1,6 @@
import type { LGraphCanvas as TLGraphCanvas, Vector2 } from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import type { LGraphCanvas as TLGraphCanvas, Vector2 } from "../typings/litegraph.js";
function binarySearch(max: number, getValue: (n: number) => number, match: number) {
let min = 0;
+1 -1
View File
@@ -1,4 +1,4 @@
import type { LGraphNode } from "typings/litegraph.js";
import type { LGraphNode } from "@litegraph/litegraph.js";
/** Removes all inputs from the end. */
export function removeUnusedInputsFromEnd(node: LGraphNode, minNumber = 1, nameMatch?: RegExp) {
+7 -7
View File
@@ -1,6 +1,3 @@
import { app } from "scripts/app.js";
import { rgthreeApi } from "rgthree/common/rgthree_api.js";
import type {
LGraphCanvas as TLGraphCanvas,
IWidget,
@@ -8,7 +5,10 @@ import type {
ContextMenuEventListener,
ContextMenu,
IContextMenuItem,
} from "../typings/litegraph.js";
} from "@litegraph/litegraph.js";
import { app } from "scripts/app.js";
import { rgthreeApi } from "rgthree/common/rgthree_api.js";
const PASS_THROUGH = function <T extends any, I extends any>(item: T) {
return item as T;
@@ -18,7 +18,7 @@ const PASS_THROUGH = function <T extends any, I extends any>(item: T) {
* Shows a lora chooser context menu.
*/
export async function showLoraChooser(
event: PointerEvent,
event: PointerEvent | MouseEvent,
callback: ContextMenuEventListener,
parentMenu?: ContextMenu | null,
loras?: string[],
@@ -29,7 +29,7 @@ export async function showLoraChooser(
}
new LiteGraph.ContextMenu(loras, {
event: event,
parentMenu,
parentMenu: parentMenu != null ? parentMenu : undefined,
title: "Choose a lora",
scale: Math.max(1, canvas.ds?.scale ?? 1),
className: "dark",
@@ -44,7 +44,7 @@ export async function showLoraChooser(
* it will be filtered out (rather than use a separate filter method).
*/
export function showNodesChooser<T extends IContextMenuItem>(
event: PointerEvent,
event: PointerEvent | MouseEvent,
mapFn: (n: LGraphNode) => T | null,
callback: ContextMenuEventListener,
parentMenu?: ContextMenu,
+85 -60
View File
@@ -1,13 +1,14 @@
import { app } from "scripts/app.js";
import type {
IWidget,
LGraphNode,
LGraphCanvas as TLGraphCanvas,
Vector2,
AdjustedMouseEvent,
Vector4,
} from "../typings/litegraph.js";
import { drawNodeWidget, drawRoundedRectangle, fitString, isLowQuality } from "./utils_canvas.js";
} from "@litegraph/litegraph.js";
import type {CanvasMouseEvent} from "@litegraph/types/events.js";
import type {IBaseWidget, IWidgetOptions} from "@litegraph/types/widgets.js";
import {app} from "scripts/app.js";
import {drawNodeWidget, drawRoundedRectangle, fitString, isLowQuality} from "./utils_canvas.js";
/**
* Draws a label on teft, and a value on the right, ellipsizing when out of space.
@@ -19,7 +20,7 @@ export function drawLabelAndValue(
width: number,
posY: number,
height: number,
options?: { offsetLeft: number },
options?: {offsetLeft: number},
) {
const outerMargin = 15;
const innerMargin = 10;
@@ -43,23 +44,23 @@ export function drawLabelAndValue(
export type RgthreeBaseWidgetBounds = {
/** The bounds, either [x, width] assuming the full height, or [x, y, width, height] if height. */
bounds: Vector2 | Vector4;
onDown?(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode): boolean | void;
onDown?(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean | void;
onDown?(
event: AdjustedMouseEvent,
event: CanvasMouseEvent,
pos: Vector2,
node: LGraphNode,
bounds: RgthreeBaseWidgetBounds,
): boolean | void;
onUp?(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode): boolean | void;
onUp?(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean | void;
onUp?(
event: AdjustedMouseEvent,
event: CanvasMouseEvent,
pos: Vector2,
node: LGraphNode,
bounds: RgthreeBaseWidgetBounds,
): boolean | void;
onMove?(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode): boolean | void;
onMove?(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean | void;
onMove?(
event: AdjustedMouseEvent,
event: CanvasMouseEvent,
pos: Vector2,
node: LGraphNode,
bounds: RgthreeBaseWidgetBounds,
@@ -71,16 +72,21 @@ export type RgthreeBaseHitAreas<Keys extends string> = {
[K in Keys]: RgthreeBaseWidgetBounds;
};
type NotArray<T> = T extends Array<any> ? never : T;
/**
* A base widget that handles mouse events more properly.
*/
export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
export abstract class RgthreeBaseWidget<V extends IBaseWidget["value"]> implements IBaseWidget {
// We don't want our value to be an array as a widget will be serialized as an "input" for the API
// which uses an array value to represent a link. To keep things simpler, we'll avoid using an
// array at all.
abstract value: T extends Array<any> ? never : T;
abstract value: NotArray<V>;
name: string;
type: "custom" = "custom";
options: IWidgetOptions = {};
y: number = 0;
last_y: number = 0;
protected mouseDowned: Vector2 | null = null;
@@ -94,6 +100,10 @@ export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
this.name = name;
}
serializeValue(node: LGraphNode, index: number): Promise<V> | V {
return this.value;
}
private clickWasWithinBounds(pos: Vector2, bounds: Vector2 | Vector4) {
let xStart = bounds[0];
let xEnd = xStart + (bounds.length > 2 ? bounds[2]! : bounds[1]!);
@@ -101,14 +111,14 @@ export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
if (bounds.length === 2) {
return clickedX;
}
return clickedX && pos[1] >= bounds[1] && pos[1] <= bounds[1] + bounds[3];
return clickedX && pos[1] >= bounds[1] && pos[1] <= bounds[1] + bounds[3]!;
}
mouse(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode) {
mouse(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode) {
const canvas = app.canvas as TLGraphCanvas;
if (event.type == "pointerdown") {
this.mouseDowned = [...pos];
this.mouseDowned = [...pos] as Vector2;
this.isMouseDownedAndOver = true;
this.downedHitAreasForMove.length = 0;
// Loop over out bounds data and call any specifics.
@@ -172,7 +182,7 @@ export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
}
/** An event that fires when the pointer is pressed down (once). */
onMouseDown(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode): boolean | void {
onMouseDown(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean | void {
return;
}
@@ -180,7 +190,7 @@ export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
* An event that fires when the pointer is let go. Only fires if this was the widget that was
* originally pressed down.
*/
onMouseUp(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode): boolean | void {
onMouseUp(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean | void {
return;
}
@@ -189,7 +199,7 @@ export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
* of the widget. Check `isMouseDownedAndOver` to determine if the mouse is currently over the
* widget or not.
*/
onMouseMove(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode): boolean | void {
onMouseMove(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean | void {
return;
}
}
@@ -199,11 +209,11 @@ export abstract class RgthreeBaseWidget<T> implements IWidget<T, any> {
*/
export class RgthreeBetterButtonWidget extends RgthreeBaseWidget<string> {
value: string = "";
mouseUpCallback: (event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode) => boolean | void;
mouseUpCallback: (event: CanvasMouseEvent, pos: Vector2, node: LGraphNode) => boolean | void;
constructor(
name: string,
mouseUpCallback: (event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode) => boolean | void,
mouseUpCallback: (event: CanvasMouseEvent, pos: Vector2, node: LGraphNode) => boolean | void,
) {
super(name);
this.mouseUpCallback = mouseUpCallback;
@@ -213,7 +223,7 @@ export class RgthreeBetterButtonWidget extends RgthreeBaseWidget<string> {
drawWidgetButton({ctx, node, width, height, y}, this.name, this.isMouseDownedAndOver);
}
override onMouseUp(event: AdjustedMouseEvent, pos: Vector2, node: LGraphNode) {
override onMouseUp(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode) {
return this.mouseUpCallback(event, pos, node);
}
}
@@ -221,24 +231,24 @@ export class RgthreeBetterButtonWidget extends RgthreeBaseWidget<string> {
/**
* A better implementation of the LiteGraph text widget, including auto ellipsis.
*/
export class RgthreeBetterTextWidget implements IWidget<string> {
name: string;
export class RgthreeBetterTextWidget extends RgthreeBaseWidget<string> {
value: string;
constructor(name: string, value: string) {
super(name);
this.name = name;
this.value = value;
}
draw(ctx: CanvasRenderingContext2D, node: LGraphNode, width: number, y: number, height: number) {
const widgetData = drawNodeWidget(ctx, { width, height, posY: y });
const widgetData = drawNodeWidget(ctx, {width, height, posY: y});
if (!widgetData.lowQuality) {
drawLabelAndValue(ctx, this.name, this.value, width, y, height);
}
}
mouse(event: MouseEvent, pos: Vector2, node: LGraphNode) {
override mouse(event: CanvasMouseEvent, pos: Vector2, node: LGraphNode): boolean {
const canvas = app.canvas as TLGraphCanvas;
if (event.type == "pointerdown") {
canvas.prompt("Label", this.value, (v: string) => (this.value = v), event);
@@ -260,13 +270,13 @@ type RgthreeDividerWidgetOptions = {
thickness: number;
};
/**
* A divider widget; can also be used as a spacer if fed a 0 thickness.
*/
export class RgthreeDividerWidget implements IWidget<null> {
options = { serialize: false };
value = null;
name = "divider";
export class RgthreeDividerWidget extends RgthreeBaseWidget<string> {
override options = {serialize: false};
value = '';
private readonly widgetOptions: RgthreeDividerWidgetOptions = {
marginTop: 7,
@@ -278,6 +288,7 @@ export class RgthreeDividerWidget implements IWidget<null> {
};
constructor(widgetOptions?: Partial<RgthreeDividerWidgetOptions>) {
super("divider");
Object.assign(this.widgetOptions, widgetOptions || {});
}
@@ -310,22 +321,21 @@ export type RgthreeLabelWidgetOptions = {
/** A label to put on the right side. */
actionLabel?: "__PLUS_ICON__" | string;
actionCallback?: (event: PointerEvent) => void;
actionCallback?: (event: PointerEvent | CanvasMouseEvent) => void;
};
/**
* A simple label widget, drawn with no background.
*/
export class RgthreeLabelWidget implements IWidget<null> {
options = { serialize: false };
value = null;
name: string;
export class RgthreeLabelWidget extends RgthreeBaseWidget<string> {
override options = {serialize: false};
value = '';
private readonly widgetOptions: RgthreeLabelWidgetOptions = {};
private posY: number = 0;
constructor(name: string, widgetOptions?: RgthreeLabelWidgetOptions) {
this.name = name;
super(name);
Object.assign(this.widgetOptions, widgetOptions);
}
@@ -374,7 +384,7 @@ export class RgthreeLabelWidget implements IWidget<null> {
ctx.restore();
}
mouse(event: PointerEvent, nodePos: Vector2, node: LGraphNode) {
override mouse(event: CanvasMouseEvent, nodePos: Vector2, node: LGraphNode): boolean {
if (
event.type !== "pointerdown" ||
isLowQuality() ||
@@ -395,37 +405,52 @@ export class RgthreeLabelWidget implements IWidget<null> {
}
/** An invisible widget. */
export class RgthreeInvisibleWidget<T> implements IWidget<T> {
name: string;
type: string;
value: T;
serializeValue: IWidget['serializeValue'] = undefined;
export class RgthreeInvisibleWidget<T extends IBaseWidget["value"]> extends RgthreeBaseWidget<T> {
value: NotArray<T>;
private serializeValueFn?: (node: LGraphNode, index: number) => Promise<T> | T;
constructor(name: string, type: string, value: T, serializeValueFn: ()=> T) {
this.name = name;
this.type = type;
constructor(
name: string,
type: string,
value: NotArray<T>,
serializeValueFn?: (node: LGraphNode, index: number) => Promise<T> | T,
) {
super(name);
// this.type = type;
this.value = value;
if (serializeValueFn) {
this.serializeValue = serializeValueFn
}
this.serializeValueFn = serializeValueFn;
}
draw() { return; }
computeSize(width: number) : Vector2 { return [0, 0]; }
}
draw() {
return;
}
computeSize(width: number): Vector2 {
return [0, 0];
}
override serializeValue(node: LGraphNode, index: number): T | Promise<T> {
return this.serializeValueFn != null
? this.serializeValueFn(node, index)
: super.serializeValue(node, index);
}
}
type DrawContext = {
ctx: CanvasRenderingContext2D,
node: LGraphNode,
width: number,
y: number,
height: number,
}
ctx: CanvasRenderingContext2D;
node: LGraphNode;
width: number;
y: number;
height: number;
};
/**
* Draws a better button.
*/
export function drawWidgetButton(drawCtx: DrawContext, text: string, isMouseDownedAndOver: boolean = false) {
export function drawWidgetButton(
drawCtx: DrawContext,
text: string,
isMouseDownedAndOver: boolean = false,
) {
// First, add a shadow if we're not down or lowquality.
if (!isLowQuality() && !isMouseDownedAndOver) {
drawRoundedRectangle(drawCtx.ctx, {
@@ -458,4 +483,4 @@ export function drawWidgetButton(drawCtx: DrawContext, text: string, isMouseDown
drawCtx.y + drawCtx.height / 2 + (isMouseDownedAndOver ? 1 : 0),
);
}
}
}
+16 -17
View File
@@ -250,15 +250,15 @@ export async function importA1111(graph: lazyComfyAny, parameters: lazyComfyAny)
let positive = parameters.substr(0, p2).trim()
let negative = parameters.substring(p2 + 18, p).trim()
const ckptNode = LiteGraph.createNode('CheckpointLoaderSimple')
const clipSkipNode = LiteGraph.createNode('CLIPSetLastLayer')
const positiveNode = LiteGraph.createNode('CLIPTextEncode')
const negativeNode = LiteGraph.createNode('CLIPTextEncode')
const samplerNode = LiteGraph.createNode('KSampler')
const imageNode = LiteGraph.createNode('EmptyLatentImage')
const vaeNode = LiteGraph.createNode('VAEDecode')
const vaeLoaderNode = LiteGraph.createNode('VAELoader')
const saveNode = LiteGraph.createNode('SaveImage')
const ckptNode = LiteGraph.createNode('CheckpointLoaderSimple')!
const clipSkipNode = LiteGraph.createNode('CLIPSetLastLayer')!
const positiveNode = LiteGraph.createNode('CLIPTextEncode')!
const negativeNode = LiteGraph.createNode('CLIPTextEncode')!
const samplerNode = LiteGraph.createNode('KSampler')!
const imageNode = LiteGraph.createNode('EmptyLatentImage')!
const vaeNode = LiteGraph.createNode('VAEDecode')!
const vaeLoaderNode = LiteGraph.createNode('VAELoader')!
const saveNode = LiteGraph.createNode('SaveImage')!
let hrSamplerNode = null as lazyComfyAny
let hrSteps = null
@@ -416,7 +416,7 @@ export async function importA1111(graph: lazyComfyAny, parameters: lazyComfyAny)
let latentNode
if (hrMethod.startsWith('Latent')) {
latentNode = upscaleNode = LiteGraph.createNode('LatentUpscale')
latentNode = upscaleNode = LiteGraph.createNode('LatentUpscale')!
graph.add(upscaleNode)
samplerNode.connect(0, upscaleNode, 0)
@@ -427,29 +427,28 @@ export async function importA1111(graph: lazyComfyAny, parameters: lazyComfyAny)
}
setWidgetValue(upscaleNode, 'upscale_method', hrMethod, true)
} else {
const decode = LiteGraph.createNode('VAEDecodeTiled')
const decode = LiteGraph.createNode('VAEDecodeTiled')!
graph.add(decode)
samplerNode.connect(0, decode, 0)
vaeLoaderNode.connect(0, decode, 1)
const upscaleLoaderNode =
LiteGraph.createNode('UpscaleModelLoader')
const upscaleLoaderNode = LiteGraph.createNode('UpscaleModelLoader')!
graph.add(upscaleLoaderNode)
setWidgetValue(upscaleLoaderNode, 'model_name', hrMethod, true)
const modelUpscaleNode = LiteGraph.createNode(
'ImageUpscaleWithModel'
)
)!
graph.add(modelUpscaleNode)
decode.connect(0, modelUpscaleNode, 1)
upscaleLoaderNode.connect(0, modelUpscaleNode, 0)
upscaleNode = LiteGraph.createNode('ImageScale')
upscaleNode = LiteGraph.createNode('ImageScale')!
graph.add(upscaleNode)
modelUpscaleNode.connect(0, upscaleNode, 0)
const vaeEncodeNode = (latentNode =
LiteGraph.createNode('VAEEncodeTiled'))
LiteGraph.createNode('VAEEncodeTiled')!)!
graph.add(vaeEncodeNode)
upscaleNode.connect(0, vaeEncodeNode, 0)
vaeLoaderNode.connect(0, vaeEncodeNode, 1)
@@ -458,7 +457,7 @@ export async function importA1111(graph: lazyComfyAny, parameters: lazyComfyAny)
setWidgetValue(upscaleNode, 'width', ceil64(uw))
setWidgetValue(upscaleNode, 'height', ceil64(uh))
hrSamplerNode = LiteGraph.createNode('KSampler')
hrSamplerNode = LiteGraph.createNode('KSampler')!
graph.add(hrSamplerNode)
ckptNode.connect(0, hrSamplerNode, 0)
positiveNode.connect(0, hrSamplerNode, 1)
+1 -1
View File
@@ -1,4 +1,4 @@
import type { LGraphNode, LGraphNodeConstructor } from "typings/litegraph.js";
import type { LGraphNode, LGraphNodeConstructor } from "@litegraph/litegraph.js";
import { createElement as $el, getClosestOrSelf, setAttributes } from "./utils_dom.js";
type RgthreeDialogButton = {
+9 -3
View File
@@ -1,17 +1,23 @@
import type { BadLinksData, SerializedGraph, SerializedLink, SerializedNode } from "typings/index.js";
import type { LGraph, LGraphNode, LLink, serializedLGraph } from "typings/litegraph.js";
import type { LGraph, LGraphNode, LLink} from "@litegraph/litegraph.js";
import type { NodeId } from "@litegraph/LGraphNode";
// TODO: Fix comyui types.
type serializedLGraph = any;
type serializedLGraphNode = any;
enum IoDirection {
INPUT,
OUTPUT,
}
function getNodeById(graph: SerializedGraph | LGraph | serializedLGraph, id: number) {
function getNodeById(graph: SerializedGraph | LGraph | serializedLGraph, id: NodeId) {
if ((graph as LGraph).getNodeById) {
return (graph as LGraph).getNodeById(id);
}
graph = graph as SerializedGraph;
return graph.nodes.find((n) => n.id === id)!;
return graph.nodes.find((node: serializedLGraphNode) => Number(node.id) === id)!;
}
function extendLink(link: SerializedLink) {
+1 -1
View File
@@ -10,7 +10,7 @@ import type {
ComfyApiPrompt,
} from "typings/comfy.js";
import { api } from "scripts/api.js";
import type { LGraph as TLGraph, LGraphCanvas as TLGraphCanvas } from "typings/litegraph.js";
import type { LGraph as TLGraph, LGraphCanvas as TLGraphCanvas } from "@litegraph/litegraph.js";
import { Resolver, getResolver } from "./shared_utils.js";
/**
+1 -1
View File
@@ -144,7 +144,7 @@ export function setObjectValue(obj: any, objKey: string, value: any, createMissi
/**
* Moves an item in an array (by item or its index) to another index.
*/
export function moveArrayItem<T>(arr: T[], itemOrFrom: T | number, to: number) {
export function moveArrayItem(arr: any[], itemOrFrom: any, to: number) {
const from = typeof itemOrFrom === "number" ? itemOrFrom : arr.indexOf(itemOrFrom);
arr.splice(to, 0, arr.splice(from, 1)[0]!);
}
+1 -1
View File
@@ -1,4 +1,4 @@
import type { LGraphNode } from "typings/litegraph.js";
import type { LGraphNode } from "@litegraph/litegraph.js";
import type { ComfyApp, ComfyWidget } from "../typings/comfy.js";
type ComfyWidgetFn = (
+3 -17
View File
@@ -1,20 +1,6 @@
import type { LGraphGroup as TLGraphGroup, LGraphNode as TLGraphNode, IWidget, SerializedLGraphNode, LGraph as TLGraph, LGraphCanvas as TLGraphCanvas, LiteGraph as TLiteGraph } from "./litegraph.js";
import type { LGraphGroup as TLGraphGroup, LGraphNode as TLGraphNode, IWidget, SerializedLGraphNode, LGraph as TLGraph, LGraphCanvas as TLGraphCanvas, LiteGraph as TLiteGraph, INodeSlot } from "@litegraph/litegraph.js";
import type {Constructor, SerializedGraph} from './index.js';
declare global {
const LiteGraph: typeof TLiteGraph;
const LGraph: typeof TLGraph;
const LGraphNode: typeof TLGraphNode;
const LGraphCanvas: typeof TLGraphCanvas;
const LGraphGroup: typeof TLGraphGroup;
interface Window {
// Used in the common/comfyui_shim to determine if we're in the app or not.
comfyAPI: {
// So much more stuffed in here, add as needed.
[key: string]: any;
}
}
}
export type getPngMetadata = (file: File | Blob) => { workflow?: string; prompt?: string };
export type getWebpMetadata = (file: File | Blob) => {
@@ -58,7 +44,7 @@ export interface ComfyWidget extends IWidget {
}
export interface ComfyGraphNode extends TLGraphNode {
getExtraMenuOptions: (node: TLGraphNode, options: ContextMenuItem[]) => void;
// getExtraMenuOptions: (node: TLGraphNode, options: ContextMenuItem[]) => void;
onExecuted(message: any): void;
}
@@ -73,7 +59,7 @@ export interface ComfyNodeConstructor extends Constructor<ComfyNode> {
static comfyClass: string;
}
export type NodeMode = 0|1|2|3|4|undefined;
// export type NodeMode = 0|1|2|3|4|undefined;
export interface ComfyExtension {
+69 -1730
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -1,8 +1,9 @@
import type { AdjustedMouseEvent, LGraphNode, Vector2 } from "./litegraph.js";
import type { LGraphNode, Vector2 } from "@litegraph/litegraph.js";
import type { CanvasMouseEvent } from "@litegraph/types/events.js";
import type {Constructor} from "./index.js";
import type {RgthreeBaseVirtualNode} from '../comfyui/base_node.js'
export type AdjustedMouseCustomEvent = CustomEvent<{ originalEvent: AdjustedMouseEvent }>;
export type AdjustedMouseCustomEvent = CustomEvent<{ originalEvent: CanvasMouseEvent }>;
export interface RgthreeBaseNodeConstructor extends Constructor<RgthreeBaseNode> {
+1
View File
@@ -7,6 +7,7 @@
// ],
"baseUrl": "./",
"paths": {
"@litegraph/*": ["node_modules/@comfyorg/litegraph/dist/*"],
"typings/*": ["src_web/typings/*"],
"rgthree/common/*": ["src_web/common/*"],
"node_modules": ["node_modules/*"],
+5 -5
View File
@@ -191,16 +191,16 @@ export class BaseAnyInputConnectedNode extends RgthreeBaseVirtualNode {
}
}
const oldLGraphNodeConnectByType = LGraphNode.prototype.connectByType;
LGraphNode.prototype.connectByType = function connectByType(slot, sourceNode, sourceSlotType, optsIn) {
if (sourceNode.inputs) {
for (const [index, input] of sourceNode.inputs.entries()) {
LGraphNode.prototype.connectByType = function connectByType(slot, targetNode, targetSlotType, optsIn) {
if (targetNode.inputs) {
for (const [index, input] of targetNode.inputs.entries()) {
if (!input.link && input.type === "*") {
this.connect(slot, sourceNode, index);
this.connect(slot, targetNode, index);
return null;
}
}
}
return ((oldLGraphNodeConnectByType &&
oldLGraphNodeConnectByType.call(this, slot, sourceNode, sourceSlotType, optsIn)) ||
oldLGraphNodeConnectByType.call(this, slot, targetNode, targetSlotType, optsIn)) ||
null);
};
+7 -3
View File
@@ -29,7 +29,7 @@ export class RgthreeBaseNode extends LGraphNode {
}
this.checkAndRunOnConstructed();
});
defineProperty(this, 'mode', {
defineProperty(this, "mode", {
get: () => {
return this.rgthree_mode;
},
@@ -80,7 +80,7 @@ export class RgthreeBaseNode extends LGraphNode {
}
clone() {
const cloned = super.clone();
if (cloned.properties && !!window.structuredClone) {
if ((cloned === null || cloned === void 0 ? void 0 : cloned.properties) && !!window.structuredClone) {
cloned.properties = structuredClone(cloned.properties);
}
return cloned;
@@ -91,6 +91,9 @@ export class RgthreeBaseNode extends LGraphNode {
action;
}
removeWidget(widgetOrSlot) {
if (!this.widgets) {
return;
}
if (typeof widgetOrSlot === "number") {
this.widgets.splice(widgetOrSlot, 1);
}
@@ -105,7 +108,7 @@ export class RgthreeBaseNode extends LGraphNode {
var _a, _b;
const menu_info = [];
if ((_b = (_a = slot === null || slot === void 0 ? void 0 : slot.output) === null || _a === void 0 ? void 0 : _a.links) === null || _b === void 0 ? void 0 : _b.length) {
menu_info.push({ content: "Disconnect Links", slot: slot });
menu_info.push({ content: "Disconnect Links", slot });
}
let inputOrOutput = slot.input || slot.output;
if (inputOrOutput) {
@@ -161,6 +164,7 @@ export class RgthreeBaseNode extends LGraphNode {
if (help) {
addHelpMenuItem(this, help, options);
}
return options;
}
}
RgthreeBaseNode.exposedActions = [];
+2 -1
View File
@@ -59,10 +59,11 @@ export class PowerPrompt {
};
const onPropertyChanged = this.node.onPropertyChanged;
this.node.onPropertyChanged = (property, value, prevValue) => {
onPropertyChanged && onPropertyChanged.call(this, property, value, prevValue);
const v = onPropertyChanged && onPropertyChanged.call(this.node, property, value, prevValue);
if (property === "combos_filter") {
this.refreshCombos(this.nodeData);
}
return v !== null && v !== void 0 ? v : true;
};
for (let i = this.node.widgets.length - 1; i >= 0; i--) {
if (this.shouldRemoveServerWidget(this.node.widgets[i])) {
+1
View File
@@ -70,6 +70,7 @@ export class Bookmark extends RgthreeBaseVirtualNode {
input.value = Object.keys(KEY_EVENT_SERVICE.downKeys).join(" + ");
});
}
return false;
}
canvasToBookmark() {
var _a, _b;
+3 -3
View File
@@ -13,17 +13,17 @@ class BypasserNode extends BaseNodeModeChanger {
}
async handleAction(action) {
if (action === "Bypass all") {
for (const widget of this.widgets) {
for (const widget of this.widgets || []) {
this.forceWidgetOff(widget, true);
}
}
else if (action === "Enable all") {
for (const widget of this.widgets) {
for (const widget of this.widgets || []) {
this.forceWidgetOn(widget, true);
}
}
else if (action === "Toggle all") {
for (const widget of this.widgets) {
for (const widget of this.widgets || []) {
this.forceWidgetToggle(widget, true);
}
}
+10 -11
View File
@@ -67,37 +67,36 @@ export class BaseContextNode extends RgthreeBaseServerNode {
this.___collapsed_width = 30 + (title ? 10 + ctx.measureText(title).width : 0);
ctx.font = oldFont;
}
connectByType(slot, sourceNode, sourceSlotType, optsIn) {
let canConnect = super.connectByType &&
super.connectByType.call(this, slot, sourceNode, sourceSlotType, optsIn);
connectByType(slot, targetNode, targetSlotType, optsIn) {
var _a;
let canConnect = (_a = super.connectByType) === null || _a === void 0 ? void 0 : _a.call(this, slot, targetNode, targetSlotType, optsIn);
if (!super.connectByType) {
canConnect = LGraphNode.prototype.connectByType.call(this, slot, sourceNode, sourceSlotType, optsIn);
canConnect = LGraphNode.prototype.connectByType.call(this, slot, targetNode, targetSlotType, optsIn);
}
if (!canConnect && slot === 0) {
const ctrlKey = KEY_EVENT_SERVICE.ctrlKey;
for (const [index, input] of (sourceNode.inputs || []).entries()) {
for (const [index, input] of (targetNode.inputs || []).entries()) {
if (input.link && !ctrlKey) {
continue;
}
const thisOutputSlot = findMatchingIndexByTypeOrName(sourceNode, input, this.outputs);
const thisOutputSlot = findMatchingIndexByTypeOrName(targetNode, input, this.outputs);
if (thisOutputSlot > -1) {
this.connect(thisOutputSlot, sourceNode, index);
this.connect(thisOutputSlot, targetNode, index);
}
}
}
return null;
}
connectByTypeOutput(slot, sourceNode, sourceSlotType, optsIn) {
var _a;
let canConnect = super.connectByTypeOutput &&
super.connectByTypeOutput.call(this, slot, sourceNode, sourceSlotType, optsIn);
var _a, _b;
let canConnect = (_a = super.connectByTypeOutput) === null || _a === void 0 ? void 0 : _a.call(this, slot, sourceNode, sourceSlotType, optsIn);
if (!super.connectByType) {
canConnect = LGraphNode.prototype.connectByTypeOutput.call(this, slot, sourceNode, sourceSlotType, optsIn);
}
if (!canConnect && slot === 0) {
const ctrlKey = KEY_EVENT_SERVICE.ctrlKey;
for (const [index, output] of (sourceNode.outputs || []).entries()) {
if (((_a = output.links) === null || _a === void 0 ? void 0 : _a.length) && !ctrlKey) {
if (((_b = output.links) === null || _b === void 0 ? void 0 : _b.length) && !ctrlKey) {
continue;
}
const thisInputSlot = findMatchingIndexByTypeOrName(sourceNode, output, this.inputs);
+2 -2
View File
@@ -21,9 +21,9 @@ class DynamicContextSwitchNode extends DynamicContextNodeBase {
handleUpstreamMutation(mutation) {
this.scheduleHardRefresh();
}
onConnectionsChange(type, slotIndex, isConnected, link, ioSlot) {
onConnectionsChange(type, slotIndex, isConnected, link, inputOrOutput) {
var _a;
(_a = super.onConnectionsChange) === null || _a === void 0 ? void 0 : _a.call(this, type, slotIndex, isConnected, link, ioSlot);
(_a = super.onConnectionsChange) === null || _a === void 0 ? void 0 : _a.call(this, type, slotIndex, isConnected, link, inputOrOutput);
if (this.configuring) {
return;
}
+22 -18
View File
@@ -19,7 +19,7 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
this.properties["buttonText"] = "🎬 Action!";
this.properties["shortcutModifier"] = "alt";
this.properties["shortcutKey"] = "";
this.buttonWidget = this.addWidget("button", this.properties["buttonText"], null, () => {
this.buttonWidget = this.addWidget("button", this.properties["buttonText"], "", () => {
this.executeConnectedNodes();
}, { serialize: false });
this.keypressBound = this.onKeypress.bind(this);
@@ -32,7 +32,7 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
if (info.widgets_values) {
for (let [index, value] of info.widgets_values.entries()) {
if (index > 0) {
if (value.startsWith("comfy_action:")) {
if (typeof value === "string" && value.startsWith("comfy_action:")) {
value = value.replace("comfy_action:", "");
this.addComfyActionWidget(index, value);
}
@@ -92,14 +92,15 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
this.executingFromShortcut = false;
}
onPropertyChanged(property, value, _prevValue) {
if (property == "buttonText") {
onPropertyChanged(property, value, prevValue) {
var _a, _b;
if (property == "buttonText" && typeof value === "string") {
this.buttonWidget.name = value;
}
if (property == "shortcutKey") {
value = value.trim();
this.properties["shortcutKey"] = (value && value[0].toLowerCase()) || "";
if (property == "shortcutKey" && typeof value === "string") {
this.properties["shortcutKey"] = (_b = (_a = value.trim()[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : "";
}
return true;
}
handleLinkedNodesStabilization(linkedNodes) {
var _a, _b, _c, _d, _e, _f, _g, _h;
@@ -200,6 +201,9 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
node.mode = MODE_ALWAYS;
}
if (node.handleAction) {
if (typeof action !== 'string') {
throw new Error('Fast Actions Button action should be a string: ' + action);
}
await node.handleAction(action);
}
app.graph.change();
@@ -210,18 +214,18 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
addComfyActionWidget(slot, value) {
let widget = this.addWidget("combo", "Comfy Action", "None", () => {
if (widget.value.startsWith("MOVE ")) {
if (String(widget.value).startsWith("MOVE ")) {
this.widgets.push(this.widgets.splice(this.widgets.indexOf(widget), 1)[0]);
widget.value = widget["lastValue_"];
widget.value = String(widget.rgthree_lastValue);
}
else if (widget.value.startsWith("REMOVE ")) {
else if (String(widget.value).startsWith("REMOVE ")) {
this.removeWidget(widget);
}
widget["lastValue_"] = widget.value;
widget.rgthree_lastValue = widget.value;
}, {
values: ["None", "Queue Prompt", "REMOVE Comfy Action", "MOVE to end"],
});
widget["lastValue_"] = value;
widget.rgthree_lastValue = value;
widget.serializeValue = async (_node, _index) => {
return `comfy_app:${widget === null || widget === void 0 ? void 0 : widget.value}`;
};
@@ -231,12 +235,12 @@ class FastActionsButton extends BaseAnyInputConnectedNode {
}
return widget;
}
onSerialize(o) {
var _a;
super.onSerialize && super.onSerialize(o);
for (let [index, value] of (o.widgets_values || []).entries()) {
if (((_a = this.widgets[index]) === null || _a === void 0 ? void 0 : _a.name) === "Comfy Action") {
o.widgets_values[index] = `comfy_action:${value}`;
onSerialize(serialised) {
var _a, _b;
(_a = super.onSerialize) === null || _a === void 0 ? void 0 : _a.call(this, serialised);
for (let [index, value] of (serialised.widgets_values || []).entries()) {
if (((_b = this.widgets[index]) === null || _b === void 0 ? void 0 : _b.name) === "Comfy Action") {
serialised.widgets_values[index] = `comfy_action:${value}`;
}
}
}
+2
View File
@@ -135,6 +135,8 @@ export class BaseFastGroupsModeChanger extends RgthreeBaseVirtualNode {
this.tempSize = [...this.size];
widget = this.addCustomWidget({
name: "RGTHREE_TOGGLE_AND_NAV",
type: 'toggle',
y: 0,
label: "",
value: false,
disabled: false,
+2 -2
View File
@@ -86,13 +86,13 @@ app.registerExtension({
LGraphCanvas.prototype.drawGroups = function (canvasEl, ctx) {
drawGroups.apply(this, [...arguments]);
if (!CONFIG_SERVICE.getFeatureValue("group_header_fast_toggle.enabled") ||
!rgthree.lastAdjustedMouseEvent) {
!rgthree.lastCanvasMouseEvent) {
return;
}
const graph = app.graph;
let groups;
if (CONFIG_SERVICE.getFeatureValue("group_header_fast_toggle.show") !== "always") {
const hoverGroup = graph.getGroupOnPos(rgthree.lastAdjustedMouseEvent.canvasX, rgthree.lastAdjustedMouseEvent.canvasY);
const hoverGroup = graph.getGroupOnPos(rgthree.lastCanvasMouseEvent.canvasX, rgthree.lastCanvasMouseEvent.canvasY);
groups = hoverGroup ? [hoverGroup] : [];
}
else {
+14 -13
View File
@@ -56,12 +56,12 @@ export class RgthreeImageComparer extends RgthreeBaseServerNode {
this.canvasWidget.value = { images: imagesToChoose };
}
}
onSerialize(o) {
onSerialize(serialised) {
var _a;
super.onSerialize && super.onSerialize(o);
for (let [index, widget_value] of (o.widgets_values || []).entries()) {
super.onSerialize && super.onSerialize(serialised);
for (let [index, widget_value] of (serialised.widgets_values || []).entries()) {
if (((_a = this.widgets[index]) === null || _a === void 0 ? void 0 : _a.name) === "rgthree_comparer") {
o.widgets_values[index] = this.widgets[index].value.images.map((d) => {
serialised.widgets_values[index] = this.widgets[index].value.images.map((d) => {
d = { ...d };
delete d.img;
return d;
@@ -87,26 +87,27 @@ export class RgthreeImageComparer extends RgthreeBaseServerNode {
});
}
}
onMouseDown(event, pos, graphCanvas) {
onMouseDown(event, pos, canvas) {
var _a;
(_a = super.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(this, event, pos, graphCanvas);
(_a = super.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(this, event, pos, canvas);
this.setIsPointerDown(true);
return false;
}
onMouseEnter(event, pos, graphCanvas) {
onMouseEnter(event) {
var _a;
(_a = super.onMouseEnter) === null || _a === void 0 ? void 0 : _a.call(this, event, pos, graphCanvas);
(_a = super.onMouseEnter) === null || _a === void 0 ? void 0 : _a.call(this, event);
this.setIsPointerDown(!!app.canvas.pointer_is_down);
this.isPointerOver = true;
}
onMouseLeave(event, pos, graphCanvas) {
onMouseLeave(event) {
var _a;
(_a = super.onMouseLeave) === null || _a === void 0 ? void 0 : _a.call(this, event, pos, graphCanvas);
(_a = super.onMouseLeave) === null || _a === void 0 ? void 0 : _a.call(this, event);
this.setIsPointerDown(false);
this.isPointerOver = false;
}
onMouseMove(event, pos, graphCanvas) {
onMouseMove(event, pos, canvas) {
var _a;
(_a = super.onMouseMove) === null || _a === void 0 ? void 0 : _a.call(this, event, pos, graphCanvas);
(_a = super.onMouseMove) === null || _a === void 0 ? void 0 : _a.call(this, event, pos, canvas);
this.pointerOverPos = [...pos];
this.imageIndex = this.pointerOverPos[0] > this.size[0] / 2 ? 1 : 0;
}
@@ -343,7 +344,7 @@ class RgthreeImageComparerWidget extends RgthreeBaseWidget {
computeSize(width) {
return [width, 20];
}
serializeValue(serializedNode, widgetIndex) {
serializeValue(node, index) {
const v = [];
for (const data of this._value.images) {
const d = { ...data };
+5 -5
View File
@@ -120,7 +120,7 @@ Label["@padding"] = { type: "number" };
Label["@borderRadius"] = { type: "number" };
const oldDrawNode = LGraphCanvas.prototype.drawNode;
LGraphCanvas.prototype.drawNode = function (node, ctx) {
if (node.constructor === Label) {
if (node.constructor === Label.prototype.constructor) {
node.bgcolor = "transparent";
node.color = "transparent";
const v = oldDrawNode.apply(this, arguments);
@@ -131,18 +131,18 @@ LGraphCanvas.prototype.drawNode = function (node, ctx) {
return v;
};
const oldGetNodeOnPos = LGraph.prototype.getNodeOnPos;
LGraph.prototype.getNodeOnPos = function (x, y, nodes_list, margin) {
LGraph.prototype.getNodeOnPos = function (x, y, nodes_list) {
var _a, _b;
if (nodes_list &&
rgthree.processingMouseDown &&
((_a = rgthree.lastAdjustedMouseEvent) === null || _a === void 0 ? void 0 : _a.type.includes("down")) &&
((_b = rgthree.lastAdjustedMouseEvent) === null || _b === void 0 ? void 0 : _b.which) === 1) {
((_a = rgthree.lastCanvasMouseEvent) === null || _a === void 0 ? void 0 : _a.type.includes("down")) &&
((_b = rgthree.lastCanvasMouseEvent) === null || _b === void 0 ? void 0 : _b.which) === 1) {
let isDoubleClick = LiteGraph.getTime() - LGraphCanvas.active_canvas.last_mouseclick < 300;
if (!isDoubleClick) {
nodes_list = [...nodes_list].filter((n) => { var _a; return !(n instanceof Label) || !((_a = n.flags) === null || _a === void 0 ? void 0 : _a.pinned); });
}
}
return oldGetNodeOnPos.apply(this, [x, y, nodes_list, margin]);
return oldGetNodeOnPos.apply(this, [x, y, nodes_list]);
};
app.registerExtension({
name: "rgthree.Label",
+7 -7
View File
@@ -8,7 +8,7 @@ app.registerExtension({
const logger = rgthree.newLogSession("[ContextMenuAutoNest]");
const existingContextMenu = LiteGraph.ContextMenu;
LiteGraph.ContextMenu = function (values, options) {
var _a, _b, _c, _d, _e, _f;
var _a, _b, _c, _d, _e, _f, _g, _h;
const threshold = CONFIG_SERVICE.getConfigValue("features.menu_auto_nest.threshold", 20);
const enabled = CONFIG_SERVICE.getConfigValue("features.menu_auto_nest.subdirs", false);
let incompatible = !enabled || !!((_a = options === null || options === void 0 ? void 0 : options.extra) === null || _a === void 0 ? void 0 : _a.rgthree_doNotNest);
@@ -16,7 +16,7 @@ app.registerExtension({
if (values.length <= threshold) {
incompatible = `Skipping context menu auto nesting b/c threshold is not met (${threshold})`;
}
if (!((_b = options.parentMenu) === null || _b === void 0 ? void 0 : _b.options.rgthree_originalCallback)) {
if (!((_c = (_b = options.parentMenu) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.rgthree_originalCallback)) {
if (!(options === null || options === void 0 ? void 0 : options.callback)) {
incompatible = `Skipping context menu auto nesting b/c a callback was expected.`;
}
@@ -28,7 +28,7 @@ app.registerExtension({
if (incompatible) {
if (enabled) {
const [n, v] = logger.infoParts("Skipping context menu auto nesting for incompatible menu.");
(_c = console[n]) === null || _c === void 0 ? void 0 : _c.call(console, ...v);
(_d = console[n]) === null || _d === void 0 ? void 0 : _d.call(console, ...v);
}
return existingContextMenu.apply(this, [...arguments]);
}
@@ -62,15 +62,15 @@ app.registerExtension({
if (foldersCount > 0) {
options.rgthree_originalCallback =
options.rgthree_originalCallback ||
((_d = options.parentMenu) === null || _d === void 0 ? void 0 : _d.options.rgthree_originalCallback) ||
((_f = (_e = options.parentMenu) === null || _e === void 0 ? void 0 : _e.options) === null || _f === void 0 ? void 0 : _f.rgthree_originalCallback) ||
options.callback;
const oldCallback = options.rgthree_originalCallback;
const oldCallback = options === null || options === void 0 ? void 0 : options.rgthree_originalCallback;
options.callback = undefined;
const newCallback = (item, options, event, parentMenu, node) => {
oldCallback === null || oldCallback === void 0 ? void 0 : oldCallback(item === null || item === void 0 ? void 0 : item.rgthree_originalValue, options, event, undefined, node);
};
const [n, v] = logger.infoParts(`Nested folders found (${foldersCount}).`);
(_e = console[n]) === null || _e === void 0 ? void 0 : _e.call(console, ...v);
(_g = console[n]) === null || _g === void 0 ? void 0 : _g.call(console, ...v);
const newValues = [];
for (const [folderName, folderValues] of Object.entries(folders)) {
newValues.push({
@@ -101,7 +101,7 @@ app.registerExtension({
}));
}
if (options.scale == null) {
options.scale = Math.max(((_f = app.canvas.ds) === null || _f === void 0 ? void 0 : _f.scale) || 1, 1);
options.scale = Math.max(((_h = app.canvas.ds) === null || _h === void 0 ? void 0 : _h.scale) || 1, 1);
}
const oldCtrResponse = existingContextMenu.call(this, values, options);
if (oldCtrResponse === null || oldCtrResponse === void 0 ? void 0 : oldCtrResponse.constructor) {
+4 -3
View File
@@ -26,9 +26,9 @@ app.registerExtension({
if (await clipboardSupportedPromise) {
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function (canvas, options) {
var _a;
getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined;
if ((_a = this.imgs) === null || _a === void 0 ? void 0 : _a.length) {
var _a, _b;
options = (_a = getExtraMenuOptions === null || getExtraMenuOptions === void 0 ? void 0 : getExtraMenuOptions.call(this, canvas, options)) !== null && _a !== void 0 ? _a : options;
if ((_b = this.imgs) === null || _b === void 0 ? void 0 : _b.length) {
let img = this.imgs[this.imageIndex || 0] || this.imgs[this.overIndex || 0] || this.imgs[0];
const foundIdx = options.findIndex((option) => { var _a; return (_a = option === null || option === void 0 ? void 0 : option.content) === null || _a === void 0 ? void 0 : _a.includes("Copy Image"); });
if (img && foundIdx === -1) {
@@ -54,6 +54,7 @@ app.registerExtension({
}
}
}
return options;
};
}
}
+5 -3
View File
@@ -24,8 +24,8 @@ function showQueueGroupNodesMenuIfGroupIsSelected(existingOptions) {
if (CONFIG_SERVICE.getConfigValue("features.menu_queue_selected_nodes") === false) {
return;
}
const group = rgthree.lastAdjustedMouseEvent &&
app.graph.getGroupOnPos(rgthree.lastAdjustedMouseEvent.canvasX, rgthree.lastAdjustedMouseEvent.canvasY);
const group = rgthree.lastCanvasMouseEvent &&
app.graph.getGroupOnPos(rgthree.lastCanvasMouseEvent.canvasX, rgthree.lastCanvasMouseEvent.canvasY);
const outputNodes = group && getOutputNodes(group._nodes);
const menuItem = {
content: `Queue Group Output Nodes (rgthree) &nbsp;`,
@@ -46,9 +46,11 @@ app.registerExtension({
async beforeRegisterNodeDef(nodeType, nodeData) {
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function (canvas, options) {
getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined;
var _a;
options = (_a = getExtraMenuOptions === null || getExtraMenuOptions === void 0 ? void 0 : getExtraMenuOptions.call(this, canvas, options)) !== null && _a !== void 0 ? _a : options;
showQueueNodesMenuIfOutputNodesAreSelected(options);
showQueueGroupNodesMenuIfGroupIsSelected(options);
return options;
};
},
async setup() {
+2 -1
View File
@@ -35,13 +35,14 @@ class CombinerNode extends CollectorNode {
note.inputEl.style.fontWeight = "bold";
note.inputEl.style.fontStyle = "italic";
note.inputEl.style.opacity = "0.8";
this.getExtraMenuOptions = (_, options) => {
this.getExtraMenuOptions = (canvas, options) => {
options.splice(options.length - 1, 0, {
content: "‼️ Update to Node Collector",
callback: (_value, _options, _event, _parentMenu, _node) => {
updateCombinerToCollector(this);
},
});
return options;
};
}
configure(info) {
+1 -1
View File
@@ -123,7 +123,7 @@ class NodeModeRelay extends BaseCollectorNode {
mode = MODE_ALWAYS;
}
else {
mode = null;
mode = undefined;
}
}
this.dispatchModeToRepeater(mode);
+7 -8
View File
@@ -149,9 +149,8 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
},
},
];
let canvas = app.canvas;
new LiteGraph.ContextMenu(menuItems, { title: "LORA WIDGET", event: rgthree.lastAdjustedMouseEvent }, canvas.getCanvasWindow());
return null;
new LiteGraph.ContextMenu(menuItems, { title: "LORA WIDGET", event: rgthree.lastCanvasMouseEvent });
return undefined;
}
return this.defaultGetSlotMenuOptions(slot);
}
@@ -179,11 +178,11 @@ class RgthreePowerLoraLoader extends RgthreeBaseServerNode {
return allOn && ((_d = this.widgets) === null || _d === void 0 ? void 0 : _d.length) ? true : false;
}
toggleAllLoras() {
var _b;
var _b, _c;
const allOn = this.allLorasState();
const toggledTo = !allOn ? true : false;
for (const widget of this.widgets) {
if ((_b = widget.name) === null || _b === void 0 ? void 0 : _b.startsWith("lora_")) {
if (((_b = widget.name) === null || _b === void 0 ? void 0 : _b.startsWith("lora_")) && ((_c = widget.value) === null || _c === void 0 ? void 0 : _c.on) != null) {
widget.value.on = toggledTo;
}
}
@@ -374,8 +373,8 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget {
ctx.fillStyle = LiteGraph.WIDGET_TEXT_COLOR;
let rposX = node.size[0] - margin - innerMargin - innerMargin;
const strengthValue = this.showModelAndClip
? (_c = this.value.strengthTwo) !== null && _c !== void 0 ? _c : 1
: (_d = this.value.strength) !== null && _d !== void 0 ? _d : 1;
? ((_c = this.value.strengthTwo) !== null && _c !== void 0 ? _c : 1)
: ((_d = this.value.strength) !== null && _d !== void 0 ? _d : 1);
let textColor = undefined;
if (((_e = this.loraInfo) === null || _e === void 0 ? void 0 : _e.strengthMax) != null && strengthValue > ((_f = this.loraInfo) === null || _f === void 0 ? void 0 : _f.strengthMax)) {
textColor = "#c66";
@@ -445,7 +444,7 @@ class PowerLoraLoaderWidget extends RgthreeBaseWidget {
ctx.globalAlpha = app.canvas.editor_alpha;
ctx.restore();
}
serializeValue(serializedNode, widgetIndex) {
serializeValue(node, index) {
var _b;
const v = { ...this.value };
if (!this.showModelAndClip) {
+51 -49
View File
@@ -49,7 +49,7 @@ class RerouteService {
}
async onCanvasSetUpListenerForLinking() {
const canvas = await waitForCanvas();
const canvasProperty = true ? 'connecting_links' : 'connecting_node';
const canvasProperty = true ? "connecting_links" : "connecting_node";
canvas[`_${canvasProperty}`];
const thisService = this;
Object.defineProperty(canvas, canvasProperty, {
@@ -98,9 +98,11 @@ class RerouteService {
}
}
getConnectingData() {
var _a, _b;
var _a, _b, _c, _d;
const oldCanvas = app.canvas;
if (oldCanvas.connecting_node && oldCanvas.connecting_slot != null && ((_a = oldCanvas.connecting_pos) === null || _a === void 0 ? void 0 : _a.length)) {
if (oldCanvas.connecting_node &&
oldCanvas.connecting_slot != null &&
((_a = oldCanvas.connecting_pos) === null || _a === void 0 ? void 0 : _a.length)) {
return {
node: oldCanvas.connecting_node,
input: oldCanvas.connecting_input,
@@ -114,8 +116,8 @@ class RerouteService {
const link = canvas.connecting_links[0];
return {
node: link.node,
input: link.input,
output: link.output,
input: (_c = link.input) !== null && _c !== void 0 ? _c : undefined,
output: (_d = link.output) !== null && _d !== void 0 ? _d : undefined,
slot: link.slot,
pos: [...link.pos],
};
@@ -125,7 +127,9 @@ class RerouteService {
setCanvasConnectingData(ctx) {
var _a, _b;
const oldCanvas = app.canvas;
if (oldCanvas.connecting_node && oldCanvas.connecting_slot != null && ((_a = oldCanvas.connecting_pos) === null || _a === void 0 ? void 0 : _a.length)) {
if (oldCanvas.connecting_node &&
oldCanvas.connecting_slot != null &&
((_a = oldCanvas.connecting_pos) === null || _a === void 0 ? void 0 : _a.length)) {
oldCanvas.connecting_node = ctx.node;
oldCanvas.connecting_input = ctx.input;
oldCanvas.connecting_output = ctx.output;
@@ -200,6 +204,8 @@ class RerouteService {
data.output = entry.node.outputs[0];
data.slot = 0;
data.pos = entry.node.getConnectionPos(false, 0);
data.direction =
layout[0] === "Top" ? 2 : layout[0] === "Bottom" ? 1 : layout[0] === "Left" ? 4 : 3;
}
else {
entry.node.connect(0, data.node, data.slot);
@@ -207,6 +213,8 @@ class RerouteService {
data.input = entry.node.inputs[0];
data.slot = 0;
data.pos = entry.node.getConnectionPos(true, 0);
data.direction =
layout[1] === "Top" ? 2 : layout[1] === "Bottom" ? 1 : layout[1] === "Left" ? 4 : 3;
}
this.setCanvasConnectingData(data);
entry.current = { ...this.connectingData };
@@ -265,7 +273,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
}
onConstructed() {
var _a;
this.setResizable((_a = this.properties["resizable"]) !== null && _a !== void 0 ? _a : configResizable);
this.setResizable(!!((_a = this.properties["resizable"]) !== null && _a !== void 0 ? _a : configResizable));
this.size = RerouteNode.size;
this.addInput("", "*");
this.addOutput("", "*");
@@ -282,7 +290,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
}
super.configure(info);
this.configuring = true;
this.setResizable((_c = this.properties["resizable"]) !== null && _c !== void 0 ? _c : configResizable);
this.setResizable(!!((_c = this.properties["resizable"]) !== null && _c !== void 0 ? _c : configResizable));
this.applyNodeSize();
this.configuring = false;
}
@@ -333,11 +341,11 @@ class RerouteNode extends RgthreeBaseVirtualNode {
ctx.restore();
}
}
findInputSlot(name) {
return 0;
findInputSlot(name, returnObj = false) {
return returnObj ? this.inputs[0] : 0;
}
findOutputSlot(name) {
return 0;
findOutputSlot(name, returnObj) {
return returnObj ? this.outputs[0] : 0;
}
disconnectOutput(slot, targetNode) {
return super.disconnectOutput(slot, targetNode);
@@ -347,7 +355,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
if (rgthree.replacingReroute != null && ((_a = this.inputs[0]) === null || _a === void 0 ? void 0 : _a.link)) {
const graph = app.graph;
const link = graph.links[this.inputs[0].link];
const node = graph.getNodeById(link === null || link === void 0 ? void 0 : link.origin_id);
const node = (link === null || link === void 0 ? void 0 : link.origin_id) != null ? graph.getNodeById(link.origin_id) : null;
if (rgthree.replacingReroute !== (node === null || node === void 0 ? void 0 : node.id)) {
return false;
}
@@ -490,7 +498,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
(_h = (_g = node).applyNodeSize) === null || _h === void 0 ? void 0 : _h.call(_g);
for (const l of node.outputs[0].links || []) {
const link = app.graph.links[l];
if (link) {
if (link && color) {
link.color = color;
}
}
@@ -516,7 +524,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
const links = inputNode.outputs[inputNodeOutputSlot].links;
for (const l of links || []) {
const link = app.graph.links[l];
if (link) {
if (link && color) {
link.color = color;
}
}
@@ -536,8 +544,8 @@ class RerouteNode extends RgthreeBaseVirtualNode {
if (newSize[0] === 10 || newSize[1] === 10) {
const props = this.properties;
props["connections_layout"] = props["connections_layout"] || ["Left", "Right"];
const layout = props["connections_layout"];
props["connections_dir"] = props["connections_dir"] || [-1, -1];
const layout = props["connections_layout"];
const dir = props["connections_dir"];
if (oldSize[0] > 10 && newSize[0] === 10) {
dir[0] = LiteGraph.DOWN;
@@ -590,27 +598,22 @@ class RerouteNode extends RgthreeBaseVirtualNode {
const h = this.size[1];
this.properties["connections_layout"] =
this.properties["connections_layout"] || this.defaultConnectionsLayout;
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(this.properties["connections_layout"][0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(this.properties["connections_layout"][1]);
const connections_layout = this.properties["connections_layout"];
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[1]);
if (degrees == 90 || degrees === -90) {
if (degrees === -90) {
this.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex - 1) % 4) + 4) % 4];
this.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex - 1) % 4) + 4) % 4];
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex - 1) % 4) + 4) % 4];
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex - 1) % 4) + 4) % 4];
}
else {
this.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 1) % 4) + 4) % 4];
this.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 1) % 4) + 4) % 4];
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 1) % 4) + 4) % 4];
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 1) % 4) + 4) % 4];
}
}
else if (degrees === 180) {
this.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
this.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
}
this.setSize([h, w]);
}
@@ -650,13 +653,15 @@ class RerouteNode extends RgthreeBaseVirtualNode {
var _a, _b;
const props = this.properties;
props["connections_layout"] = props["connections_layout"] || ["Left", "Right"];
const connections_layout = this.properties["connections_layout"];
const propIdx = ioDir == IoDirection.INPUT ? 0 : 1;
const oppositeIdx = propIdx ? 0 : 1;
let currentLayout = props["connections_layout"][propIdx];
let oppositeLayout = props["connections_layout"][oppositeIdx];
let currentLayout = connections_layout[propIdx];
let oppositeLayout = connections_layout[oppositeIdx];
if (this.size[0] === 10 || this.size[1] === 10) {
props["connections_dir"] = props["connections_dir"] || [-1, -1];
let currentDir = props["connections_dir"][propIdx];
const connections_dir = this.properties["connections_dir"];
let currentDir = connections_dir[propIdx];
const options = this.size[0] === 10
? currentLayout === "Bottom"
? [LiteGraph.DOWN, LiteGraph.RIGHT, LiteGraph.LEFT]
@@ -666,7 +671,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
: [LiteGraph.LEFT, LiteGraph.UP, LiteGraph.DOWN];
let idx = options.indexOf(currentDir);
let next = (_a = options[idx + 1]) !== null && _a !== void 0 ? _a : options[0];
this.properties["connections_dir"][propIdx] = next;
connections_dir[propIdx] = next;
return;
}
let next = currentLayout;
@@ -674,7 +679,7 @@ class RerouteNode extends RgthreeBaseVirtualNode {
let idx = LAYOUT_CLOCKWISE.indexOf(next);
next = (_b = LAYOUT_CLOCKWISE[idx + 1]) !== null && _b !== void 0 ? _b : LAYOUT_CLOCKWISE[0];
} while (next === oppositeLayout);
this.properties["connections_layout"][propIdx] = next;
connections_layout[propIdx] = next;
this.setDirtyCanvas(true, true);
}
onMouseMove(event) {
@@ -910,26 +915,23 @@ addMenuItem(RerouteNode, app, {
node.rotate(180);
}
else {
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(node.properties["connections_layout"][0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(node.properties["connections_layout"][1]);
const connections_layout = node.properties["connections_layout"];
const inputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[0]);
const outputDirIndex = LAYOUT_CLOCKWISE.indexOf(connections_layout[1]);
if (value === null || value === void 0 ? void 0 : value.startsWith("Flip Horizontally")) {
if (["Left", "Right"].includes(node.properties["connections_layout"][0])) {
node.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
if (["Left", "Right"].includes(connections_layout[0])) {
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
}
if (["Left", "Right"].includes(node.properties["connections_layout"][1])) {
node.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
if (["Left", "Right"].includes(connections_layout[1])) {
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
}
}
else if (value === null || value === void 0 ? void 0 : value.startsWith("Flip Vertically")) {
if (["Top", "Bottom"].includes(node.properties["connections_layout"][0])) {
node.properties["connections_layout"][0] =
LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
if (["Top", "Bottom"].includes(connections_layout[0])) {
connections_layout[0] = LAYOUT_CLOCKWISE[(((inputDirIndex + 2) % 4) + 4) % 4];
}
if (["Top", "Bottom"].includes(node.properties["connections_layout"][1])) {
node.properties["connections_layout"][1] =
LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
if (["Top", "Bottom"].includes(connections_layout[1])) {
connections_layout[1] = LAYOUT_CLOCKWISE[(((outputDirIndex + 2) % 4) + 4) % 4];
}
}
}
+15 -13
View File
@@ -135,7 +135,7 @@ class Rgthree extends EventTarget {
this.processingMouseDown = false;
this.processingMouseUp = false;
this.processingMouseMove = false;
this.lastAdjustedMouseEvent = null;
this.lastCanvasMouseEvent = null;
this.canvasCurrentlyCopyingToClipboard = false;
this.canvasCurrentlyCopyingToClipboardWithMultipleNodes = false;
this.initialGraphToPromptSerializedWorkflowBecauseComfyUIBrokeStuff = null;
@@ -181,11 +181,11 @@ class Rgthree extends EventTarget {
LiteGraph.closeAllContextMenus();
if (e.button == 2) {
const canvas = await waitForCanvas();
new LiteGraph.ContextMenu(this.getRgthreeContextMenuItems(), {
new LiteGraph.ContextMenu(this.getRgthreeIContextMenuValues(), {
title: `<div class="rgthree-contextmenu-item rgthree-contextmenu-title-rgthree-comfy">${logoRgthree} rgthree-comfy</div>`,
left: e.clientX,
top: 5,
}, canvas.getCanvasWindow());
});
return;
}
if (e.button == 0) {
@@ -246,7 +246,7 @@ class Rgthree extends EventTarget {
const adjustMouseEvent = LGraphCanvas.prototype.adjustMouseEvent;
LGraphCanvas.prototype.adjustMouseEvent = function (e) {
adjustMouseEvent.apply(this, [...arguments]);
rgthree.lastAdjustedMouseEvent = e;
rgthree.lastCanvasMouseEvent = e;
};
const copyToClipboard = LGraphCanvas.prototype.copyToClipboard;
LGraphCanvas.prototype.copyToClipboard = function (nodes) {
@@ -311,7 +311,7 @@ class Rgthree extends EventTarget {
content: logoRgthree + `rgthree-comfy`,
className: "rgthree-contextmenu-item rgthree-contextmenu-main-item-rgthree-comfy",
submenu: {
options: that.getRgthreeContextMenuItems(),
options: that.getRgthreeIContextMenuValues(),
},
});
options.push(null);
@@ -334,7 +334,7 @@ class Rgthree extends EventTarget {
};
}, 1016);
}
getRgthreeContextMenuItems() {
getRgthreeIContextMenuValues() {
const [canvas, graph] = [app.canvas, app.graph];
const selectedNodes = Object.values(canvas.selected_nodes || {});
let rerouteNodes = [];
@@ -361,13 +361,15 @@ class Rgthree extends EventTarget {
options: getNodeTypeStrings(),
callback: (value, options, event) => {
const node = LiteGraph.createNode(addRgthree(value));
node.pos = [
rgthree.lastAdjustedMouseEvent.canvasX,
rgthree.lastAdjustedMouseEvent.canvasY,
];
canvas.graph.add(node);
canvas.selectNode(node);
app.graph.setDirtyCanvas(true, true);
if (node) {
node.pos = [
rgthree.lastCanvasMouseEvent.canvasX,
rgthree.lastCanvasMouseEvent.canvasY,
];
canvas.graph.add(node);
canvas.selectNode(node);
app.graph.setDirtyCanvas(true, true);
}
},
extra: { rgthree_doNotNest: true },
},
+8 -7
View File
@@ -44,7 +44,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
}
}
onNodeCreated() {
var _a;
var _a, _b, _c;
(_a = super.onNodeCreated) === null || _a === void 0 ? void 0 : _a.call(this);
for (const [i, w] of this.widgets.entries()) {
if (w.name === "seed") {
@@ -56,17 +56,17 @@ class RgthreeSeed extends RgthreeBaseServerNode {
}
}
let step = this.seedWidget.options.step || 1;
this.randMax = Math.min(1125899906842624, this.seedWidget.options.max);
this.randMin = Math.max(0, this.seedWidget.options.min);
this.randMax = Math.min(1125899906842624, (_b = this.seedWidget.options.max) !== null && _b !== void 0 ? _b : 0);
this.randMin = Math.max(0, (_c = this.seedWidget.options.min) !== null && _c !== void 0 ? _c : 0);
this.randomRange = (this.randMax - Math.max(0, this.randMin)) / (step / 10);
this.addWidget("button", "🎲 Randomize Each Time", null, () => {
this.addWidget("button", "🎲 Randomize Each Time", '', () => {
this.seedWidget.value = SPECIAL_SEED_RANDOM;
}, { serialize: false });
this.addWidget("button", "🎲 New Fixed Random", null, () => {
this.addWidget("button", "🎲 New Fixed Random", '', () => {
this.seedWidget.value =
Math.floor(Math.random() * this.randomRange) * (step / 10) + this.randMin;
}, { serialize: false });
this.lastSeedButton = this.addWidget("button", LAST_SEED_BUTTON_LABEL, null, () => {
this.lastSeedButton = this.addWidget("button", LAST_SEED_BUTTON_LABEL, '', () => {
this.seedWidget.value = this.lastSeed != null ? this.lastSeed : this.seedWidget.value;
this.lastSeedButton.name = LAST_SEED_BUTTON_LABEL;
this.lastSeedButton.disabled = true;
@@ -88,6 +88,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
}
},
});
return options;
}
addLastSeedValue() {
if (this.lastSeedValue)
@@ -141,7 +142,7 @@ class RgthreeSeed extends RgthreeBaseServerNode {
}
}
getSeedToUse() {
const inputSeed = this.seedWidget.value;
const inputSeed = Number(this.seedWidget.value);
let seedToUse = null;
if (SPECIAL_SEEDS.includes(inputSeed)) {
if (typeof this.lastSeed === "number" && !SPECIAL_SEEDS.includes(this.lastSeed)) {
+2 -2
View File
@@ -63,7 +63,7 @@ class FastGroupsService {
if (!this.cachedNodeBoundings) {
this.cachedNodeBoundings = {};
for (const node of app.graph._nodes) {
this.cachedNodeBoundings[node.id] = node.getBounding();
this.cachedNodeBoundings[Number(node.id)] = node.getBounding();
}
setTimeout(() => {
this.cachedNodeBoundings = null;
@@ -76,7 +76,7 @@ class FastGroupsService {
const nodes = group.graph._nodes;
group._nodes.length = 0;
for (const node of nodes) {
const node_bounding = cachedBoundings[node.id];
const node_bounding = cachedBoundings[Number(node.id)];
if (!node_bounding || !LiteGraph.overlapBounding(group._bounding, node_bounding)) {
continue;
}
+4 -3
View File
@@ -130,7 +130,7 @@ export function addConnectionLayoutSupport(node, app, options = [
subMenuOptions: options.map((option) => option[0] + (option[1] ? " -> " + option[1] : "")),
prepareValue: (value, node) => {
var _a;
const values = value.split(" -> ");
const values = String(value).split(" -> ");
if (!values[1] && !((_a = node.outputs) === null || _a === void 0 ? void 0 : _a.length)) {
values[1] = LAYOUT_LABEL_OPPOSITES[values[0]];
}
@@ -209,8 +209,9 @@ export function getConnectionPosForLayout(node, isInput, slotNumber, out) {
return count;
}, 0);
cxn.dir = data[0];
if ((node.size[0] == 10 || node.size[1] == 10) && node.properties["connections_dir"]) {
cxn.dir = node.properties["connections_dir"][isInput ? 0 : 1];
const connections_dir = node.properties["connections_dir"];
if ((node.size[0] == 10 || node.size[1] == 10) && connections_dir) {
cxn.dir = connections_dir[isInput ? 0 : 1];
}
if (side === "Left") {
if (node.flags.collapsed) {
+1 -1
View File
@@ -11,7 +11,7 @@ export async function showLoraChooser(event, callback, parentMenu, loras) {
}
new LiteGraph.ContextMenu(loras, {
event: event,
parentMenu,
parentMenu: parentMenu != null ? parentMenu : undefined,
title: "Choose a lora",
scale: Math.max(1, (_b = (_a = canvas.ds) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : 1),
className: "dark",
+28 -16
View File
@@ -20,6 +20,9 @@ export function drawLabelAndValue(ctx, label, value, width, posY, height, option
}
export class RgthreeBaseWidget {
constructor(name) {
this.type = "custom";
this.options = {};
this.y = 0;
this.last_y = 0;
this.mouseDowned = null;
this.isMouseDownedAndOver = false;
@@ -27,6 +30,9 @@ export class RgthreeBaseWidget {
this.downedHitAreasForMove = [];
this.name = name;
}
serializeValue(node, index) {
return this.value;
}
clickWasWithinBounds(pos, bounds) {
let xStart = bounds[0];
let xEnd = xStart + (bounds.length > 2 ? bounds[2] : bounds[1]);
@@ -115,8 +121,9 @@ export class RgthreeBetterButtonWidget extends RgthreeBaseWidget {
return this.mouseUpCallback(event, pos, node);
}
}
export class RgthreeBetterTextWidget {
export class RgthreeBetterTextWidget extends RgthreeBaseWidget {
constructor(name, value) {
super(name);
this.name = name;
this.value = value;
}
@@ -135,11 +142,11 @@ export class RgthreeBetterTextWidget {
return false;
}
}
export class RgthreeDividerWidget {
export class RgthreeDividerWidget extends RgthreeBaseWidget {
constructor(widgetOptions) {
super("divider");
this.options = { serialize: false };
this.value = null;
this.name = "divider";
this.value = '';
this.widgetOptions = {
marginTop: 7,
marginBottom: 7,
@@ -166,13 +173,13 @@ export class RgthreeDividerWidget {
];
}
}
export class RgthreeLabelWidget {
export class RgthreeLabelWidget extends RgthreeBaseWidget {
constructor(name, widgetOptions) {
super(name);
this.options = { serialize: false };
this.value = null;
this.value = '';
this.widgetOptions = {};
this.posY = 0;
this.name = name;
Object.assign(this.widgetOptions, widgetOptions);
}
draw(ctx, node, width, posY, height) {
@@ -223,18 +230,23 @@ export class RgthreeLabelWidget {
return true;
}
}
export class RgthreeInvisibleWidget {
export class RgthreeInvisibleWidget extends RgthreeBaseWidget {
constructor(name, type, value, serializeValueFn) {
this.serializeValue = undefined;
this.name = name;
this.type = type;
super(name);
this.value = value;
if (serializeValueFn) {
this.serializeValue = serializeValueFn;
}
this.serializeValueFn = serializeValueFn;
}
draw() {
return;
}
computeSize(width) {
return [0, 0];
}
serializeValue(node, index) {
return this.serializeValueFn != null
? this.serializeValueFn(node, index)
: super.serializeValue(node, index);
}
draw() { return; }
computeSize(width) { return [0, 0]; }
}
export function drawWidgetButton(drawCtx, text, isMouseDownedAndOver = false) {
if (!isLowQuality() && !isMouseDownedAndOver) {
+1 -1
View File
@@ -8,7 +8,7 @@ function getNodeById(graph, id) {
return graph.getNodeById(id);
}
graph = graph;
return graph.nodes.find((n) => n.id === id);
return graph.nodes.find((node) => Number(node.id) === id);
}
function extendLink(link) {
return {