Typescript conversion 0.7.84 (#194)

* Convert litegraph.js to TS

* Overhaul static litegraph.d.ts with updated types

* Fix rename oversights and revert fix unused param

- Some functions were broken by merging updated TS function signatures which included param renames
- Removal of unused params does not belong in the TS conversion PR, and has been reverted

* Remove legacy static .d.ts file

* Add callback decl from #180

Support allowing links to widgets (#180)
c23e610c11

* Convert NodeId to string | number

Results in significantly less downstream changes, despite being a change from the old static file.

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
This commit is contained in:
Chenlei Hu
2024-10-07 11:15:29 -04:00
committed by GitHub
parent 36a8b1fea0
commit 142c22ea41
18 changed files with 1703 additions and 2322 deletions

1646
public/litegraph.d.ts vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
// @ts-nocheck
import type { IContextMenuOptions, IContextMenuValue } from "./interfaces"
import { LiteGraph } from "./litegraph";
/* LiteGraph GUI elements used for canvas editing *************************************/
@@ -15,7 +16,13 @@ import { LiteGraph } from "./litegraph";
* - event: you can pass a MouseEvent, this way the ContextMenu appears in that position
*/
export class ContextMenu {
constructor(values, options) {
options?: IContextMenuOptions
parentMenu?: ContextMenu
root: HTMLDivElement
current_submenu?: ContextMenu
lock?: boolean
constructor(values: IContextMenuValue[] | string[], options: IContextMenuOptions) {
options = options || {};
this.options = options;
var that = this;
@@ -206,7 +213,7 @@ export class ContextMenu {
}
}
addItem(name, value, options) {
addItem(name: string, value: IContextMenuValue, options: IContextMenuOptions): HTMLElement {
var that = this;
options = options || {};
@@ -347,7 +354,7 @@ export class ContextMenu {
return element;
}
close(e, ignore_parent_menu) {
close(e?: MouseEvent, ignore_parent_menu?: boolean): void {
if (this.root.parentNode) {
this.root.parentNode.removeChild(this.root);
}
@@ -374,7 +381,7 @@ export class ContextMenu {
}
//this code is used to trigger events easily (used in the context menu mouseleave
static trigger(element, event_name, params, origin) {
static trigger(element: HTMLDivElement, event_name: string, params: MouseEvent, origin?: undefined) {
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail
evt.srcElement = origin;
@@ -388,21 +395,21 @@ export class ContextMenu {
}
//returns the top most menu
getTopMenu() {
getTopMenu(): ContextMenu {
if (this.options.parentMenu) {
return this.options.parentMenu.getTopMenu();
}
return this;
}
getFirstEvent() {
getFirstEvent(): MouseEvent {
if (this.options.parentMenu) {
return this.options.parentMenu.getFirstEvent();
}
return this.options.event;
}
static isCursorOverElement(event, element) {
static isCursorOverElement(event: MouseEvent, element: HTMLDivElement): boolean {
var left = event.clientX;
var top = event.clientY;
var rect = element.getBoundingClientRect();

View File

@@ -1,11 +1,27 @@
// @ts-nocheck
import type { Point, Rect, Rect32 } from "./interfaces"
import { LiteGraph } from "./litegraph";
//****************************************
//Scale and Offset
export class DragAndScale {
constructor(element, skip_events) {
max_scale: number
min_scale: number
offset: Point
scale: number
enabled: boolean
last_mouse: Point
element?: HTMLCanvasElement
visible_area: Rect32
_binded_mouse_callback
dragging?: boolean
viewport?: Rect
onredraw?(das: DragAndScale): void
/** @deprecated */
onmouse?(e: any): boolean
constructor(element?: HTMLCanvasElement, skip_events?: boolean) {
this.offset = new Float32Array([0, 0]);
this.scale = 1;
this.max_scale = 10;
@@ -41,7 +57,7 @@ export class DragAndScale {
element.addEventListener("wheel", this._binded_mouse_callback, false);
}
computeVisibleArea(viewport) {
computeVisibleArea(viewport: Rect): void {
if (!this.element) {
this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0;
return;
@@ -134,12 +150,12 @@ export class DragAndScale {
}
}
toCanvasContext(ctx) {
toCanvasContext(ctx: CanvasRenderingContext2D): void {
ctx.scale(this.scale, this.scale);
ctx.translate(this.offset[0], this.offset[1]);
}
convertOffsetToCanvas(pos) {
convertOffsetToCanvas(pos: Point): Point {
//return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]];
return [
(pos[0] + this.offset[0]) * this.scale,
@@ -147,7 +163,7 @@ export class DragAndScale {
];
}
convertCanvasToOffset(pos, out) {
convertCanvasToOffset(pos: Point, out?: Point): Point {
out = out || [0, 0];
out[0] = pos[0] / this.scale - this.offset[0];
out[1] = pos[1] / this.scale - this.offset[1];
@@ -163,7 +179,7 @@ export class DragAndScale {
}
}
changeScale(value, zooming_center) {
changeScale(value: number, zooming_center?: Point): void {
if (value < this.min_scale) {
value = this.min_scale;
} else if (value > this.max_scale) {
@@ -207,11 +223,11 @@ export class DragAndScale {
}
}
changeDeltaScale(value, zooming_center) {
changeDeltaScale(value: number, zooming_center?: Point) {
this.changeScale(this.scale * value, zooming_center);
}
reset() {
reset(): void {
this.scale = 1;
this.offset[0] = 0;
this.offset[1] = 0;

View File

@@ -1,13 +1,21 @@
// @ts-nocheck
import type { Dictionary, IContextMenuValue, ISlotType, MethodNames, Point } from "./interfaces"
import type { ISerialisedGraph } from "@/types/serialisation"
import type { LGraphEventMode } from "./types/globalEnums"
import { LiteGraph } from "./litegraph";
import { LGraphCanvas } from "./LGraphCanvas";
import { LGraphGroup } from "./LGraphGroup";
import { LGraphNode } from "./LGraphNode";
import { LLink } from "./LLink";
import { type NodeId, LGraphNode } from "./LGraphNode";
import { type LinkId, LLink } from "./LLink";
interface IGraphInput {
name: string
type: string
value?: unknown
}
type ParamsArray<T extends Record<any, any>, K extends MethodNames<T>> = Parameters<T[K]>[1] extends undefined ? Parameters<T[K]> | Parameters<T[K]>[0] : Parameters<T[K]>
//*********************************************************************************
// LGraph CLASS
//*********************************************************************************
/**
* LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.
* supported callbacks:
@@ -26,7 +34,71 @@ export class LGraph {
static STATUS_STOPPED = 1;
static STATUS_RUNNING = 2;
constructor(o) {
_version: number
links: Record<LinkId, LLink>
list_of_graphcanvas?: LGraphCanvas[]
status: number
last_node_id: number
last_link_id: number
/** The largest ID created by this graph */
last_reroute_id: number
_nodes: LGraphNode[]
_nodes_by_id: Record<NodeId, LGraphNode>
_nodes_in_order: LGraphNode[]
_nodes_executable: LGraphNode[]
_groups: LGraphGroup[]
iteration: number
globaltime: number
runningtime: number
fixedtime: number
fixedtime_lapse: number
elapsed_time: number
last_update_time: number
starttime: number
catch_errors: boolean
execution_timer_id: number
errors_in_execution: boolean
execution_time: number
_last_trigger_time?: number
filter?: string
_subgraph_node?: LGraphNode
config: { align_to_grid?: any; links_ontop?: any }
vars: Dictionary<unknown>
nodes_executing: boolean[]
nodes_actioning: (string | boolean)[]
nodes_executedAction: string[]
extra: Record<any, any>
inputs: Dictionary<IGraphInput>
outputs: Dictionary<IGraphInput>
onInputsOutputsChange?(): void
onInputAdded?(name: string, type: string): void
onAfterStep?(): void
onBeforeStep?(): void
onPlayEvent?(): void
onStopEvent?(): void
onAfterExecute?(): void
onExecuteStep?(): void
onNodeAdded?(node: LGraphNode): void
onNodeRemoved?(node: LGraphNode): void
onTrigger?(action: string, param: unknown): void
onInputRenamed?(old_name: string, name: string): void
onInputTypeChanged?(name: string, type: string): void
onInputRemoved?(name: string): void
onOutputAdded?(name: string, type: string): void
onOutputRenamed?(old_name: string, name: string): void
onOutputTypeChanged?(name: string, type: string): void
onOutputRemoved?(name: string): void
onBeforeChange?(graph: LGraph, info: LGraphNode): void
onAfterChange?(graph: LGraph, info: LGraphNode): void
onConnectionChange?(node: LGraphNode): void
on_change?(graph: LGraph): void
onSerialize?(data: ISerialisedGraph): void
onConfigure?(data: ISerialisedGraph): void
onGetNodeMenuOptions?(options: IContextMenuValue[], node: LGraphNode): void
private _input_nodes?: LGraphNode[]
constructor(o?: ISerialisedGraph) {
if (LiteGraph.debug) {
console.log("Graph created");
}
@@ -38,14 +110,14 @@ export class LGraph {
}
}
//used to know which types of connections support this graph (some graphs do not allow certain types)
getSupportedTypes() {
getSupportedTypes(): string[] {
return this.supported_types || LGraph.supported_types;
}
/**
* Removes all nodes from this graph
* @method clear
*/
clear() {
clear(): void {
this.stop();
this.status = LGraph.STATUS_STOPPED;
@@ -130,7 +202,7 @@ export class LGraph {
* @method attachCanvas
* @param {GraphCanvas} graph_canvas
*/
attachCanvas(graphcanvas) {
attachCanvas(graphcanvas: LGraphCanvas): void {
if (graphcanvas.constructor != LGraphCanvas) {
throw "attachCanvas expects a LGraphCanvas instance";
}
@@ -150,7 +222,7 @@ export class LGraph {
* @method detachCanvas
* @param {GraphCanvas} graph_canvas
*/
detachCanvas(graphcanvas) {
detachCanvas(graphcanvas: LGraphCanvas): void {
if (!this.list_of_graphcanvas) {
return;
}
@@ -167,7 +239,7 @@ export class LGraph {
* @method start
* @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate
*/
start(interval) {
start(interval?: number): void {
if (this.status == LGraph.STATUS_RUNNING) {
return;
}
@@ -215,7 +287,7 @@ export class LGraph {
* Stops the execution loop of the graph
* @method stop execution
*/
stop() {
stop(): void {
if (this.status == LGraph.STATUS_STOPPED) {
return;
}
@@ -242,7 +314,7 @@ export class LGraph {
* @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors
* @param {number} limit max number of nodes to execute (used to execute from start to a node)
*/
runStep(num, do_not_catch_errors, limit) {
runStep(num: number, do_not_catch_errors: boolean, limit?: number): void {
num = num || 1;
var start = LiteGraph.getTime();
@@ -329,7 +401,7 @@ export class LGraph {
* nodes with only inputs.
* @method updateExecutionOrder
*/
updateExecutionOrder() {
updateExecutionOrder(): void {
this._nodes_in_order = this.computeExecutionOrder(false);
this._nodes_executable = [];
for (var i = 0; i < this._nodes_in_order.length; ++i) {
@@ -339,8 +411,7 @@ export class LGraph {
}
}
//This is more internal, it computes the executable nodes in order and returns it
computeExecutionOrder(only_onExecute,
set_level) {
computeExecutionOrder(only_onExecute: boolean, set_level?: boolean): LGraphNode[] {
var L = [];
var S = [];
var M = {};
@@ -480,7 +551,7 @@ export class LGraph {
* @method getAncestors
* @return {Array} an array with all the LGraphNodes that affect this node, in order of execution
*/
getAncestors(node) {
getAncestors(node: LGraphNode): LGraphNode[] {
var ancestors = [];
var pending = [node];
var visited = {};
@@ -512,7 +583,7 @@ export class LGraph {
* Positions every node in a more readable manner
* @method arrange
*/
arrange(margin, layout) {
arrange(margin?: number, layout?: string): void {
margin = margin || 100;
const nodes = this.computeExecutionOrder(false, true);
@@ -556,7 +627,7 @@ export class LGraph {
* @method getTime
* @return {number} number of milliseconds the graph has been running
*/
getTime() {
getTime(): number {
return this.globaltime;
}
/**
@@ -564,7 +635,7 @@ export class LGraph {
* @method getFixedTime
* @return {number} number of milliseconds the graph has been running
*/
getFixedTime() {
getFixedTime(): number {
return this.fixedtime;
}
/**
@@ -573,7 +644,7 @@ export class LGraph {
* @method getElapsedTime
* @return {number} number of milliseconds it took the last cycle
*/
getElapsedTime() {
getElapsedTime(): number {
return this.elapsed_time;
}
/**
@@ -582,7 +653,7 @@ export class LGraph {
* @param {String} eventname the name of the event (function to be called)
* @param {Array} params parameters in array format
*/
sendEventToAllNodes(eventname, params, mode) {
sendEventToAllNodes(eventname: string, params?: object | object[], mode?: LGraphEventMode): void {
mode = mode || LiteGraph.ALWAYS;
var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes;
@@ -613,7 +684,7 @@ export class LGraph {
}
}
}
sendActionToCanvas(action, params) {
sendActionToCanvas<T extends MethodNames<LGraphCanvas>>(action: T, params?: ParamsArray<LGraphCanvas, T>): void {
if (!this.list_of_graphcanvas) {
return;
}
@@ -630,7 +701,7 @@ export class LGraph {
* @method add
* @param {LGraphNode} node the instance of the node
*/
add(node, skip_compute_order) {
add(node: LGraphNode | LGraphGroup, skip_compute_order?: boolean): LGraphNode | null {
if (!node) {
return;
}
@@ -707,7 +778,7 @@ export class LGraph {
* @method remove
* @param {LGraphNode} node the instance of the node
*/
remove(node) {
remove(node: LGraphNode | LGraphGroup): void {
if (node.constructor === LiteGraph.LGraphGroup) {
var index = this._groups.indexOf(node);
if (index != -1) {
@@ -799,7 +870,7 @@ export class LGraph {
* @method getNodeById
* @param {Number} id
*/
getNodeById(id) {
getNodeById(id: NodeId): LGraphNode | null {
if (id == null) {
return null;
}
@@ -811,7 +882,7 @@ export class LGraph {
* @param {Class} classObject the class itself (not an string)
* @return {Array} a list with all the nodes of this type
*/
findNodesByClass(classObject, result) {
findNodesByClass(classObject: Function, result: LGraphNode[]): LGraphNode[] {
result = result || [];
result.length = 0;
for (var i = 0, l = this._nodes.length; i < l; ++i) {
@@ -827,7 +898,7 @@ export class LGraph {
* @param {String} type the name of the node type
* @return {Array} a list with all the nodes of this type
*/
findNodesByType(type, result) {
findNodesByType(type: string, result: LGraphNode[]): LGraphNode[] {
var type = type.toLowerCase();
result = result || [];
result.length = 0;
@@ -844,7 +915,7 @@ export class LGraph {
* @param {String} name the name of the node to search
* @return {Node} the node or null
*/
findNodeByTitle(title) {
findNodeByTitle(title: string): LGraphNode {
for (var i = 0, l = this._nodes.length; i < l; ++i) {
if (this._nodes[i].title == title) {
return this._nodes[i];
@@ -858,7 +929,7 @@ export class LGraph {
* @param {String} name the name of the node to search
* @return {Array} a list with all the nodes with this name
*/
findNodesByTitle(title) {
findNodesByTitle(title: string): LGraphNode[] {
var result = [];
for (var i = 0, l = this._nodes.length; i < l; ++i) {
if (this._nodes[i].title == title) {
@@ -875,7 +946,7 @@ export class LGraph {
* @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph
* @return {LGraphNode} the node at this position or null
*/
getNodeOnPos(x, y, nodes_list, margin) {
getNodeOnPos(x: number, y: number, nodes_list?: LGraphNode[], margin?: number): LGraphNode {
nodes_list = nodes_list || this._nodes;
var nRet = null;
for (var i = nodes_list.length - 1; i >= 0; i--) {
@@ -899,7 +970,7 @@ export class LGraph {
* @param {number} y the y coordinate in canvas space
* @return {LGraphGroup | null} the group or null
*/
getGroupOnPos(x, y, { margin = 2 } = {}) {
getGroupOnPos(x: number, y: number, { margin = 2 } = {}) {
return this._groups.reverse().find(g => g.isPointInside(x, y, margin, /* skip_title */ true));
}
@@ -933,7 +1004,7 @@ export class LGraph {
this.updateExecutionOrder();
}
// ********** GLOBALS *****************
onAction(action, param, options) {
onAction(action: string, param: unknown, options: { action_call?: string }): void {
this._input_nodes = this.findNodesByClass(
LiteGraph.GraphInput,
this._input_nodes
@@ -948,7 +1019,7 @@ export class LGraph {
break;
}
}
trigger(action, param) {
trigger(action: string, param: unknown) {
if (this.onTrigger) {
this.onTrigger(action, param);
}
@@ -960,7 +1031,7 @@ export class LGraph {
* @param {String} type
* @param {*} value [optional]
*/
addInput(name, type, value) {
addInput(name: string, type: string, value?: unknown): void {
var input = this.inputs[name];
if (input) {
//already exist
@@ -986,7 +1057,7 @@ export class LGraph {
* @param {String} name
* @param {*} data
*/
setInputData(name, data) {
setInputData(name: string, data: unknown): void {
var input = this.inputs[name];
if (!input) {
return;
@@ -999,7 +1070,7 @@ export class LGraph {
* @param {String} name
* @return {*} the data
*/
getInputData(name) {
getInputData(name: string): unknown {
var input = this.inputs[name];
if (!input) {
return null;
@@ -1012,7 +1083,7 @@ export class LGraph {
* @param {String} old_name
* @param {String} new_name
*/
renameInput(old_name, name) {
renameInput(old_name: string, name: string): boolean {
if (name == old_name) {
return;
}
@@ -1044,7 +1115,7 @@ export class LGraph {
* @param {String} name
* @param {String} type
*/
changeInputType(name, type) {
changeInputType(name: string, type: string): boolean {
if (!this.inputs[name]) {
return false;
}
@@ -1067,7 +1138,7 @@ export class LGraph {
* @param {String} name
* @param {String} type
*/
removeInput(name) {
removeInput(name: string): boolean {
if (!this.inputs[name]) {
return false;
}
@@ -1091,7 +1162,7 @@ export class LGraph {
* @param {String} type
* @param {*} value
*/
addOutput(name, type, value) {
addOutput(name: string, type: string, value: unknown): void {
this.outputs[name] = { name: name, type: type, value: value };
this._version++;
@@ -1109,7 +1180,7 @@ export class LGraph {
* @param {String} name
* @param {String} value
*/
setOutputData(name, value) {
setOutputData(name: string, value: unknown): void {
var output = this.outputs[name];
if (!output) {
return;
@@ -1122,7 +1193,7 @@ export class LGraph {
* @param {String} name
* @return {*} the data
*/
getOutputData(name) {
getOutputData(name: string): unknown {
var output = this.outputs[name];
if (!output) {
return null;
@@ -1135,7 +1206,7 @@ export class LGraph {
* @param {String} old_name
* @param {String} new_name
*/
renameOutput(old_name, name) {
renameOutput(old_name: string, name: string): boolean {
if (!this.outputs[old_name]) {
return false;
}
@@ -1163,7 +1234,7 @@ export class LGraph {
* @param {String} name
* @param {String} type
*/
changeOutputType(name, type) {
changeOutputType(name: string, type: string): boolean {
if (!this.outputs[name]) {
return false;
}
@@ -1185,7 +1256,7 @@ export class LGraph {
* @method removeOutput
* @param {String} name
*/
removeOutput(name) {
removeOutput(name: string): boolean {
if (!this.outputs[name]) {
return false;
}
@@ -1201,33 +1272,33 @@ export class LGraph {
}
return true;
}
triggerInput(name, value) {
triggerInput(name: string, value: any): void {
var nodes = this.findNodesByTitle(name);
for (var i = 0; i < nodes.length; ++i) {
nodes[i].onTrigger(value);
}
}
setCallback(name, func) {
setCallback(name: string, func: any): void {
var nodes = this.findNodesByTitle(name);
for (var i = 0; i < nodes.length; ++i) {
nodes[i].setTrigger(func);
}
}
//used for undo, called before any change is made to the graph
beforeChange(info) {
beforeChange(info?: LGraphNode): void {
if (this.onBeforeChange) {
this.onBeforeChange(this, info);
}
this.sendActionToCanvas("onBeforeChange", this);
}
//used to resend actions, called after any change is made to the graph
afterChange(info) {
afterChange(info?: LGraphNode): void {
if (this.onAfterChange) {
this.onAfterChange(this, info);
}
this.sendActionToCanvas("onAfterChange", this);
}
connectionChange(node, link_info) {
connectionChange(node: LGraphNode, link_info): void {
this.updateExecutionOrder();
if (this.onConnectionChange) {
this.onConnectionChange(node);
@@ -1239,7 +1310,7 @@ export class LGraph {
* returns if the graph is in live mode
* @method isLive
*/
isLive() {
isLive(): boolean {
if (!this.list_of_graphcanvas) {
return false;
}
@@ -1256,7 +1327,7 @@ export class LGraph {
* clears the triggered slot animation in all links (stop visual animation)
* @method clearTriggeredSlots
*/
clearTriggeredSlots() {
clearTriggeredSlots(): void {
for (var i in this.links) {
var link_info = this.links[i];
if (!link_info) {
@@ -1268,7 +1339,7 @@ export class LGraph {
}
}
/* Called when something visually changed (not the graph!) */
change() {
change(): void {
if (LiteGraph.debug) {
console.log("Graph changed");
}
@@ -1277,7 +1348,7 @@ export class LGraph {
this.on_change(this);
}
}
setDirtyCanvas(fg, bg) {
setDirtyCanvas(fg: boolean, bg?: boolean): void {
this.sendActionToCanvas("setDirty", [fg, bg]);
}
/**
@@ -1285,7 +1356,7 @@ export class LGraph {
* @method removeLink
* @param {Number} link_id
*/
removeLink(link_id) {
removeLink(link_id: LinkId): void {
var link = this.links[link_id];
if (!link) {
return;
@@ -1301,7 +1372,7 @@ export class LGraph {
* @method serialize
* @return {Object} value of the node
*/
serialize(option = { sortNodes: false }) {
serialize(option?: { sortNodes: boolean }): ISerialisedGraph {
var nodes_info = [];
nodes_info = (
option?.sortNodes ?
@@ -1357,7 +1428,7 @@ export class LGraph {
* @param {String} str configure a graph from a JSON string
* @param {Boolean} returns if there was any error parsing
*/
configure(data, keep_old) {
configure(data: ISerialisedGraph, keep_old?: boolean): boolean {
if (!data) {
return;
}
@@ -1451,7 +1522,7 @@ export class LGraph {
this.setDirtyCanvas(true, true);
return error;
}
load(url, callback) {
load(url: string | Blob | URL | File, callback: () => void) {
var that = this;
//from file
@@ -1486,7 +1557,7 @@ export class LGraph {
console.error("Error loading graph:", err);
};
}
onNodeTrace(node, msg, color) {
onNodeTrace(node?: LGraphNode, msg?: string, color?) {
//TODO
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,40 @@
// @ts-nocheck
import type { Point, Size } from "./interfaces"
import type { LGraph } from "./LGraph"
import { LiteGraph } from "./litegraph";
import { LGraphCanvas } from "./LGraphCanvas";
import { overlapBounding } from "./LiteGraphGlobal";
import { LGraphNode } from "./LGraphNode";
export interface IGraphGroup {
_pos: Point
_size: Size
title: string
}
export interface IGraphGroupFlags extends Record<string, unknown> {
pinned?: true
}
export class LGraphGroup {
pos: Point
color: string
title: string
font?: string
font_size: number
_bounding: Float32Array
_pos: Point
_size: Size
_nodes: LGraphNode[]
graph?: LGraph
flags: IGraphGroupFlags
size?: Size
constructor(title) {
constructor(title?: string) {
this._ctor(title);
}
_ctor(title) {
_ctor(title?: string) {
this.title = title || "Group";
this.font_size = LiteGraph.DEFAULT_GROUP_FONT || 24;
this.color = LGraphCanvas.node_colors.pale_blue

View File

@@ -1,9 +1,33 @@
// @ts-nocheck
import { BadgePosition } from "./LGraphBadge";
import type { Dictionary, IContextMenuValue, IFoundSlot, INodeFlags, INodeInputSlot, INodeOutputSlot, IOptionalInputsData, ISlotType, Point, Rect, Size } from "./interfaces"
import type { LGraph } from "./LGraph"
import type { IWidget } from "./types/widgets"
import type { ISerialisedNode } from "./types/serialisation"
import type { RenderShape } from "./types/globalEnums"
import type { LGraphCanvas } from "./LGraphCanvas"
import type { CanvasMouseEvent } from "./types/events"
import { BadgePosition, LGraphBadge } from "./LGraphBadge";
import { LiteGraph } from "./litegraph";
import { isInsideRectangle } from "./LiteGraphGlobal";
import { LLink } from "./LLink";
export type NodeId = number | string
export interface INodePropertyInfo {
name: string
type: string
default_value: unknown
}
export type INodeProperties = Dictionary<unknown> & {
horizontal?: boolean
}
interface IMouseOverData {
inputId: number
outputId: number
}
// *************************************************************
// Node CLASS *******
// *************************************************************
@@ -65,12 +89,151 @@ supported callbacks:
*/
export class LGraphNode {
// Static properties used by dynamic child classes
static title?: string
static MAX_CONSOLE?: number
static type?: string
static category?: string
static supported_extensions?: string[]
static filter?: string
static skip_list?: boolean
constructor(title) {
title?: string
graph: LGraph
id?: NodeId
type?: string
inputs: INodeInputSlot[]
outputs: INodeOutputSlot[]
// Not used
connections: unknown[]
properties: INodeProperties
properties_info: INodePropertyInfo[]
flags?: INodeFlags
widgets?: IWidget[]
size: Point
pos: Point
_pos: Point
locked?: boolean
// Execution order, automatically computed during run
order?: number
mode: number
last_serialization?: ISerialisedNode
serialize_widgets?: boolean
color: string
bgcolor: string
boxcolor: string
shape?: Rect
exec_version: number
action_call?: string
execute_triggered: number
action_triggered: number
widgets_up?: boolean
widgets_start_y?: number
lostFocusAt?: number
gotFocusAt?: number
badges: (LGraphBadge | (() => LGraphBadge))[]
badgePosition: BadgePosition
onOutputRemoved?(this: LGraphNode, slot: number): void
onInputRemoved?(this: LGraphNode, slot: number, input: INodeInputSlot): void
_collapsed_width: number
onBounding?(this: LGraphNode, out: Rect): void
horizontal?: boolean
console?: string[]
_level: number
_shape?: RenderShape
subgraph?: LGraph
skip_subgraph_button?: boolean
mouseOver?: IMouseOverData
is_selected?: boolean
redraw_on_mouse?: boolean
// Appears unused
optional_inputs?
// Appears unused
optional_outputs?
resizable?: boolean
clonable?: boolean
_relative_id?: number
clip_area?: boolean
ignore_remove?: boolean
has_errors?: boolean
removable?: boolean
block_delete?: boolean
// Used in group node
setInnerNodes?(this: LGraphNode, nodes: LGraphNode[]): void
onConnectInput?(this: LGraphNode, target_slot: number, type: unknown, output: INodeOutputSlot, node: LGraphNode, slot: number): boolean
onConnectOutput?(this: LGraphNode, slot: number, type: unknown, input: INodeInputSlot, target_node: number | LGraphNode, target_slot: number): boolean
onResize?(this: LGraphNode, size: Size): void
onPropertyChanged?(this: LGraphNode, name: string, value: unknown, prev_value?: unknown): boolean
onConnectionsChange?(this: LGraphNode, type: ISlotType, index: number, isConnected: boolean, link_info: LLink, inputOrOutput: INodeInputSlot | INodeOutputSlot): void
onInputAdded?(this: LGraphNode, input: INodeInputSlot): void
onOutputAdded?(this: LGraphNode, output: INodeOutputSlot): void
onConfigure?(this: LGraphNode, serialisedNode: ISerialisedNode): void
onSerialize?(this: LGraphNode, serialised: ISerialisedNode): any
onExecute?(this: LGraphNode, param?: unknown, options?: { action_call?: any }): void
onAction?(this: LGraphNode, action: string, param: unknown, options: { action_call?: string }): void
onDrawBackground?(this: LGraphNode, ctx: CanvasRenderingContext2D, canvas: LGraphCanvas, canvasElement: HTMLCanvasElement, mousePosition: Point): void
onNodeCreated?(this: LGraphNode): void
/**
* Callback invoked by {@link connect} to override the target slot index. Its return value overrides the target index selection.
* @param target_slot The current input slot index
* @param requested_slot The originally requested slot index - could be negative, or if using (deprecated) name search, a string
* @returns {number | null} If a number is returned, the connection will be made to that input index.
* If an invalid index or non-number (false, null, NaN etc) is returned, the connection will be cancelled.
*/
onBeforeConnectInput?(this: LGraphNode, target_slot: number, requested_slot: number | string): number | false | null
onShowCustomPanelInfo?(this: LGraphNode, panel: any): void
onAddPropertyToPanel?(this: LGraphNode, pName: string, panel: any): boolean
onWidgetChanged?(this: LGraphNode, name: string, value: unknown, old_value: unknown, w: IWidget): void
onDeselected?(this: LGraphNode): void
onKeyUp?(this: LGraphNode, e: KeyboardEvent): void
onKeyDown?(this: LGraphNode, e: KeyboardEvent): void
onSelected?(this: LGraphNode): void
getExtraMenuOptions?(this: LGraphNode, canvas: LGraphCanvas, options: IContextMenuValue[]): IContextMenuValue[]
getMenuOptions?(this: LGraphNode, canvas: LGraphCanvas): IContextMenuValue[]
onAdded?(this: LGraphNode, graph: LGraph): void
onDrawCollapsed?(this: LGraphNode, ctx: CanvasRenderingContext2D, cavnas: LGraphCanvas): boolean
onDrawForeground?(this: LGraphNode, ctx: CanvasRenderingContext2D, canvas: LGraphCanvas, canvasElement: HTMLCanvasElement): void
onMouseLeave?(this: LGraphNode, e: CanvasMouseEvent): void
getSlotMenuOptions?(this: LGraphNode, slot: any): IOptionalInputsData[]
// FIXME: Re-typing
onDropItem?(this: LGraphNode, event: Event): boolean
onDropData?(this: LGraphNode, data: string | ArrayBuffer, filename: any, file: any): void
onDropFile?(this: LGraphNode, file: any): void
onInputClick?(this: LGraphNode, index: number, e: CanvasMouseEvent): void
onInputDblClick?(this: LGraphNode, index: number, e: CanvasMouseEvent): void
onOutputClick?(this: LGraphNode, index: number, e: CanvasMouseEvent): void
onOutputDblClick?(this: LGraphNode, index: number, e: CanvasMouseEvent): void
// TODO: Return type
onGetPropertyInfo?(this: LGraphNode, property: string): any
onNodeOutputAdd?(this: LGraphNode, value): void
onNodeInputAdd?(this: LGraphNode, value): void
onMenuNodeInputs?(this: LGraphNode, entries: IOptionalInputsData[]): IOptionalInputsData[]
onMenuNodeOutputs?(this: LGraphNode, entries: IOptionalInputsData[]): IOptionalInputsData[]
onGetInputs?(this: LGraphNode): INodeInputSlot[]
onGetOutputs?(this: LGraphNode): INodeOutputSlot[]
onMouseUp?(this: LGraphNode, e: CanvasMouseEvent, pos: Point): void
onMouseEnter?(this: LGraphNode, e: CanvasMouseEvent): void
onMouseDown?(this: LGraphNode, e: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): boolean
onDblClick?(this: LGraphNode, e: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): void
onNodeTitleDblClick?(this: LGraphNode, e: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): void
onDrawTitle?(this: LGraphNode, ctx: CanvasRenderingContext2D): void
onDrawTitleText?(this: LGraphNode, ctx: CanvasRenderingContext2D, title_height: number, size: Size, scale: number, title_text_font: string, selected: boolean): void
onDrawTitleBox?(this: LGraphNode, ctx: CanvasRenderingContext2D, title_height: number, size: Size, scale: number): void
onDrawTitleBar?(this: LGraphNode, ctx: CanvasRenderingContext2D, title_height: number, size: Size, scale: number, fgcolor: any): void
onRemoved?(this: LGraphNode): void
onMouseMove?(this: LGraphNode, e: MouseEvent, pos: Point, arg2: LGraphCanvas): void
onPropertyChange?(this: LGraphNode): void
updateOutputData?(this: LGraphNode, origin_slot: number): void
constructor(title: string) {
this._ctor(title);
}
_ctor(title) {
_ctor(title: string): void {
this.title = title || "Unnamed";
this.size = [LiteGraph.NODE_WIDTH, 60];
this.graph = null;
@@ -117,7 +280,7 @@ export class LGraphNode {
* configure a node from an object containing the serialized info
* @method configure
*/
configure(info) {
configure(info: ISerialisedNode): void {
if (this.graph) {
this.graph._version++;
}
@@ -213,7 +376,7 @@ export class LGraphNode {
* serialize the content
* @method serialize
*/
serialize() {
serialize(): ISerialisedNode {
//create serialization object
var o = {
id: this.id,
@@ -291,7 +454,7 @@ export class LGraphNode {
}
/* Creates a clone of this node */
clone() {
clone(): LGraphNode {
var node = LiteGraph.createNode(this.type);
if (!node) {
return null;
@@ -331,7 +494,7 @@ export class LGraphNode {
* serialize and stringify
* @method toString
*/
toString() {
toString(): string {
return JSON.stringify(this.serialize());
}
@@ -340,7 +503,7 @@ export class LGraphNode {
* get the title string
* @method getTitle
*/
getTitle() {
getTitle(): string {
return this.title || this.constructor.title;
}
@@ -350,7 +513,7 @@ export class LGraphNode {
* @param {String} name
* @param {*} value
*/
setProperty(name, value) {
setProperty(name: string, value: TWidgetValue): void {
if (!this.properties) {
this.properties = {};
}
@@ -381,7 +544,7 @@ export class LGraphNode {
* @param {number} slot
* @param {*} data
*/
setOutputData(slot, data) {
setOutputData(slot: number, data: unknown): void {
if (!this.outputs) {
return;
}
@@ -418,7 +581,7 @@ export class LGraphNode {
* @param {number} slot
* @param {String} datatype
*/
setOutputDataType(slot, type) {
setOutputDataType(slot: number, type: ISlotType): void {
if (!this.outputs) {
return;
}
@@ -448,7 +611,7 @@ export class LGraphNode {
* @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link
* @return {*} data or if it is not connected returns undefined
*/
getInputData(slot, force_update) {
getInputData(slot: number, force_update?: boolean): unknown {
if (!this.inputs) {
return;
} //undefined;
@@ -489,7 +652,7 @@ export class LGraphNode {
* @param {number} slot
* @return {String} datatype in string format
*/
getInputDataType(slot) {
getInputDataType(slot: number): ISlotType {
if (!this.inputs) {
return null;
} //undefined;
@@ -521,8 +684,7 @@ export class LGraphNode {
* @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link
* @return {*} data or if it is not connected returns null
*/
getInputDataByName(slot_name,
force_update) {
getInputDataByName(slot_name: string, force_update: boolean): unknown {
var slot = this.findInputSlot(slot_name);
if (slot == -1) {
return null;
@@ -536,7 +698,7 @@ export class LGraphNode {
* @param {number} slot
* @return {boolean}
*/
isInputConnected(slot) {
isInputConnected(slot: number): boolean {
if (!this.inputs) {
return false;
}
@@ -549,7 +711,7 @@ export class LGraphNode {
* @param {number} slot
* @return {Object} object or null { link: id, name: string, type: string or 0 }
*/
getInputInfo(slot) {
getInputInfo(slot: number): INodeInputSlot {
if (!this.inputs) {
return null;
}
@@ -565,7 +727,7 @@ export class LGraphNode {
* @param {number} slot
* @return {LLink} object or null
*/
getInputLink(slot) {
getInputLink(slot: number): LLink | null {
if (!this.inputs) {
return null;
}
@@ -582,7 +744,7 @@ export class LGraphNode {
* @param {number} slot
* @return {LGraphNode} node or null
*/
getInputNode(slot) {
getInputNode(slot: number): LGraphNode {
if (!this.inputs) {
return null;
}
@@ -606,7 +768,7 @@ export class LGraphNode {
* @param {string} name
* @return {*} value
*/
getInputOrProperty(name) {
getInputOrProperty(name: string): unknown {
if (!this.inputs || !this.inputs.length) {
return this.properties ? this.properties[name] : null;
}
@@ -629,7 +791,7 @@ export class LGraphNode {
* @param {number} slot
* @return {Object} object or null
*/
getOutputData(slot) {
getOutputData(slot: number): unknown {
if (!this.outputs) {
return null;
}
@@ -647,7 +809,7 @@ export class LGraphNode {
* @param {number} slot
* @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] }
*/
getOutputInfo(slot) {
getOutputInfo(slot: number): INodeOutputSlot {
if (!this.outputs) {
return null;
}
@@ -663,7 +825,7 @@ export class LGraphNode {
* @param {number} slot
* @return {boolean}
*/
isOutputConnected(slot) {
isOutputConnected(slot: number): boolean {
if (!this.outputs) {
return false;
}
@@ -679,7 +841,7 @@ export class LGraphNode {
* @method isAnyOutputConnected
* @return {boolean}
*/
isAnyOutputConnected() {
isAnyOutputConnected(): boolean {
if (!this.outputs) {
return false;
}
@@ -697,7 +859,7 @@ export class LGraphNode {
* @param {number} slot
* @return {array}
*/
getOutputNodes(slot) {
getOutputNodes(slot: number): LGraphNode[] {
if (!this.outputs || this.outputs.length == 0) {
return null;
}
@@ -725,7 +887,7 @@ export class LGraphNode {
return r;
}
addOnTriggerInput() {
addOnTriggerInput(): number {
var trigS = this.findInputSlot("onTrigger");
if (trigS == -1) { //!trigS ||
var input = this.addInput("onTrigger", LiteGraph.EVENT, { optional: true, nameLocked: true });
@@ -734,7 +896,7 @@ export class LGraphNode {
return trigS;
}
addOnExecutedOutput() {
addOnExecutedOutput(): number {
var trigS = this.findOutputSlot("onExecuted");
if (trigS == -1) { //!trigS ||
var output = this.addOutput("onExecuted", LiteGraph.ACTION, { optional: true, nameLocked: true });
@@ -743,7 +905,7 @@ export class LGraphNode {
return trigS;
}
onAfterExecuteNode(param, options) {
onAfterExecuteNode(param: unknown, options?: { action_call?: any }) {
var trigS = this.findOutputSlot("onExecuted");
if (trigS != -1) {
@@ -755,7 +917,7 @@ export class LGraphNode {
}
}
changeMode(modeTo) {
changeMode(modeTo: number): boolean {
switch (modeTo) {
case LiteGraph.ON_EVENT:
// this.addOnExecutedOutput();
@@ -789,7 +951,7 @@ export class LGraphNode {
* @param {*} param
* @param {*} options
*/
doExecute(param, options) {
doExecute(param?: unknown, options?: { action_call?: any }): void {
options = options || {};
if (this.onExecute) {
@@ -821,7 +983,7 @@ export class LGraphNode {
* @param {String} action name
* @param {*} param
*/
actionDo(action, param, options) {
actionDo(action: string, param: unknown, options: { action_call?: string }): void {
options = options || {};
if (this.onAction) {
@@ -852,7 +1014,7 @@ export class LGraphNode {
* @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all
* @param {*} param
*/
trigger(action, param, options) {
trigger(action: string, param: unknown, options: { action_call?: any }): void {
if (!this.outputs || !this.outputs.length) {
return;
}
@@ -875,7 +1037,7 @@ export class LGraphNode {
* @param {*} param
* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot
*/
triggerSlot(slot, param, link_id, options) {
triggerSlot(slot: number, param: unknown, link_id: number, options: { action_call?: any }): void {
options = options || {};
if (!this.outputs) {
return;
@@ -950,7 +1112,7 @@ export class LGraphNode {
* @param {Number} slot the index of the output slot
* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot
*/
clearTriggeredSlot(slot, link_id) {
clearTriggeredSlot(slot: number, link_id: number): void {
if (!this.outputs) {
return;
}
@@ -986,7 +1148,7 @@ export class LGraphNode {
* @method setSize
* @param {vec2} size
*/
setSize(size) {
setSize(size: Size): void {
this.size = size;
if (this.onResize)
this.onResize(this.size);
@@ -1000,10 +1162,10 @@ export class LGraphNode {
* @param {string} type string defining the output type ("vec3","number",...)
* @param {Object} extra_info this can be used to have special properties of the property (like values, etc)
*/
addProperty(name,
default_value,
type,
extra_info) {
addProperty(name: string,
default_value: unknown,
type?: string,
extra_info?: Dictionary<unknown>): INodePropertyInfo {
var o = { name: name, type: type, default_value: default_value };
if (extra_info) {
for (var i in extra_info) {
@@ -1029,7 +1191,7 @@ export class LGraphNode {
* @param {string} type string defining the output type ("vec3","number",...)
* @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc)
*/
addOutput(name, type, extra_info) {
addOutput(name?: string, type?: ISlotType, extra_info?: object): INodeOutputSlot {
var output = { name: name, type: type, links: null };
if (extra_info) {
for (var i in extra_info) {
@@ -1057,7 +1219,7 @@ export class LGraphNode {
* @method addOutputs
* @param {Array} array of triplets like [[name,type,extra_info],[...]]
*/
addOutputs(array) {
addOutputs(array: [string, ISlotType, Record<string, unknown>][]): void {
for (var i = 0; i < array.length; ++i) {
var info = array[i];
var o = { name: info[0], type: info[1], link: null };
@@ -1088,7 +1250,7 @@ export class LGraphNode {
* @method removeOutput
* @param {number} slot
*/
removeOutput(slot) {
removeOutput(slot: number): void {
this.disconnectOutput(slot);
this.outputs.splice(slot, 1);
for (var i = slot; i < this.outputs.length; ++i) {
@@ -1119,7 +1281,7 @@ export class LGraphNode {
* @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0
* @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc)
*/
addInput(name, type, extra_info) {
addInput(name: string, type: ISlotType, extra_info?: object): INodeInputSlot {
type = type || 0;
var input = { name: name, type: type, link: null };
if (extra_info) {
@@ -1150,7 +1312,7 @@ export class LGraphNode {
* @method addInputs
* @param {Array} array of triplets like [[name,type,extra_info],[...]]
*/
addInputs(array) {
addInputs(array: [string, ISlotType, Record<string, unknown>][]): void {
for (var i = 0; i < array.length; ++i) {
var info = array[i];
var o = { name: info[0], type: info[1], link: null };
@@ -1180,7 +1342,7 @@ export class LGraphNode {
* @method removeInput
* @param {number} slot
*/
removeInput(slot) {
removeInput(slot: number): void {
this.disconnectInput(slot);
var slot_info = this.inputs.splice(slot, 1);
for (var i = slot; i < this.inputs.length; ++i) {
@@ -1208,7 +1370,7 @@ export class LGraphNode {
* @param {[x,y]} pos position of the connection inside the node
* @param {string} direction if is input or output
*/
addConnection(name, type, pos, direction) {
addConnection(name: string, type: string, pos: Point, direction: string) {
var o = {
name: name,
type: type,
@@ -1226,7 +1388,7 @@ export class LGraphNode {
* @param {vec2} minHeight
* @return {vec2} the total size
*/
computeSize(out) {
computeSize(out?: Size): Size {
if (this.constructor.size) {
return this.constructor.size.concat();
}
@@ -1313,7 +1475,7 @@ export class LGraphNode {
return size;
}
inResizeCorner(canvasX, canvasY) {
inResizeCorner(canvasX: number, canvasY: number): boolean {
var rows = this.outputs ? this.outputs.length : 1;
var outputs_offset = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT;
return isInsideRectangle(canvasX,
@@ -1332,7 +1494,7 @@ export class LGraphNode {
* @param {String} property name of the property
* @return {Object} the object with all the available info
*/
getPropertyInfo(property) {
getPropertyInfo(property: string) {
var info = null;
//there are several ways to define info about a property
@@ -1378,7 +1540,7 @@ export class LGraphNode {
* @param {Object} options the object that contains special properties of this widget
* @return {Object} the created widget object
*/
addWidget(type, name, value, callback, options) {
addWidget(type: string, name: string, value: any, callback: IWidget["callback"], options?: any): IWidget {
if (!this.widgets) {
this.widgets = [];
}
@@ -1427,7 +1589,7 @@ export class LGraphNode {
return w;
}
addCustomWidget(custom_widget) {
addCustomWidget(custom_widget: IWidget): IWidget {
if (!this.widgets) {
this.widgets = [];
}
@@ -1442,7 +1604,7 @@ export class LGraphNode {
* @param compute_outer {boolean?} [optional] set to true to include the shadow and connection points in the bounding calculation
* @return {Float32Array[4]} the bounding box in format of [topleft_cornerx, topleft_cornery, width, height]
*/
getBounding(out, compute_outer) {
getBounding(out?: Float32Array, compute_outer?: boolean): Float32Array {
out = out || new Float32Array(4);
const nodePos = this.pos;
const isCollapsed = this.flags.collapsed;
@@ -1487,7 +1649,7 @@ export class LGraphNode {
* @param {number} y
* @return {boolean}
*/
isPointInside(x, y, margin, skip_title) {
isPointInside(x: number, y: number, margin?: number, skip_title?: boolean): boolean {
margin = margin || 0;
var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT;
@@ -1523,7 +1685,7 @@ export class LGraphNode {
* @param {number} y
* @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }
*/
getSlotInPosition(x, y) {
getSlotInPosition(x: number, y: number): IFoundSlot | null {
//search for inputs
var link_pos = new Float32Array(2);
if (this.inputs) {
@@ -1570,7 +1732,18 @@ export class LGraphNode {
* @param {boolean} returnObj if the obj itself wanted
* @return {number_or_object} the slot (-1 if not found)
*/
findInputSlot(name, returnObj) {
findInputSlot<TReturn extends false>(name: string, returnObj?: TReturn): number
findInputSlot<TReturn extends true>(name: string, returnObj?: TReturn): INodeInputSlot
findInputSlot(name: string, returnObj: boolean = false) {
if (!this.inputs) {
return -1
}
for (let i = 0, l = this.inputs.length; i < l; ++i) {
if (name == this.inputs[i].name) {
return !returnObj ? i : this.inputs[i]
}
}
return -1
if (!this.inputs) {
return -1;
}
@@ -1589,7 +1762,9 @@ export class LGraphNode {
* @param {boolean} returnObj if the obj itself wanted
* @return {number_or_object} the slot (-1 if not found)
*/
findOutputSlot(name, returnObj) {
findOutputSlot<TReturn extends false>(name: string, returnObj?: TReturn): number
findOutputSlot<TReturn extends true>(name: string, returnObj?: TReturn): INodeOutputSlot
findOutputSlot(name: string, returnObj: boolean = false) {
returnObj = returnObj || false;
if (!this.outputs) {
return -1;
@@ -1609,7 +1784,9 @@ export class LGraphNode {
* @param {object} options
* @return {number_or_object} the slot (-1 if not found)
*/
findInputSlotFree(optsIn) {
findInputSlotFree<TReturn extends false>(optsIn: { typesNotAccepted: number[], returnObj?: TReturn }): number
findInputSlotFree<TReturn extends true>(optsIn: { typesNotAccepted: number[], returnObj?: TReturn }): INodeInputSlot
findInputSlotFree(optsIn: { typesNotAccepted: number[], returnObj?: boolean }) {
var optsIn = optsIn || {};
var optsDef = {
returnObj: false,
@@ -1637,7 +1814,9 @@ export class LGraphNode {
* @param {object} options
* @return {number_or_object} the slot (-1 if not found)
*/
findOutputSlotFree(optsIn) {
findOutputSlotFree<TReturn extends false>(optsIn: { typesNotAccepted: number[], returnObj?: TReturn }): number
findOutputSlotFree<TReturn extends true>(optsIn: { typesNotAccepted: number[], returnObj?: TReturn }): INodeOutputSlot
findOutputSlotFree(optsIn: { typesNotAccepted: number[], returnObj?: boolean }) {
var optsIn = optsIn || {};
var optsDef = {
returnObj: false,
@@ -1740,7 +1919,7 @@ export class LGraphNode {
* @param {string} target_type the input slot type of the target node
* @return {Object} the link_info is created, otherwise null
*/
connectByType(slot, target_node, target_slotType, optsIn) {
connectByType(slot: number, target_node: LGraphNode, target_slotType: ISlotType, optsIn?: { reroutes?: RerouteId[] }): LLink | null {
var optsIn = optsIn || {};
var optsDef = {
createEventInCase: true,
@@ -1793,7 +1972,7 @@ export class LGraphNode {
* @param {string} target_type the output slot type of the target node
* @return {Object} the link_info is created, otherwise null
*/
connectByTypeOutput(slot, source_node, source_slotType, optsIn) {
connectByTypeOutput(slot: number, source_node: LGraphNode, source_slotType: ISlotType, optsIn?: unknown): any {
var optsIn = optsIn || {};
var optsDef = {
createEventInCase: true,
@@ -1848,7 +2027,7 @@ export class LGraphNode {
* @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)
* @return {Object} the link_info is created, otherwise null
*/
connect(slot, target_node, target_slot) {
connect(slot: number, target_node: LGraphNode, target_slot: ISlotType, reroutes?: RerouteId[]): LLink | null {
target_slot = target_slot || 0;
if (!this.graph) {
@@ -2057,7 +2236,7 @@ export class LGraphNode {
* @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]
* @return {boolean} if it was disconnected successfully
*/
disconnectOutput(slot, target_node) {
disconnectOutput(slot: string | number, target_node?: LGraphNode): boolean {
if (slot.constructor === String) {
slot = this.findOutputSlot(slot);
if (slot == -1) {
@@ -2213,7 +2392,7 @@ export class LGraphNode {
* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
* @return {boolean} if it was disconnected successfully
*/
disconnectInput(slot) {
disconnectInput(slot: number | string): boolean {
//seek for the output slot
if (slot.constructor === String) {
slot = this.findInputSlot(slot);
@@ -2307,9 +2486,7 @@ export class LGraphNode {
* @param {vec2} out [optional] a place to store the output, to free garbage
* @return {[x,y]} the position
**/
getConnectionPos(is_input,
slot_number,
out) {
getConnectionPos(is_input: boolean, slot_number: number, out?: Point): Point {
out = out || new Float32Array(2);
var num_slots = 0;
if (is_input && this.inputs) {
@@ -2389,7 +2566,7 @@ export class LGraphNode {
}
/* Force align to grid */
alignToGrid() {
alignToGrid(): void {
this.pos[0] =
LiteGraph.CANVAS_GRID_SIZE *
Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE);
@@ -2399,7 +2576,7 @@ export class LGraphNode {
}
/* Console output */
trace(msg) {
trace(msg?: string) {
if (!this.console) {
this.console = [];
}
@@ -2414,8 +2591,7 @@ export class LGraphNode {
}
/* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */
setDirtyCanvas(dirty_foreground,
dirty_background) {
setDirtyCanvas(dirty_foreground: boolean, dirty_background?: boolean) {
if (!this.graph) {
return;
}
@@ -2425,7 +2601,7 @@ export class LGraphNode {
]);
}
loadImage(url) {
loadImage(url: string): any {
var img = new Image();
img.src = LiteGraph.node_images_path + url;
img.ready = false;
@@ -2477,7 +2653,7 @@ export class LGraphNode {
}
*/
/* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */
captureInput(v) {
captureInput(v: boolean): void {
if (!this.graph || !this.graph.list_of_graphcanvas) {
return;
}
@@ -2508,7 +2684,7 @@ export class LGraphNode {
* Collapse the node to make it smaller on the canvas
* @method collapse
**/
collapse(force) {
collapse(force?: boolean): void {
this.graph._version++;
if (!this.collapsible && !force) {
return;
@@ -2544,10 +2720,10 @@ export class LGraphNode {
}
}
localToScreen(x, y, graphcanvas) {
localToScreen(x: number, y: number, dragAndScale: DragAndScale): Point {
return [
(x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0],
(y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]
(x + this.pos[0]) * dragAndScale.scale + dragAndScale.offset[0],
(y + this.pos[1]) * dragAndScale.scale + dragAndScale.offset[1]
];
}

View File

@@ -1,7 +1,23 @@
// @ts-nocheck
//this is the class in charge of storing link information
import type { ISlotType } from "./interfaces"
import type { NodeId } from "./LGraphNode"
export type LinkId = number | string
export type SerialisedLLinkArray = [LinkId, NodeId, number, NodeId, number, ISlotType]
//this is the class in charge of storing link information
export class LLink {
id?: LinkId
type?: ISlotType
origin_id?: NodeId
origin_slot?: number
target_id?: NodeId
target_slot?: number
data?: number | string | boolean | { toToolTip?(): string }
_data?: unknown
_pos: Float32Array
_last_time?: number
constructor(id, type, origin_id, origin_slot, target_id, target_slot) {
this.id = id;
this.type = type;
@@ -13,6 +29,8 @@ export class LLink {
this._data = null;
this._pos = new Float32Array(2); //center
}
// configure(o: LLink | SerialisedLLinkArray) {
configure(o) {
if (o.constructor === Array) {
this.id = o[0];
@@ -30,7 +48,8 @@ export class LLink {
this.target_slot = o.target_slot;
}
}
serialize() {
serialize(): SerialisedLLinkArray {
return [
this.id,
this.origin_id,

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
import type { Vector2, INodeSlot } from "../public/litegraph";
import type { Vector2 } from "./litegraph";
import type { INodeSlot } from "./interfaces"
export enum SlotType {
Array = "array",

138
src/interfaces.ts Normal file
View File

@@ -0,0 +1,138 @@
import type { ContextMenu } from "./ContextMenu"
import type { LGraphNode } from "./LGraphNode"
import type { LinkDirection, RenderShape } from "./types/globalEnums"
import type { LinkId } from "./LLink"
import type { SlotDirection } from "./draw"
export type Dictionary<T> = { [key: string]: T }
export type CanvasColour = string | CanvasGradient | CanvasPattern
export interface IInputOrOutput {
// If an input, this will be defined
input?: INodeInputSlot
// If an output, this will be defined
output?: INodeOutputSlot
}
export interface IFoundSlot extends IInputOrOutput {
// Slot index
slot: number
// Centre point of the rendered slot connection
link_pos: Point
}
/** A point on the canvas: x, y */
export type Point = [x: number, y: number] | Float32Array | Float64Array
/** A size: width, height */
export type Size = [width: number, height: number] | Float32Array | Float64Array
/** A very firm array */
type ArRect = [x: number, y: number, width: number, height: number]
/** A rectangle starting at top-left coordinates: x, y, width, height */
export type Rect = ArRect | Float32Array | Float64Array
// TODO: Need regular arrays also?
export type Rect32 = Float32Array
/** Union of property names that are of type Match */
export type KeysOfType<T, Match> = { [P in keyof T]: T[P] extends Match ? P : never }[keyof T]
/** A new type that contains only the properties of T that are of type Match */
export type PickByType<T, Match> = { [P in keyof T]: Extract<T[P], Match> }
/** The names of all methods and functions in T */
export type MethodNames<T> = KeysOfType<T, (...args: any) => any>
export interface IBoundaryNodes {
top: LGraphNode
right: LGraphNode
bottom: LGraphNode
left: LGraphNode
}
export type Direction = "top" | "bottom" | "left" | "right"
// TODO: Rename IOptionalSlotsData
export interface IOptionalInputsData {
content: string
value?
className?: string
}
export type ISlotType = number | string
export interface INodeSlot {
name: string
type: ISlotType
dir?: LinkDirection & SlotDirection
removable?: boolean
shape?: RenderShape
not_subgraph_input?: boolean
color_off?: CanvasColour
color_on?: CanvasColour
label?: string
locked?: boolean
nameLocked?: boolean
pos?: Point
widget?: unknown
}
export interface INodeFlags {
skip_repeated_outputs?: boolean
allow_interaction?: boolean
pinned?: boolean
collapsed?: boolean
}
export interface INodeInputSlot extends INodeSlot {
link?: LinkId
not_subgraph_input?: boolean
}
export interface INodeOutputSlot extends INodeSlot {
links?: LinkId[]
_data?: unknown
slot_index?: number
not_subgraph_output?: boolean
}
/** Links */
export interface ConnectingLink extends IInputOrOutput {
node: LGraphNode
slot: number
pos: Point
direction?: LinkDirection
}
/** ContextMenu */
export interface IContextMenuOptions {
ignore_item_callbacks?: boolean
title?: string
parentMenu?: ContextMenu
className?: string
event?: MouseEvent
extra?: unknown
scroll_speed?: number
left?: number
top?: number
scale?: string
node?: LGraphNode
autoopen?: boolean
callback?(value?: unknown, options?: unknown, event?: MouseEvent, previous_menu?: ContextMenu, node?: LGraphNode): void
}
export interface IContextMenuValue {
title?: string
content: string
has_submenu?: boolean
disabled?: boolean
className?: any
submenu?: unknown
property?: string
type?: string
slot?: IFoundSlot
callback?: IContextMenuOptions["callback"]
}

View File

@@ -1,23 +0,0 @@
import { LiteGraphGlobal } from "./LiteGraphGlobal";
import { LGraph } from "./LGraph"
import { LLink } from "./LLink"
import { LGraphNode } from "./LGraphNode";
import { LGraphGroup } from "./LGraphGroup";
import { DragAndScale } from "./DragAndScale";
import { LGraphCanvas } from "./LGraphCanvas";
import { ContextMenu } from "./ContextMenu";
import { CurveEditor } from "./CurveEditor";
import { loadPolyfills } from "./polyfills";
export { LGraph, LLink, LGraphNode, LGraphGroup, DragAndScale, LGraphCanvas, ContextMenu, CurveEditor }
export const LiteGraph = new LiteGraphGlobal()
export function clamp(v, a, b) {
return a > v ? a : b < v ? b : v;
};
// Load legacy polyfills
loadPolyfills();
export { LGraphBadge, BadgePosition } from "./LGraphBadge"

106
src/litegraph.ts Normal file
View File

@@ -0,0 +1,106 @@
import type { Point, ConnectingLink } from "./interfaces"
import type { INodeSlot, INodeInputSlot, INodeOutputSlot, CanvasColour, Direction, IBoundaryNodes, IContextMenuOptions, IContextMenuValue, IFoundSlot, IInputOrOutput, INodeFlags, IOptionalInputsData, ISlotType, KeysOfType, MethodNames, PickByType, Rect, Rect32, Size } from "./interfaces"
import type { SlotShape, LabelPosition, SlotDirection, SlotType } from "./draw"
import type { IWidget } from "./types/widgets"
import type { TitleMode } from "./types/globalEnums"
import { LiteGraphGlobal } from "./LiteGraphGlobal"
import { loadPolyfills } from "./polyfills"
import { LGraph } from "./LGraph"
import { LGraphCanvas } from "./LGraphCanvas"
import { DragAndScale } from "./DragAndScale"
import { LGraphNode } from "./LGraphNode"
import { LGraphGroup } from "./LGraphGroup"
import { LLink } from "./LLink"
import { ContextMenu } from "./ContextMenu"
import { CurveEditor } from "./CurveEditor"
import { LGraphBadge, BadgePosition } from "./LGraphBadge"
export const LiteGraph = new LiteGraphGlobal()
export { LGraph, LGraphCanvas, DragAndScale, LGraphNode, LGraphGroup, LLink, ContextMenu, CurveEditor }
export { INodeSlot, INodeInputSlot, INodeOutputSlot, ConnectingLink, CanvasColour, Direction, IBoundaryNodes, IContextMenuOptions, IContextMenuValue, IFoundSlot, IInputOrOutput, INodeFlags, IOptionalInputsData, ISlotType, KeysOfType, MethodNames, PickByType, Rect, Rect32, Size }
export { IWidget }
export { LGraphBadge, BadgePosition }
export { SlotShape, LabelPosition, SlotDirection, SlotType }
// TODO: Remove legacy accessors
LiteGraph.LGraph = LGraph
LiteGraph.LLink = LLink
LiteGraph.LGraphNode = LGraphNode
LiteGraph.LGraphGroup = LGraphGroup
LiteGraph.DragAndScale = DragAndScale
LiteGraph.LGraphCanvas = LGraphCanvas
LiteGraph.ContextMenu = ContextMenu
LiteGraph.CurveEditor = CurveEditor
export function clamp(v: number, a: number, b: number): number {
return a > v ? a : b < v ? b : v
};
// Load legacy polyfills
loadPolyfills()
// Backwards compat
// Type definitions for litegraph.js 0.7.0
// Project: litegraph.js
// Definitions by: NateScarlet <https://github.com/NateScarlet>
export type Vector2 = Point
export type Vector4 = [number, number, number, number]
export interface IContextMenuItem {
content: string
callback?: ContextMenuEventListener
/** Used as innerHTML for extra child element */
title?: string
disabled?: boolean
has_submenu?: boolean
submenu?: {
options: IContextMenuItem[]
} & IContextMenuOptions
className?: string
}
export type ContextMenuEventListener = (
value: IContextMenuItem,
options: IContextMenuOptions,
event: MouseEvent,
parentMenu: ContextMenu | undefined,
node: LGraphNode
) => boolean | void
export interface LinkReleaseContext {
node_to?: LGraphNode
node_from?: LGraphNode
slot_from: INodeSlot
type_filter_in?: string
type_filter_out?: string
}
export interface LinkReleaseContextExtended {
links: ConnectingLink[]
}
export type LiteGraphCanvasEventType = "empty-release" | "empty-double-click" | "group-double-click"
export interface LiteGraphCanvasEvent extends CustomEvent<{
subType: string
originalEvent: Event
linkReleaseContext?: LinkReleaseContextExtended
group?: LGraphGroup
}> { }
export interface LiteGraphCanvasGroupEvent extends CustomEvent<{
subType: "group-double-click"
originalEvent: MouseEvent
group: LGraphGroup
}> { }
/** https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#lgraphnode */
export interface LGraphNodeConstructor<T extends LGraphNode = LGraphNode> {
nodeData: any
new(): T
}
// End backwards compat

40
src/types/events.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* Event interfaces for event extension
*/
/** For Canvas*Event - adds graph space co-ordinates (property names are shipped) */
export interface ICanvasPosition {
/** X co-ordinate of the event, in graph space (NOT canvas space) */
canvasX?: number
/** Y co-ordinate of the event, in graph space (NOT canvas space) */
canvasY?: number
}
/** For Canvas*Event */
export interface IDeltaPosition {
deltaX?: number
deltaY?: number
}
/** PointerEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasPointerEvent extends PointerEvent, CanvasMouseEvent { }
/** MouseEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasMouseEvent extends MouseEvent, ICanvasPosition, IDeltaPosition {
dragging?: boolean
click_time?: number
dataTransfer?: unknown
}
/** WheelEvent with canvasX/Y properties */
export interface CanvasWheelEvent extends WheelEvent, ICanvasPosition {
dragging?: boolean
click_time?: number
dataTransfer?: unknown
}
/** DragEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasDragEvent extends DragEvent, ICanvasPosition, IDeltaPosition { }
/** TouchEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasTouchEvent extends TouchEvent, ICanvasPosition, IDeltaPosition { }

51
src/types/globalEnums.ts Normal file
View File

@@ -0,0 +1,51 @@
/** Node slot type - input or output */
export enum NodeSlotType {
INPUT = 1,
OUTPUT = 2,
}
/** Shape that an object will render as - used by nodes and slots */
export enum RenderShape {
BOX = 1,
ROUND = 2,
CIRCLE = 3,
CARD = 4,
ARROW = 5,
/** intended for slot arrays */
GRID = 6,
}
/** The direction that a link point will flow towards - e.g. horizontal outputs are right by default */
export enum LinkDirection {
NONE = 0,
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4,
CENTER = 5,
}
/** The path calculation that links follow */
export enum LinkRenderType {
/** Juts out from the input & output a little @see LinkDirection, then a straight line between them */
STRAIGHT_LINK = 0,
/** 90° angles, clean and box-like */
LINEAR_LINK = 1,
/** Smooth curved links - default */
SPLINE_LINK = 2,
}
export enum TitleMode {
NORMAL_TITLE = 0,
NO_TITLE = 1,
TRANSPARENT_TITLE = 2,
AUTOHIDE_TITLE = 3,
}
export enum LGraphEventMode {
ALWAYS = 0,
ON_EVENT = 1,
NEVER = 2,
ON_TRIGGER = 3,
BYPASS = 4,
}

View File

@@ -0,0 +1,61 @@
import type { CanvasColour, Dictionary, INodeFlags, INodeInputSlot, INodeOutputSlot, Point, Rect, Size } from "@/interfaces"
import type { LGraph } from "@/LGraph"
import type { IGraphGroupFlags, LGraphGroup } from "@/LGraphGroup"
import type { LGraphNode, NodeId } from "@/LGraphNode"
import type { LiteGraph } from "@/litegraph"
import type { LinkId, LLink } from "@/LLink"
import type { TWidgetValue } from "@/types/widgets"
/** Serialised LGraphNode */
export interface ISerialisedNode {
title?: string
id?: NodeId
type?: string
pos?: Point
size?: Size
flags?: INodeFlags
order?: number
mode?: number
outputs?: INodeOutputSlot[]
inputs?: INodeInputSlot[]
properties?: Dictionary<unknown>
shape?: Rect
boxcolor?: CanvasColour
color?: CanvasColour
bgcolor?: string
widgets_values?: TWidgetValue[]
}
/** Contains serialised graph elements */
export type ISerialisedGraph<
TNode = ReturnType<LGraphNode["serialize"]>,
TLink = ReturnType<LLink["serialize"]>,
TGroup = ReturnType<LGraphGroup["serialize"]>
> = {
last_node_id: LGraph["last_node_id"]
last_link_id: LGraph["last_link_id"]
last_reroute_id: LGraph["last_reroute_id"]
nodes: TNode[]
links: TLink[] | LLink[]
groups: TGroup[]
config: LGraph["config"]
version: typeof LiteGraph.VERSION
extra?: unknown
}
/** Serialised LGraphGroup */
export interface ISerialisedGroup {
title: string
bounding: number[]
color: string
font_size: number
flags?: IGraphGroupFlags
}
export type TClipboardLink = [targetRelativeIndex: number, originSlot: number, nodeRelativeIndex: number, targetSlot: number, targetNodeId: NodeId]
/** */
export interface IClipboardContents {
nodes?: ISerialisedNode[]
links?: TClipboardLink[]
}

124
src/types/widgets.ts Normal file
View File

@@ -0,0 +1,124 @@
import { CanvasColour, Point, Size } from "@/interfaces"
import type { LGraphCanvas, LGraphNode } from "@/litegraph"
import type { CanvasMouseEvent } from "./events"
export interface IWidgetOptions<TValue = unknown> extends Record<string, unknown> {
on?: string
off?: string
max?: number
min?: number
slider_color?: CanvasColour
marker_color?: CanvasColour
precision?: number
read_only?: boolean
step?: number
y?: number
multiline?: boolean
// TODO: Confirm this
property?: string
hasOwnProperty?(arg0: string): any
// values?(widget?: IWidget, node?: LGraphNode): any
values?: TValue[]
callback?: IWidget["callback"]
onHide?(widget: IWidget): void
}
/**
* A widget for a node.
* @typescript
* All types are based on IBaseWidget - additions can be made there or directly on individual types.
*
* Implemented as a discriminative union of widget types, so this type itself cannot be extended.
* Recommend declaration merging any properties that use IWidget (e.g. {@link LGraphNode.widgets}) with a new type alias.
* @see ICustomWidget
*/
export type IWidget = IBooleanWidget | INumericWidget | IStringWidget | IMultilineStringWidget | IComboWidget | ICustomWidget
export interface IBooleanWidget extends IBaseWidget {
type?: "toggle"
value: boolean
}
/** Any widget that uses a numeric backing */
export interface INumericWidget extends IBaseWidget {
type?: "slider" | "number"
value: number
}
/** A combo-box widget (dropdown, select, etc) */
export interface IComboWidget extends IBaseWidget {
type?: "combo"
value: string | number
options: IWidgetOptions<string>
}
export type IStringWidgetType = IStringWidget["type"] | IMultilineStringWidget["type"]
/** A widget with a string value */
export interface IStringWidget extends IBaseWidget {
type?: "string" | "text" | "button"
value: string
}
/** A widget with a string value and a multiline text input */
export interface IMultilineStringWidget<TElement extends HTMLElement = HTMLTextAreaElement> extends IBaseWidget {
type?: "multiline"
value: string
/** HTML textarea element */
element?: TElement
}
/** A custom widget - accepts any value and has no built-in special handling */
export interface ICustomWidget<TElement extends HTMLElement = HTMLElement> extends IBaseWidget<TElement> {
type?: "custom"
value: string | object
element?: TElement
}
/**
* Valid widget types. TS cannot provide easily extensible type safety for this at present.
* Override linkedWidgets[]
* Values not in this list will not result in litegraph errors, however they will be treated the same as "custom".
*/
export type TWidgetType = IWidget["type"]
export type TWidgetValue = IWidget["value"]
/**
* The base type for all widgets. Should not be implemented directly.
* @see IWidget
*/
export interface IBaseWidget<TElement extends HTMLElement = HTMLElement> {
linkedWidgets?: IWidget[]
options: IWidgetOptions
marker?: number
label?: string
clicked?: boolean
name?: string
/** Widget type (see {@link TWidgetType}) */
type?: TWidgetType
value?: TWidgetValue
y?: number
last_y?: number
width?: number
disabled?: boolean
tooltip?: string
/** HTML widget element */
element?: TElement
// TODO: Confirm this format
callback?(value: any, canvas?: LGraphCanvas, node?: LGraphNode, pos?: Point, e?: CanvasMouseEvent): void
onRemove?(): void
beforeQueued?(): void
mouse?(event: CanvasMouseEvent, arg1: number[], node: LGraphNode): boolean
draw?(ctx: CanvasRenderingContext2D, node: LGraphNode, widget_width: number, y: number, H: number): void
computeSize?(width: number): Size
}

View File

@@ -5,7 +5,7 @@ import dts from 'vite-plugin-dts'
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/litegraph.js'),
entry: path.resolve(__dirname, 'src/litegraph'),
name: 'litegraph.js',
fileName: (format) => `litegraph.${format}.js`,
formats: ['es', 'umd']
@@ -21,4 +21,9 @@ export default defineConfig({
outDir: 'dist',
}),
],
resolve: {
alias: {
'@': '/src'
}
}
})