Pull in actual litegraph.js types, replacing rgthree-comfy version.
This commit is contained in:
Generated
+7
-1
@@ -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
@@ -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,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
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 [
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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,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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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,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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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 = {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,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 = (
|
||||
|
||||
Vendored
+3
-17
@@ -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 {
|
||||
|
||||
Vendored
+69
-1730
File diff suppressed because it is too large
Load Diff
Vendored
+3
-2
@@ -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> {
|
||||
|
||||
@@ -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/*"],
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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])) {
|
||||
|
||||
@@ -70,6 +70,7 @@ export class Bookmark extends RgthreeBaseVirtualNode {
|
||||
input.value = Object.keys(KEY_EVENT_SERVICE.downKeys).join(" + ");
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
canvasToBookmark() {
|
||||
var _a, _b;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) `,
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -123,7 +123,7 @@ class NodeModeRelay extends BaseCollectorNode {
|
||||
mode = MODE_ALWAYS;
|
||||
}
|
||||
else {
|
||||
mode = null;
|
||||
mode = undefined;
|
||||
}
|
||||
}
|
||||
this.dispatchModeToRepeater(mode);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user