mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-01 11:42:06 +00:00
Revert branch to cb6e80a645 (#257)
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 80
|
||||
"singleQuote": false,
|
||||
"semi": true,
|
||||
"tabWidth": 2
|
||||
}
|
||||
@@ -23,7 +23,6 @@
|
||||
"build": "tsc && vite build",
|
||||
"dev": "vite",
|
||||
"preview": "vite preview",
|
||||
"watch": "vite build --watch",
|
||||
"release": "node scripts/release.js",
|
||||
"test": "jest",
|
||||
"deprecated-test:allVersions": "./utils/test.sh",
|
||||
|
||||
@@ -210,7 +210,7 @@
|
||||
.litegraph.lite-search-item.not_in_filter{
|
||||
/*background-color: rgba(50, 50, 50, 0.5);*/
|
||||
/*color: #999;*/
|
||||
color: #b99;
|
||||
color: #B99;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@
|
||||
margin-top: -150px;
|
||||
margin-left: -200px;
|
||||
|
||||
background-color: #2a2a2a;
|
||||
background-color: #2A2A2A;
|
||||
|
||||
min-width: 400px;
|
||||
min-height: 200px;
|
||||
@@ -289,18 +289,12 @@
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-header {
|
||||
color: #aaa;
|
||||
color: #AAA;
|
||||
border-bottom: 1px solid #161616;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-header {
|
||||
height: 40px;
|
||||
}
|
||||
.litegraph .dialog .dialog-footer {
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #1a1a1a;
|
||||
}
|
||||
.litegraph .dialog .dialog-header { height: 40px; }
|
||||
.litegraph .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;}
|
||||
|
||||
.litegraph .dialog .dialog-header .dialog-title {
|
||||
font: 20px "Arial";
|
||||
@@ -309,13 +303,12 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-content,
|
||||
.litegraph .dialog .dialog-alt-content {
|
||||
.litegraph .dialog .dialog-content, .litegraph .dialog .dialog-alt-content {
|
||||
height: calc(100% - 90px);
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
display: inline-block;
|
||||
color: #aaa;
|
||||
color: #AAA;
|
||||
/*background-color: black;*/
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -384,8 +377,8 @@
|
||||
.litegraph .dialog .property_value {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
color: #aaa;
|
||||
background-color: #1a1a1a;
|
||||
color: #AAA;
|
||||
background-color: #1A1A1A;
|
||||
/*width: calc( 100% - 122px );*/
|
||||
max-width: calc( 100% - 162px );
|
||||
min-width: 200px;
|
||||
@@ -404,16 +397,16 @@
|
||||
|
||||
.litegraph .dialog .property.boolean .property_value {
|
||||
padding-right: 30px;
|
||||
color: #a88;
|
||||
color: #A88;
|
||||
/*width: auto;
|
||||
float: right;*/
|
||||
}
|
||||
|
||||
.litegraph .dialog .property.boolean.bool-on .property_name{
|
||||
color: #8a8;
|
||||
color: #8A8;
|
||||
}
|
||||
.litegraph .dialog .property.boolean.bool-on .property_value{
|
||||
color: #8a8;
|
||||
color: #8A8;
|
||||
}
|
||||
|
||||
.litegraph .dialog .btn {
|
||||
@@ -427,11 +420,11 @@
|
||||
|
||||
.litegraph .dialog .btn:hover {
|
||||
background-color: #111;
|
||||
color: #fff;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.litegraph .dialog .btn.delete:hover {
|
||||
background-color: #f33;
|
||||
background-color: #F33;
|
||||
color: black;
|
||||
}
|
||||
|
||||
@@ -467,7 +460,7 @@
|
||||
.litegraph .subgraph_property input {
|
||||
width: 140px;
|
||||
color: #999;
|
||||
background-color: #1a1a1a;
|
||||
background-color: #1A1A1A;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
margin-right: 10px;
|
||||
@@ -697,3 +690,4 @@
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { IContextMenuOptions, IContextMenuValue } from './interfaces'
|
||||
import { LiteGraph } from './litegraph'
|
||||
import type { IContextMenuOptions, IContextMenuValue } from "./interfaces"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
|
||||
interface ContextMenuDivElement extends HTMLDivElement {
|
||||
value?: IContextMenuValue | string
|
||||
@@ -39,56 +39,52 @@ export class ContextMenu {
|
||||
const parent = options.parentMenu
|
||||
if (parent) {
|
||||
if (!(parent instanceof ContextMenu)) {
|
||||
console.error('parentMenu must be of class ContextMenu, ignoring it')
|
||||
console.error("parentMenu must be of class ContextMenu, ignoring it")
|
||||
options.parentMenu = null
|
||||
} else {
|
||||
this.parentMenu = parent
|
||||
this.parentMenu.lock = true
|
||||
this.parentMenu.current_submenu = this
|
||||
}
|
||||
if (parent.options?.className === 'dark') {
|
||||
options.className = 'dark'
|
||||
if (parent.options?.className === "dark") {
|
||||
options.className = "dark"
|
||||
}
|
||||
}
|
||||
|
||||
//use strings because comparing classes between windows doesnt work
|
||||
const eventClass = options.event ? options.event.constructor.name : null
|
||||
if (
|
||||
eventClass !== 'MouseEvent' &&
|
||||
eventClass !== 'CustomEvent' &&
|
||||
eventClass !== 'PointerEvent'
|
||||
) {
|
||||
console.error(
|
||||
`Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. (${eventClass})`,
|
||||
)
|
||||
const eventClass = options.event
|
||||
? options.event.constructor.name
|
||||
: null
|
||||
if (eventClass !== "MouseEvent" &&
|
||||
eventClass !== "CustomEvent" &&
|
||||
eventClass !== "PointerEvent") {
|
||||
console.error(`Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. (${eventClass})`)
|
||||
options.event = null
|
||||
}
|
||||
|
||||
const root: ContextMenuDivElement = document.createElement('div')
|
||||
let classes = 'litegraph litecontextmenu litemenubar-panel'
|
||||
if (options.className) classes += ' ' + options.className
|
||||
const root: ContextMenuDivElement = document.createElement("div")
|
||||
let classes = "litegraph litecontextmenu litemenubar-panel"
|
||||
if (options.className) classes += " " + options.className
|
||||
root.className = classes
|
||||
root.style.minWidth = '100'
|
||||
root.style.minHeight = '100'
|
||||
root.style.minWidth = "100"
|
||||
root.style.minHeight = "100"
|
||||
// TODO: Fix use of timer in place of events
|
||||
root.style.pointerEvents = 'none'
|
||||
root.style.pointerEvents = "none"
|
||||
setTimeout(function () {
|
||||
root.style.pointerEvents = 'auto'
|
||||
root.style.pointerEvents = "auto"
|
||||
}, 100) //delay so the mouse up event is not caught by this element
|
||||
|
||||
//this prevents the default context browser menu to open in case this menu was created when pressing right button
|
||||
LiteGraph.pointerListenerAdd(
|
||||
root,
|
||||
'up',
|
||||
LiteGraph.pointerListenerAdd(root, "up",
|
||||
function (e: MouseEvent) {
|
||||
//console.log("pointerevents: ContextMenu up root prevent");
|
||||
e.preventDefault()
|
||||
return true
|
||||
},
|
||||
true,
|
||||
true
|
||||
)
|
||||
root.addEventListener(
|
||||
'contextmenu',
|
||||
"contextmenu",
|
||||
function (e: MouseEvent) {
|
||||
//right button
|
||||
if (e.button != 2) return false
|
||||
@@ -98,9 +94,7 @@ export class ContextMenu {
|
||||
true
|
||||
)
|
||||
|
||||
LiteGraph.pointerListenerAdd(
|
||||
root,
|
||||
'down',
|
||||
LiteGraph.pointerListenerAdd(root, "down",
|
||||
(e: MouseEvent) => {
|
||||
//console.log("pointerevents: ContextMenu down");
|
||||
if (e.button == 2) {
|
||||
@@ -114,7 +108,8 @@ export class ContextMenu {
|
||||
|
||||
function on_mouse_wheel(e: WheelEvent) {
|
||||
const pos = parseInt(root.style.top)
|
||||
root.style.top = (pos + e.deltaY * options.scroll_speed).toFixed() + 'px'
|
||||
root.style.top =
|
||||
(pos + e.deltaY * options.scroll_speed).toFixed() + "px"
|
||||
e.preventDefault()
|
||||
return true
|
||||
}
|
||||
@@ -123,14 +118,14 @@ export class ContextMenu {
|
||||
options.scroll_speed = 0.1
|
||||
}
|
||||
|
||||
root.addEventListener('wheel', on_mouse_wheel, true)
|
||||
root.addEventListener("wheel", on_mouse_wheel, true)
|
||||
|
||||
this.root = root
|
||||
|
||||
//title
|
||||
if (options.title) {
|
||||
const element = document.createElement('div')
|
||||
element.className = 'litemenu-title'
|
||||
const element = document.createElement("div")
|
||||
element.className = "litemenu-title"
|
||||
element.innerHTML = options.title
|
||||
root.appendChild(element)
|
||||
}
|
||||
@@ -140,16 +135,16 @@ export class ContextMenu {
|
||||
const value = values[i]
|
||||
let name = Array.isArray(values) ? value : String(i)
|
||||
|
||||
if (typeof name !== 'string') {
|
||||
if (typeof name !== "string") {
|
||||
name = name != null
|
||||
? (name.content === undefined ? String(name) : name.content)
|
||||
: (name as null | undefined)
|
||||
? name.content === undefined ? String(name) : name.content
|
||||
: name as null | undefined
|
||||
}
|
||||
|
||||
this.addItem(name, value, options)
|
||||
}
|
||||
|
||||
LiteGraph.pointerListenerAdd(root, 'enter', function () {
|
||||
LiteGraph.pointerListenerAdd(root, "enter", function () {
|
||||
if (root.closing_timer) {
|
||||
clearTimeout(root.closing_timer)
|
||||
}
|
||||
@@ -159,11 +154,10 @@ export class ContextMenu {
|
||||
const ownerDocument = (options.event?.target as Node).ownerDocument
|
||||
const root_document = ownerDocument || document
|
||||
|
||||
if (root_document.fullscreenElement) {
|
||||
if (root_document.fullscreenElement)
|
||||
root_document.fullscreenElement.appendChild(root)
|
||||
} else {
|
||||
else
|
||||
root_document.body.appendChild(root)
|
||||
}
|
||||
|
||||
//compute best position
|
||||
let left = options.left || 0
|
||||
@@ -181,80 +175,74 @@ export class ContextMenu {
|
||||
const body_rect = document.body.getBoundingClientRect()
|
||||
const root_rect = root.getBoundingClientRect()
|
||||
if (body_rect.height == 0)
|
||||
console.error('document.body height is 0. That is dangerous, set html,body { height: 100%; }')
|
||||
console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }")
|
||||
|
||||
if (body_rect.width && left > body_rect.width - root_rect.width - 10) {
|
||||
if (body_rect.width && left > body_rect.width - root_rect.width - 10)
|
||||
left = body_rect.width - root_rect.width - 10
|
||||
}
|
||||
|
||||
if (body_rect.height && top > body_rect.height - root_rect.height - 10) {
|
||||
if (body_rect.height && top > body_rect.height - root_rect.height - 10)
|
||||
top = body_rect.height - root_rect.height - 10
|
||||
}
|
||||
}
|
||||
|
||||
root.style.left = left + 'px'
|
||||
root.style.top = top + 'px'
|
||||
root.style.left = left + "px"
|
||||
root.style.top = top + "px"
|
||||
|
||||
if (options.scale) {
|
||||
if (options.scale)
|
||||
root.style.transform = `scale(${options.scale})`
|
||||
}
|
||||
}
|
||||
|
||||
addItem(
|
||||
name: string,
|
||||
value: IContextMenuValue | string,
|
||||
options: IContextMenuOptions,
|
||||
): HTMLElement {
|
||||
addItem(name: string, value: IContextMenuValue | string, options: IContextMenuOptions): HTMLElement {
|
||||
options ||= {}
|
||||
|
||||
const element: ContextMenuDivElement = document.createElement('div')
|
||||
element.className = 'litemenu-entry submenu'
|
||||
const element: ContextMenuDivElement = document.createElement("div")
|
||||
element.className = "litemenu-entry submenu"
|
||||
|
||||
let disabled = false
|
||||
|
||||
if (value === null) {
|
||||
element.classList.add('separator')
|
||||
element.classList.add("separator")
|
||||
} else {
|
||||
if (typeof value === 'string') {
|
||||
if (typeof value === "string") {
|
||||
element.innerHTML = name
|
||||
} else {
|
||||
element.innerHTML = value?.title ?? name
|
||||
|
||||
if (value.disabled) {
|
||||
disabled = true
|
||||
element.classList.add('disabled')
|
||||
element.setAttribute('aria-disabled', 'true')
|
||||
element.classList.add("disabled")
|
||||
element.setAttribute("aria-disabled", "true")
|
||||
}
|
||||
if (value.submenu || value.has_submenu) {
|
||||
element.classList.add('has_submenu')
|
||||
element.setAttribute('aria-haspopup', 'true')
|
||||
element.setAttribute('aria-expanded', 'false')
|
||||
element.classList.add("has_submenu")
|
||||
element.setAttribute("aria-haspopup", "true")
|
||||
element.setAttribute("aria-expanded", "false")
|
||||
}
|
||||
if (value.className) element.className += ' ' + value.className
|
||||
if (value.className)
|
||||
element.className += " " + value.className
|
||||
}
|
||||
element.value = value
|
||||
element.setAttribute('role', 'menuitem')
|
||||
element.setAttribute("role", "menuitem")
|
||||
|
||||
if (typeof value === 'function') {
|
||||
element.dataset['value'] = name
|
||||
if (typeof value === "function") {
|
||||
element.dataset["value"] = name
|
||||
element.onclick_callback = value
|
||||
} else {
|
||||
element.dataset['value'] = String(value)
|
||||
element.dataset["value"] = String(value)
|
||||
}
|
||||
}
|
||||
|
||||
this.root.appendChild(element)
|
||||
if (!disabled) element.addEventListener('click', inner_onclick)
|
||||
if (!disabled && options.autoopen) LiteGraph.pointerListenerAdd(element, 'enter', inner_over)
|
||||
if (!disabled) element.addEventListener("click", inner_onclick)
|
||||
if (!disabled && options.autoopen)
|
||||
LiteGraph.pointerListenerAdd(element, "enter", inner_over)
|
||||
|
||||
const setAriaExpanded = () => {
|
||||
const entries = this.root.querySelectorAll('div.litemenu-entry.has_submenu')
|
||||
const entries = this.root.querySelectorAll("div.litemenu-entry.has_submenu")
|
||||
if (entries) {
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
entries[i].setAttribute('aria-expanded', 'false')
|
||||
entries[i].setAttribute("aria-expanded", "false")
|
||||
}
|
||||
}
|
||||
element.setAttribute('aria-expanded', 'true')
|
||||
element.setAttribute("aria-expanded", "true")
|
||||
}
|
||||
|
||||
function inner_over(this: ContextMenuDivElement, e: MouseEvent) {
|
||||
@@ -273,24 +261,40 @@ export class ContextMenu {
|
||||
let close_parent = true
|
||||
|
||||
that.current_submenu?.close(e)
|
||||
if ((value as IContextMenuValue)?.has_submenu || (value as IContextMenuValue)?.submenu)
|
||||
setAriaExpanded()
|
||||
if ((value as IContextMenuValue)?.has_submenu || (value as IContextMenuValue)?.submenu) setAriaExpanded()
|
||||
|
||||
//global callback
|
||||
if (options.callback) {
|
||||
const r = options.callback.call(this, value, options, e, that, options.node)
|
||||
const r = options.callback.call(
|
||||
this,
|
||||
value,
|
||||
options,
|
||||
e,
|
||||
that,
|
||||
options.node
|
||||
)
|
||||
if (r === true) close_parent = false
|
||||
}
|
||||
|
||||
//special cases
|
||||
if (typeof value === 'object') {
|
||||
if (value.callback && !options.ignore_item_callbacks && value.disabled !== true) {
|
||||
if (typeof value === "object") {
|
||||
if (value.callback &&
|
||||
!options.ignore_item_callbacks &&
|
||||
value.disabled !== true) {
|
||||
//item callback
|
||||
const r = value.callback.call(this, value, options, e, that, options.extra)
|
||||
const r = value.callback.call(
|
||||
this,
|
||||
value,
|
||||
options,
|
||||
e,
|
||||
that,
|
||||
options.extra
|
||||
)
|
||||
if (r === true) close_parent = false
|
||||
}
|
||||
if (value.submenu) {
|
||||
if (!value.submenu.options) throw 'ContextMenu submenu needs options'
|
||||
if (!value.submenu.options)
|
||||
throw "ContextMenu submenu needs options"
|
||||
|
||||
new that.constructor(value.submenu.options, {
|
||||
callback: value.submenu.callback,
|
||||
@@ -299,13 +303,14 @@ export class ContextMenu {
|
||||
ignore_item_callbacks: value.submenu.ignore_item_callbacks,
|
||||
title: value.submenu.title,
|
||||
extra: value.submenu.extra,
|
||||
autoopen: options.autoopen,
|
||||
autoopen: options.autoopen
|
||||
})
|
||||
close_parent = false
|
||||
}
|
||||
}
|
||||
|
||||
if (close_parent && !that.lock) that.close()
|
||||
if (close_parent && !that.lock)
|
||||
that.close()
|
||||
}
|
||||
|
||||
return element
|
||||
@@ -318,25 +323,20 @@ export class ContextMenu {
|
||||
this.parentMenu.current_submenu = null
|
||||
if (e === undefined) {
|
||||
this.parentMenu.close()
|
||||
} else if (e && !ContextMenu.isCursorOverElement(e, this.parentMenu.root)) {
|
||||
ContextMenu.trigger(this.parentMenu.root, LiteGraph.pointerevents_method + 'leave', e)
|
||||
} else if (e &&
|
||||
!ContextMenu.isCursorOverElement(e, this.parentMenu.root)) {
|
||||
ContextMenu.trigger(this.parentMenu.root, LiteGraph.pointerevents_method + "leave", e)
|
||||
}
|
||||
}
|
||||
this.current_submenu?.close(e, true)
|
||||
|
||||
if (this.root.closing_timer) {
|
||||
if (this.root.closing_timer)
|
||||
clearTimeout(this.root.closing_timer)
|
||||
}
|
||||
}
|
||||
|
||||
//this code is used to trigger events easily (used in the context menu mouseleave
|
||||
static trigger(
|
||||
element: HTMLDivElement,
|
||||
event_name: string,
|
||||
params: MouseEvent,
|
||||
origin?: unknown,
|
||||
): CustomEvent {
|
||||
const evt = document.createEvent('CustomEvent')
|
||||
static trigger(element: HTMLDivElement, event_name: string, params: MouseEvent, origin?: unknown): CustomEvent {
|
||||
const evt = document.createEvent("CustomEvent")
|
||||
evt.initCustomEvent(event_name, true, true, params) //canBubble, cancelable, detail
|
||||
// @ts-expect-error
|
||||
evt.srcElement = origin
|
||||
@@ -349,11 +349,15 @@ export class ContextMenu {
|
||||
|
||||
//returns the top most menu
|
||||
getTopMenu(): ContextMenu {
|
||||
return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this
|
||||
return this.options.parentMenu
|
||||
? this.options.parentMenu.getTopMenu()
|
||||
: this
|
||||
}
|
||||
|
||||
getFirstEvent(): MouseEvent {
|
||||
return this.options.parentMenu ? this.options.parentMenu.getFirstEvent() : this.options.event
|
||||
return this.options.parentMenu
|
||||
? this.options.parentMenu.getFirstEvent()
|
||||
: this.options.event
|
||||
}
|
||||
|
||||
static isCursorOverElement(event: MouseEvent, element: HTMLDivElement): boolean {
|
||||
@@ -362,12 +366,10 @@ export class ContextMenu {
|
||||
const rect = element.getBoundingClientRect()
|
||||
if (!rect) return false
|
||||
|
||||
if (
|
||||
top > rect.top &&
|
||||
if (top > rect.top &&
|
||||
top < rect.top + rect.height &&
|
||||
left > rect.left &&
|
||||
left < rect.left + rect.width
|
||||
) {
|
||||
left < rect.left + rect.width) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Point, Rect } from './interfaces'
|
||||
import { clamp, LGraphCanvas } from './litegraph'
|
||||
import { distance } from './measure'
|
||||
import type { Point, Rect } from "./interfaces"
|
||||
import { clamp, LGraphCanvas } from "./litegraph"
|
||||
import { distance } from "./measure"
|
||||
|
||||
//used by some widgets to render a curve editor
|
||||
|
||||
@@ -23,48 +23,46 @@ export class CurveEditor {
|
||||
}
|
||||
|
||||
static sampleCurve(f: number, points: Point[]): number {
|
||||
if (!points) return
|
||||
if (!points)
|
||||
return
|
||||
for (let i = 0; i < points.length - 1; ++i) {
|
||||
const p = points[i]
|
||||
const pn = points[i + 1]
|
||||
if (pn[0] < f) continue
|
||||
const r = pn[0] - p[0]
|
||||
if (Math.abs(r) < 0.00001) return p[1]
|
||||
if (pn[0] < f)
|
||||
continue
|
||||
const r = (pn[0] - p[0])
|
||||
if (Math.abs(r) < 0.00001)
|
||||
return p[1]
|
||||
const local_f = (f - p[0]) / r
|
||||
return p[1] * (1.0 - local_f) + pn[1] * local_f
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
draw(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
size: Rect,
|
||||
graphcanvas?: LGraphCanvas,
|
||||
background_color?: string,
|
||||
line_color?: string,
|
||||
inactive = false,
|
||||
): void {
|
||||
draw(ctx: CanvasRenderingContext2D, size: Rect, graphcanvas?: LGraphCanvas, background_color?: string, line_color?: string, inactive = false): void {
|
||||
const points = this.points
|
||||
if (!points) return
|
||||
if (!points)
|
||||
return
|
||||
this.size = size
|
||||
const w = size[0] - this.margin * 2
|
||||
const h = size[1] - this.margin * 2
|
||||
|
||||
line_color = line_color || '#666'
|
||||
line_color = line_color || "#666"
|
||||
|
||||
ctx.save()
|
||||
ctx.translate(this.margin, this.margin)
|
||||
|
||||
if (background_color) {
|
||||
ctx.fillStyle = '#111'
|
||||
ctx.fillStyle = "#111"
|
||||
ctx.fillRect(0, 0, w, h)
|
||||
ctx.fillStyle = '#222'
|
||||
ctx.fillStyle = "#222"
|
||||
ctx.fillRect(w * 0.5, 0, 1, h)
|
||||
ctx.strokeStyle = '#333'
|
||||
ctx.strokeStyle = "#333"
|
||||
ctx.strokeRect(0, 0, w, h)
|
||||
}
|
||||
ctx.strokeStyle = line_color
|
||||
if (inactive) ctx.globalAlpha = 0.5
|
||||
if (inactive)
|
||||
ctx.globalAlpha = 0.5
|
||||
ctx.beginPath()
|
||||
for (let i = 0; i < points.length; ++i) {
|
||||
const p = points[i]
|
||||
@@ -75,7 +73,7 @@ export class CurveEditor {
|
||||
if (!inactive)
|
||||
for (let i = 0; i < points.length; ++i) {
|
||||
const p = points[i]
|
||||
ctx.fillStyle = this.selected == i ? '#FFF' : this.nearest == i ? '#DDD' : '#AAA'
|
||||
ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA")
|
||||
ctx.beginPath()
|
||||
ctx.arc(p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
@@ -86,8 +84,10 @@ export class CurveEditor {
|
||||
//localpos is mouse in curve editor space
|
||||
onMouseDown(localpos: Point, graphcanvas: LGraphCanvas): boolean {
|
||||
const points = this.points
|
||||
if (!points) return
|
||||
if (localpos[1] < 0) return
|
||||
if (!points)
|
||||
return
|
||||
if (localpos[1] < 0)
|
||||
return
|
||||
|
||||
//this.captureInput(true);
|
||||
const w = this.size[0] - this.margin * 2
|
||||
@@ -102,47 +102,40 @@ export class CurveEditor {
|
||||
if (this.selected == -1) {
|
||||
const point: Point = [x / w, 1 - y / h]
|
||||
points.push(point)
|
||||
points.sort(function (a, b) {
|
||||
return a[0] - b[0]
|
||||
})
|
||||
points.sort(function (a, b) { return a[0] - b[0] })
|
||||
this.selected = points.indexOf(point)
|
||||
this.must_update = true
|
||||
}
|
||||
if (this.selected != -1) return true
|
||||
if (this.selected != -1)
|
||||
return true
|
||||
}
|
||||
|
||||
onMouseMove(localpos: Point, graphcanvas: LGraphCanvas): void {
|
||||
const points = this.points
|
||||
if (!points) return
|
||||
if (!points)
|
||||
return
|
||||
const s = this.selected
|
||||
if (s < 0) return
|
||||
if (s < 0)
|
||||
return
|
||||
const x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2)
|
||||
const y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2)
|
||||
const curvepos: Point = [localpos[0] - this.margin, localpos[1] - this.margin]
|
||||
const curvepos: Point = [(localpos[0] - this.margin), (localpos[1] - this.margin)]
|
||||
const max_dist = 30 / graphcanvas.ds.scale
|
||||
this._nearest = this.getCloserPoint(curvepos, max_dist)
|
||||
const point = points[s]
|
||||
if (point) {
|
||||
const is_edge_point = s == 0 || s == points.length - 1
|
||||
if (
|
||||
!is_edge_point &&
|
||||
(localpos[0] < -10 ||
|
||||
localpos[0] > this.size[0] + 10 ||
|
||||
localpos[1] < -10 ||
|
||||
localpos[1] > this.size[1] + 10)
|
||||
) {
|
||||
if (!is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10)) {
|
||||
points.splice(s, 1)
|
||||
this.selected = -1
|
||||
return
|
||||
}
|
||||
if (!is_edge_point)
|
||||
//not edges
|
||||
if (!is_edge_point) //not edges
|
||||
point[0] = clamp(x, 0, 1)
|
||||
else point[0] = s == 0 ? 0 : 1
|
||||
else
|
||||
point[0] = s == 0 ? 0 : 1
|
||||
point[1] = 1.0 - clamp(y, 0, 1)
|
||||
points.sort(function (a, b) {
|
||||
return a[0] - b[0]
|
||||
})
|
||||
points.sort(function (a, b) { return a[0] - b[0] })
|
||||
this.selected = points.indexOf(point)
|
||||
this.must_update = true
|
||||
}
|
||||
@@ -156,10 +149,11 @@ export class CurveEditor {
|
||||
|
||||
getCloserPoint(pos: Point, max_dist: number): number {
|
||||
const points = this.points
|
||||
if (!points) return -1
|
||||
if (!points)
|
||||
return -1
|
||||
max_dist = max_dist || 30
|
||||
const w = this.size[0] - this.margin * 2
|
||||
const h = this.size[1] - this.margin * 2
|
||||
const w = (this.size[0] - this.margin * 2)
|
||||
const h = (this.size[1] - this.margin * 2)
|
||||
const num = points.length
|
||||
const p2: Point = [0, 0]
|
||||
let min_dist = 1000000
|
||||
@@ -169,7 +163,8 @@ export class CurveEditor {
|
||||
p2[0] = p[0] * w
|
||||
p2[1] = (1.0 - p[1]) * h
|
||||
const dist = distance(pos, p2)
|
||||
if (dist > min_dist || dist > max_dist) continue
|
||||
if (dist > min_dist || dist > max_dist)
|
||||
continue
|
||||
closest = i
|
||||
min_dist = dist
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Point, Rect, Rect32 } from './interfaces'
|
||||
import type { CanvasMouseEvent } from './types/events'
|
||||
import { LiteGraph } from './litegraph'
|
||||
import type { Point, Rect, Rect32 } from "./interfaces"
|
||||
import type { CanvasMouseEvent } from "./types/events"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
|
||||
export class DragAndScale {
|
||||
/** Maximum scale (zoom in) */
|
||||
@@ -46,12 +46,16 @@ export class DragAndScale {
|
||||
|
||||
this._binded_mouse_callback = this.onMouse.bind(this)
|
||||
|
||||
LiteGraph.pointerListenerAdd(element, 'down', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(element, 'move', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(element, 'up', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(element, "down", this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(element, "move", this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(element, "up", this._binded_mouse_callback)
|
||||
|
||||
element.addEventListener('mousewheel', this._binded_mouse_callback, false)
|
||||
element.addEventListener('wheel', this._binded_mouse_callback, false)
|
||||
element.addEventListener(
|
||||
"mousewheel",
|
||||
this._binded_mouse_callback,
|
||||
false
|
||||
)
|
||||
element.addEventListener("wheel", this._binded_mouse_callback, false)
|
||||
}
|
||||
|
||||
computeVisibleArea(viewport: Rect): void {
|
||||
@@ -94,25 +98,19 @@ export class DragAndScale {
|
||||
e.canvasy = y
|
||||
e.dragging = this.dragging
|
||||
|
||||
const is_inside =
|
||||
!this.viewport ||
|
||||
(this.viewport &&
|
||||
x >= this.viewport[0] &&
|
||||
x < this.viewport[0] + this.viewport[2] &&
|
||||
y >= this.viewport[1] &&
|
||||
y < this.viewport[1] + this.viewport[3])
|
||||
const is_inside = !this.viewport || (this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]))
|
||||
|
||||
let ignore = false
|
||||
if (this.onmouse) {
|
||||
ignore = this.onmouse(e)
|
||||
}
|
||||
|
||||
if (e.type == LiteGraph.pointerevents_method + 'down' && is_inside) {
|
||||
if (e.type == LiteGraph.pointerevents_method + "down" && is_inside) {
|
||||
this.dragging = true
|
||||
LiteGraph.pointerListenerRemove(canvas, 'move', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(document, 'move', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(document, 'up', this._binded_mouse_callback)
|
||||
} else if (e.type == LiteGraph.pointerevents_method + 'move') {
|
||||
LiteGraph.pointerListenerRemove(canvas, "move", this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(document, "move", this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(document, "up", this._binded_mouse_callback)
|
||||
} else if (e.type == LiteGraph.pointerevents_method + "move") {
|
||||
if (!ignore) {
|
||||
const deltax = x - this.last_mouse[0]
|
||||
const deltay = y - this.last_mouse[1]
|
||||
@@ -120,27 +118,27 @@ export class DragAndScale {
|
||||
this.mouseDrag(deltax, deltay)
|
||||
}
|
||||
}
|
||||
} else if (e.type == LiteGraph.pointerevents_method + 'up') {
|
||||
} else if (e.type == LiteGraph.pointerevents_method + "up") {
|
||||
this.dragging = false
|
||||
LiteGraph.pointerListenerRemove(document, 'move', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerRemove(document, 'up', this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(canvas, 'move', this._binded_mouse_callback)
|
||||
} else if (
|
||||
is_inside &&
|
||||
(e.type == 'mousewheel' || e.type == 'wheel' || e.type == 'DOMMouseScroll')
|
||||
) {
|
||||
LiteGraph.pointerListenerRemove(document, "move", this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerRemove(document, "up", this._binded_mouse_callback)
|
||||
LiteGraph.pointerListenerAdd(canvas, "move", this._binded_mouse_callback)
|
||||
} else if (is_inside &&
|
||||
(e.type == "mousewheel" ||
|
||||
e.type == "wheel" ||
|
||||
e.type == "DOMMouseScroll")) {
|
||||
// @ts-expect-error Deprecated
|
||||
e.eventType = 'mousewheel'
|
||||
e.eventType = "mousewheel"
|
||||
// @ts-expect-error Deprecated
|
||||
if (e.type == 'wheel') e.wheel = -e.deltaY
|
||||
if (e.type == "wheel") e.wheel = -e.deltaY
|
||||
// @ts-expect-error Deprecated
|
||||
else e.wheel = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60
|
||||
|
||||
//from stack overflow
|
||||
// @ts-expect-error Deprecated
|
||||
e.delta = e.wheelDelta
|
||||
? // @ts-expect-error Deprecated
|
||||
e.wheelDelta / 40
|
||||
// @ts-expect-error Deprecated
|
||||
? e.wheelDelta / 40
|
||||
: e.deltaY
|
||||
? -e.deltaY / 3
|
||||
: 0
|
||||
@@ -164,7 +162,10 @@ export class DragAndScale {
|
||||
}
|
||||
|
||||
convertOffsetToCanvas(pos: Point): Point {
|
||||
return [(pos[0] + this.offset[0]) * this.scale, (pos[1] + this.offset[1]) * this.scale]
|
||||
return [
|
||||
(pos[0] + this.offset[0]) * this.scale,
|
||||
(pos[1] + this.offset[1]) * this.scale
|
||||
]
|
||||
}
|
||||
|
||||
convertCanvasToOffset(pos: Point, out?: Point): Point {
|
||||
@@ -195,13 +196,19 @@ export class DragAndScale {
|
||||
const rect = this.element.getBoundingClientRect()
|
||||
if (!rect) return
|
||||
|
||||
zooming_center = zooming_center || [rect.width * 0.5, rect.height * 0.5]
|
||||
zooming_center = zooming_center || [
|
||||
rect.width * 0.5,
|
||||
rect.height * 0.5
|
||||
]
|
||||
const center = this.convertCanvasToOffset(zooming_center)
|
||||
this.scale = value
|
||||
if (Math.abs(this.scale - 1) < 0.01) this.scale = 1
|
||||
|
||||
const new_center = this.convertCanvasToOffset(zooming_center)
|
||||
const delta_offset = [new_center[0] - center[0], new_center[1] - center[1]]
|
||||
const delta_offset = [
|
||||
new_center[0] - center[0],
|
||||
new_center[1] - center[1]
|
||||
]
|
||||
|
||||
this.offset[0] += delta_offset[0]
|
||||
this.offset[1] += delta_offset[1]
|
||||
|
||||
228
src/LGraph.ts
228
src/LGraph.ts
@@ -1,12 +1,12 @@
|
||||
import type { Dictionary, IContextMenuValue, ISlotType, MethodNames, Point } from './interfaces'
|
||||
import type { ISerialisedGraph } from '@/types/serialisation'
|
||||
import { LGraphEventMode, TitleMode } from './types/globalEnums'
|
||||
import { LiteGraph } from './litegraph'
|
||||
import { LGraphCanvas } from './LGraphCanvas'
|
||||
import { LGraphGroup } from './LGraphGroup'
|
||||
import { type NodeId, LGraphNode } from './LGraphNode'
|
||||
import { type LinkId, LLink, type SerialisedLLinkArray } from './LLink'
|
||||
import { MapProxyHandler } from './MapProxyHandler'
|
||||
import type { Dictionary, IContextMenuValue, ISlotType, MethodNames, Point } from "./interfaces"
|
||||
import type { ISerialisedGraph } from "@/types/serialisation"
|
||||
import { LGraphEventMode, TitleMode } from "./types/globalEnums"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
import { LGraphCanvas } from "./LGraphCanvas"
|
||||
import { LGraphGroup } from "./LGraphGroup"
|
||||
import { type NodeId, LGraphNode } from "./LGraphNode"
|
||||
import { type LinkId, LLink, type SerialisedLLinkArray } from "./LLink"
|
||||
import { MapProxyHandler } from "./MapProxyHandler"
|
||||
|
||||
interface IGraphInput {
|
||||
name: string
|
||||
@@ -14,11 +14,7 @@ interface IGraphInput {
|
||||
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]>
|
||||
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 is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.
|
||||
@@ -34,7 +30,7 @@ type ParamsArray<T extends Record<any, any>, K extends MethodNames<T>> = Paramet
|
||||
|
||||
export class LGraph {
|
||||
//default supported types
|
||||
static supported_types = ['number', 'string', 'boolean']
|
||||
static supported_types = ["number", "string", "boolean"]
|
||||
static STATUS_STOPPED = 1
|
||||
static STATUS_RUNNING = 2
|
||||
|
||||
@@ -73,7 +69,7 @@ export class LGraph {
|
||||
last_update_time: number
|
||||
starttime: number
|
||||
catch_errors: boolean
|
||||
execution_timer_id: number | ReturnType<typeof setTimeout> | null
|
||||
execution_timer_id: number | null
|
||||
errors_in_execution: boolean
|
||||
execution_time: number
|
||||
_last_trigger_time?: number
|
||||
@@ -97,7 +93,6 @@ export class LGraph {
|
||||
onExecuteStep?(): void
|
||||
onNodeAdded?(node: LGraphNode): void
|
||||
onNodeRemoved?(node: LGraphNode): void
|
||||
onNodeUpdated?(node: LGraphNode): void
|
||||
onTrigger?(action: string, param: unknown): void
|
||||
onInputRenamed?(old_name: string, name: string): void
|
||||
onInputTypeChanged?(name: string, type: string): void
|
||||
@@ -106,25 +101,19 @@ export class LGraph {
|
||||
onOutputRenamed?(old_name: string, name: string): void
|
||||
onOutputTypeChanged?(name: string, type: string): void
|
||||
onOutputRemoved?(name: string): void
|
||||
onBeforeChange?(graph: LGraph, node?: LGraphNode): void
|
||||
onAfterChange?(graph: LGraph, node?: LGraphNode): 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
|
||||
onNodeConnectionChange?(
|
||||
nodeSlotType: ISlotType,
|
||||
targetNode: LGraphNode,
|
||||
slotIndex: number,
|
||||
sourceNode?: LGraphNode,
|
||||
sourceSlotIndex?: number,
|
||||
): void
|
||||
onNodeConnectionChange?(nodeSlotType: ISlotType, targetNode: LGraphNode, slotIndex: number, sourceNode?: LGraphNode, sourceSlotIndex?: number): void
|
||||
|
||||
private _input_nodes?: LGraphNode[]
|
||||
|
||||
constructor(o?: ISerialisedGraph) {
|
||||
if (LiteGraph.debug) console.log('Graph created')
|
||||
if (LiteGraph.debug) console.log("Graph created")
|
||||
|
||||
/** @see MapProxyHandler */
|
||||
const links = this._links
|
||||
@@ -202,7 +191,7 @@ export class LGraph {
|
||||
//notify canvas to redraw
|
||||
this.change()
|
||||
|
||||
this.sendActionToCanvas('clear')
|
||||
this.sendActionToCanvas("clear")
|
||||
}
|
||||
|
||||
get nodes() {
|
||||
@@ -219,8 +208,9 @@ export class LGraph {
|
||||
*/
|
||||
attachCanvas(graphcanvas: LGraphCanvas): void {
|
||||
if (graphcanvas.constructor != LGraphCanvas)
|
||||
throw 'attachCanvas expects a LGraphCanvas instance'
|
||||
if (graphcanvas.graph != this) graphcanvas.graph?.detachCanvas(graphcanvas)
|
||||
throw "attachCanvas expects a LGraphCanvas instance"
|
||||
if (graphcanvas.graph != this)
|
||||
graphcanvas.graph?.detachCanvas(graphcanvas)
|
||||
|
||||
graphcanvas.graph = this
|
||||
|
||||
@@ -249,7 +239,7 @@ export class LGraph {
|
||||
this.status = LGraph.STATUS_RUNNING
|
||||
|
||||
this.onPlayEvent?.()
|
||||
this.sendEventToAllNodes('onStart')
|
||||
this.sendEventToAllNodes("onStart")
|
||||
|
||||
//launch
|
||||
this.starttime = LiteGraph.getTime()
|
||||
@@ -258,7 +248,7 @@ export class LGraph {
|
||||
const that = this
|
||||
|
||||
//execute once per frame
|
||||
if (interval == 0 && typeof window != 'undefined' && window.requestAnimationFrame) {
|
||||
if (interval == 0 && typeof window != "undefined" && window.requestAnimationFrame) {
|
||||
function on_frame() {
|
||||
if (that.execution_timer_id != -1) return
|
||||
|
||||
@@ -269,9 +259,8 @@ export class LGraph {
|
||||
}
|
||||
this.execution_timer_id = -1
|
||||
on_frame()
|
||||
} else {
|
||||
//execute every 'interval' ms
|
||||
|
||||
} else { //execute every 'interval' ms
|
||||
// @ts-expect-error
|
||||
this.execution_timer_id = setInterval(function () {
|
||||
//execute
|
||||
that.onBeforeStep?.()
|
||||
@@ -297,7 +286,7 @@ export class LGraph {
|
||||
this.execution_timer_id = null
|
||||
}
|
||||
|
||||
this.sendEventToAllNodes('onStop')
|
||||
this.sendEventToAllNodes("onStop")
|
||||
}
|
||||
/**
|
||||
* Run N steps (cycles) of the graph
|
||||
@@ -311,7 +300,9 @@ export class LGraph {
|
||||
const start = LiteGraph.getTime()
|
||||
this.globaltime = 0.001 * (start - this.starttime)
|
||||
|
||||
const nodes = this._nodes_executable ? this._nodes_executable : this._nodes
|
||||
const nodes = this._nodes_executable
|
||||
? this._nodes_executable
|
||||
: this._nodes
|
||||
if (!nodes) return
|
||||
|
||||
limit = limit || nodes.length
|
||||
@@ -354,7 +345,7 @@ export class LGraph {
|
||||
this.errors_in_execution = true
|
||||
if (LiteGraph.throw_errors) throw err
|
||||
|
||||
if (LiteGraph.debug) console.log('Error during execution: ' + err)
|
||||
if (LiteGraph.debug) console.log("Error during execution: " + err)
|
||||
this.stop()
|
||||
}
|
||||
}
|
||||
@@ -437,7 +428,8 @@ export class LGraph {
|
||||
const output = node.outputs[i]
|
||||
//not connected
|
||||
// TODO: Confirm functionality, clean condition
|
||||
if (output?.links == null || output.links.length == 0) continue
|
||||
if (output?.links == null || output.links.length == 0)
|
||||
continue
|
||||
|
||||
//for every connection
|
||||
for (let j = 0; j < output.links.length; j++) {
|
||||
@@ -475,7 +467,7 @@ export class LGraph {
|
||||
}
|
||||
|
||||
if (L.length != this._nodes.length && LiteGraph.debug)
|
||||
console.warn('something went wrong, nodes missing')
|
||||
console.warn("something went wrong, nodes missing")
|
||||
|
||||
const l = L.length
|
||||
|
||||
@@ -501,7 +493,9 @@ export class LGraph {
|
||||
const Bp = B.constructor.priority || B.priority || 0
|
||||
//if same priority, sort by order
|
||||
|
||||
return Ap == Bp ? A.order - B.order : Ap - Bp
|
||||
return Ap == Bp
|
||||
? A.order - B.order
|
||||
: Ap - Bp
|
||||
})
|
||||
|
||||
//save order number in the node, again...
|
||||
@@ -566,13 +560,13 @@ export class LGraph {
|
||||
let y = margin + LiteGraph.NODE_TITLE_HEIGHT
|
||||
for (let j = 0; j < column.length; ++j) {
|
||||
const node = column[j]
|
||||
node.pos[0] = layout == LiteGraph.VERTICAL_LAYOUT ? y : x
|
||||
node.pos[1] = layout == LiteGraph.VERTICAL_LAYOUT ? x : y
|
||||
const max_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 1 : 0
|
||||
node.pos[0] = (layout == LiteGraph.VERTICAL_LAYOUT) ? y : x
|
||||
node.pos[1] = (layout == LiteGraph.VERTICAL_LAYOUT) ? x : y
|
||||
const max_size_index = (layout == LiteGraph.VERTICAL_LAYOUT) ? 1 : 0
|
||||
if (node.size[max_size_index] > max_size) {
|
||||
max_size = node.size[max_size_index]
|
||||
}
|
||||
const node_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 0 : 1
|
||||
const node_size_index = (layout == LiteGraph.VERTICAL_LAYOUT) ? 0 : 1
|
||||
y += node.size[node_size_index] + margin + LiteGraph.NODE_TITLE_HEIGHT
|
||||
}
|
||||
x += max_size + margin
|
||||
@@ -617,7 +611,7 @@ export class LGraph {
|
||||
const node = nodes[j]
|
||||
|
||||
// @ts-expect-error
|
||||
if (node.constructor === LiteGraph.Subgraph && eventname != 'onExecute') {
|
||||
if (node.constructor === LiteGraph.Subgraph && eventname != "onExecute") {
|
||||
if (node.mode == mode) {
|
||||
// @ts-expect-error Subgraph - not currently in use
|
||||
node.sendEventToAllNodes(eventname, params, mode)
|
||||
@@ -635,10 +629,7 @@ export class LGraph {
|
||||
}
|
||||
}
|
||||
}
|
||||
sendActionToCanvas<T extends MethodNames<LGraphCanvas>>(
|
||||
action: T,
|
||||
params?: ParamsArray<LGraphCanvas, T>,
|
||||
): void {
|
||||
sendActionToCanvas<T extends MethodNames<LGraphCanvas>>(action: T, params?: ParamsArray<LGraphCanvas, T>): void {
|
||||
if (!this.list_of_graphcanvas) return
|
||||
|
||||
for (let i = 0; i < this.list_of_graphcanvas.length; ++i) {
|
||||
@@ -666,21 +657,27 @@ export class LGraph {
|
||||
|
||||
//nodes
|
||||
if (node.id != -1 && this._nodes_by_id[node.id] != null) {
|
||||
console.warn('LiteGraph: there is already a node with this ID, changing it')
|
||||
node.id = LiteGraph.use_uuids ? LiteGraph.uuidv4() : ++this.last_node_id
|
||||
console.warn(
|
||||
"LiteGraph: there is already a node with this ID, changing it"
|
||||
)
|
||||
node.id = LiteGraph.use_uuids
|
||||
? LiteGraph.uuidv4()
|
||||
: ++this.last_node_id
|
||||
}
|
||||
|
||||
if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {
|
||||
throw 'LiteGraph: max number of nodes in a graph reached'
|
||||
throw "LiteGraph: max number of nodes in a graph reached"
|
||||
}
|
||||
|
||||
//give him an id
|
||||
if (LiteGraph.use_uuids) {
|
||||
if (node.id == null || node.id == -1) node.id = LiteGraph.uuidv4()
|
||||
} else {
|
||||
if (node.id == null || node.id == -1)
|
||||
node.id = LiteGraph.uuidv4()
|
||||
}
|
||||
else {
|
||||
if (node.id == null || node.id == -1) {
|
||||
node.id = ++this.last_node_id
|
||||
} else if (typeof node.id === 'number' && this.last_node_id < node.id) {
|
||||
} else if (typeof node.id === "number" && this.last_node_id < node.id) {
|
||||
this.last_node_id = node.id
|
||||
}
|
||||
}
|
||||
@@ -733,7 +730,8 @@ export class LGraph {
|
||||
if (node.inputs) {
|
||||
for (let i = 0; i < node.inputs.length; i++) {
|
||||
const slot = node.inputs[i]
|
||||
if (slot.link != null) node.disconnectInput(i)
|
||||
if (slot.link != null)
|
||||
node.disconnectInput(i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,7 +739,8 @@ export class LGraph {
|
||||
if (node.outputs) {
|
||||
for (let i = 0; i < node.outputs.length; i++) {
|
||||
const slot = node.outputs[i]
|
||||
if (slot.links?.length) node.disconnectOutput(i)
|
||||
if (slot.links?.length)
|
||||
node.disconnectOutput(i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,9 +754,11 @@ export class LGraph {
|
||||
if (this.list_of_graphcanvas) {
|
||||
for (let i = 0; i < this.list_of_graphcanvas.length; ++i) {
|
||||
const canvas = this.list_of_graphcanvas[i]
|
||||
if (canvas.selected_nodes[node.id]) delete canvas.selected_nodes[node.id]
|
||||
if (canvas.selected_nodes[node.id])
|
||||
delete canvas.selected_nodes[node.id]
|
||||
|
||||
if (canvas.node_dragged == node) canvas.node_dragged = null
|
||||
if (canvas.node_dragged == node)
|
||||
canvas.node_dragged = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,7 +771,7 @@ export class LGraph {
|
||||
this.onNodeRemoved?.(node)
|
||||
|
||||
//close panels
|
||||
this.sendActionToCanvas('checkPanels')
|
||||
this.sendActionToCanvas("checkPanels")
|
||||
|
||||
this.setDirtyCanvas(true, true)
|
||||
this.afterChange() //sure? - almost sure is wrong
|
||||
@@ -783,7 +784,9 @@ export class LGraph {
|
||||
* @param {Number} id
|
||||
*/
|
||||
getNodeById(id: NodeId): LGraphNode | null {
|
||||
return id != null ? this._nodes_by_id[id] : null
|
||||
return id != null
|
||||
? this._nodes_by_id[id]
|
||||
: null
|
||||
}
|
||||
/**
|
||||
* Returns a list of nodes that matches a class
|
||||
@@ -795,7 +798,8 @@ export class LGraph {
|
||||
result = result || []
|
||||
result.length = 0
|
||||
for (let i = 0, l = this._nodes.length; i < l; ++i) {
|
||||
if (this._nodes[i].constructor === classObject) result.push(this._nodes[i])
|
||||
if (this._nodes[i].constructor === classObject)
|
||||
result.push(this._nodes[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -809,7 +813,8 @@ export class LGraph {
|
||||
result = result || []
|
||||
result.length = 0
|
||||
for (let i = 0, l = this._nodes.length; i < l; ++i) {
|
||||
if (this._nodes[i].type?.toLowerCase() == matchType) result.push(this._nodes[i])
|
||||
if (this._nodes[i].type?.toLowerCase() == matchType)
|
||||
result.push(this._nodes[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -820,7 +825,8 @@ export class LGraph {
|
||||
*/
|
||||
findNodeByTitle(title: string): LGraphNode | null {
|
||||
for (let i = 0, l = this._nodes.length; i < l; ++i) {
|
||||
if (this._nodes[i].title == title) return this._nodes[i]
|
||||
if (this._nodes[i].title == title)
|
||||
return this._nodes[i]
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -832,7 +838,8 @@ export class LGraph {
|
||||
findNodesByTitle(title: string): LGraphNode[] {
|
||||
const result: LGraphNode[] = []
|
||||
for (let i = 0, l = this._nodes.length; i < l; ++i) {
|
||||
if (this._nodes[i].title == title) result.push(this._nodes[i])
|
||||
if (this._nodes[i].title == title)
|
||||
result.push(this._nodes[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -843,18 +850,14 @@ 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: number,
|
||||
y: number,
|
||||
nodes_list?: LGraphNode[],
|
||||
margin?: number,
|
||||
): LGraphNode | null {
|
||||
getNodeOnPos(x: number, y: number, nodes_list?: LGraphNode[], margin?: number): LGraphNode | null {
|
||||
nodes_list = nodes_list || this._nodes
|
||||
const nRet = null
|
||||
for (let i = nodes_list.length - 1; i >= 0; i--) {
|
||||
const n = nodes_list[i]
|
||||
const skip_title = n.constructor.title_mode == TitleMode.NO_TITLE
|
||||
if (n.isPointInside(x, y, margin, skip_title)) return n
|
||||
if (n.isPointInside(x, y, margin, skip_title))
|
||||
return n
|
||||
}
|
||||
return nRet
|
||||
}
|
||||
@@ -865,7 +868,7 @@ export class LGraph {
|
||||
* @return The group or null
|
||||
*/
|
||||
getGroupOnPos(x: number, y: number, { margin = 2 } = {}): LGraphGroup | undefined {
|
||||
return this._groups.toReversed().find((g) => g.isPointInside(x, y, margin, true))
|
||||
return this._groups.toReversed().find(g => g.isPointInside(x, y, margin, true))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -878,7 +881,7 @@ export class LGraph {
|
||||
const ctor = LiteGraph.registered_node_types[node.type]
|
||||
if (node.constructor == ctor) continue
|
||||
|
||||
console.log('node being replaced by newer version: ' + node.type)
|
||||
console.log("node being replaced by newer version: " + node.type)
|
||||
const newnode = LiteGraph.createNode(node.type)
|
||||
this._nodes[i] = newnode
|
||||
newnode.configure(node.serialize())
|
||||
@@ -895,7 +898,7 @@ export class LGraph {
|
||||
this._input_nodes = this.findNodesByClass(
|
||||
// @ts-expect-error Never impl.
|
||||
LiteGraph.GraphInput,
|
||||
this._input_nodes,
|
||||
this._input_nodes
|
||||
)
|
||||
for (let i = 0; i < this._input_nodes.length; ++i) {
|
||||
const node = this._input_nodes[i]
|
||||
@@ -945,7 +948,9 @@ export class LGraph {
|
||||
*/
|
||||
getInputData(name: string): unknown {
|
||||
const input = this.inputs[name]
|
||||
return input ? input.value : null
|
||||
return input
|
||||
? input.value
|
||||
: null
|
||||
}
|
||||
/**
|
||||
* Changes the name of a global graph input
|
||||
@@ -958,7 +963,7 @@ export class LGraph {
|
||||
if (!this.inputs[old_name]) return false
|
||||
|
||||
if (this.inputs[name]) {
|
||||
console.error('there is already one input with that name')
|
||||
console.error("there is already one input with that name")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -977,10 +982,9 @@ export class LGraph {
|
||||
changeInputType(name: string, type: string): boolean | undefined {
|
||||
if (!this.inputs[name]) return false
|
||||
|
||||
if (
|
||||
this.inputs[name].type &&
|
||||
String(this.inputs[name].type).toLowerCase() == String(type).toLowerCase()
|
||||
) {
|
||||
if (this.inputs[name].type &&
|
||||
String(this.inputs[name].type).toLowerCase() ==
|
||||
String(type).toLowerCase()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1046,7 +1050,7 @@ export class LGraph {
|
||||
if (!this.outputs[old_name]) return false
|
||||
|
||||
if (this.outputs[name]) {
|
||||
console.error('there is already one output with that name')
|
||||
console.error("there is already one output with that name")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1066,10 +1070,9 @@ export class LGraph {
|
||||
changeOutputType(name: string, type: string): boolean | undefined {
|
||||
if (!this.outputs[name]) return false
|
||||
|
||||
if (
|
||||
this.outputs[name].type &&
|
||||
String(this.outputs[name].type).toLowerCase() == String(type).toLowerCase()
|
||||
) {
|
||||
if (this.outputs[name].type &&
|
||||
String(this.outputs[name].type).toLowerCase() ==
|
||||
String(type).toLowerCase()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1109,28 +1112,22 @@ export class LGraph {
|
||||
}
|
||||
}
|
||||
//used for undo, called before any change is made to the graph
|
||||
beforeChange(node?: LGraphNode): void {
|
||||
this.onBeforeChange?.(this, node)
|
||||
this.sendActionToCanvas('onBeforeChange', this)
|
||||
beforeChange(info?: LGraphNode): void {
|
||||
this.onBeforeChange?.(this, info)
|
||||
this.sendActionToCanvas("onBeforeChange", this)
|
||||
}
|
||||
//used to resend actions, called after any change is made to the graph
|
||||
afterChange(node?: LGraphNode): void {
|
||||
this.onAfterChange?.(this, node)
|
||||
this.sendActionToCanvas('onAfterChange', this)
|
||||
afterChange(info?: LGraphNode): void {
|
||||
this.onAfterChange?.(this, info)
|
||||
this.sendActionToCanvas("onAfterChange", this)
|
||||
}
|
||||
|
||||
nodeUpdate(node?: LGraphNode): void {
|
||||
this.onNodeUpdated?.(node)
|
||||
this.sendActionToCanvas('onNodeUpdated', node)
|
||||
}
|
||||
|
||||
connectionChange(node: LGraphNode): void {
|
||||
this.updateExecutionOrder()
|
||||
this.onConnectionChange?.(node)
|
||||
this._version++
|
||||
// TODO: Interface never implemented - any consumers?
|
||||
// @ts-expect-error
|
||||
this.sendActionToCanvas('onConnectionChange')
|
||||
this.sendActionToCanvas("onConnectionChange")
|
||||
}
|
||||
/**
|
||||
* returns if the graph is in live mode
|
||||
@@ -1151,19 +1148,20 @@ export class LGraph {
|
||||
for (const link_info of this._links.values()) {
|
||||
if (!link_info) continue
|
||||
|
||||
if (link_info._last_time) link_info._last_time = 0
|
||||
if (link_info._last_time)
|
||||
link_info._last_time = 0
|
||||
}
|
||||
}
|
||||
/* Called when something visually changed (not the graph!) */
|
||||
change(): void {
|
||||
if (LiteGraph.debug) {
|
||||
console.log('Graph changed')
|
||||
console.log("Graph changed")
|
||||
}
|
||||
this.sendActionToCanvas('setDirty', [true, true])
|
||||
this.sendActionToCanvas("setDirty", [true, true])
|
||||
this.on_change?.(this)
|
||||
}
|
||||
setDirtyCanvas(fg: boolean, bg?: boolean): void {
|
||||
this.sendActionToCanvas('setDirty', [fg, bg])
|
||||
this.sendActionToCanvas("setDirty", [fg, bg])
|
||||
}
|
||||
/**
|
||||
* Destroys a link
|
||||
@@ -1182,12 +1180,11 @@ export class LGraph {
|
||||
* @return {Object} value of the node
|
||||
*/
|
||||
serialize(option?: { sortNodes: boolean }): ISerialisedGraph {
|
||||
const nodes =
|
||||
!LiteGraph.use_uuids && option?.sortNodes
|
||||
? // @ts-expect-error If LiteGraph.use_uuids is false, ids are numbers.
|
||||
[...this._nodes].sort((a, b) => a.id - b.id)
|
||||
const nodes = !LiteGraph.use_uuids && option?.sortNodes
|
||||
// @ts-expect-error If LiteGraph.use_uuids is false, ids are numbers.
|
||||
? [...this._nodes].sort((a, b) => a.id - b.id)
|
||||
: this._nodes
|
||||
const nodes_info = nodes.map((node) => node.serialize())
|
||||
const nodes_info = nodes.map(node => node.serialize())
|
||||
|
||||
//pack link info into a non-verbose format
|
||||
const links: SerialisedLLinkArray[] = []
|
||||
@@ -1208,7 +1205,7 @@ export class LGraph {
|
||||
groups: groups_info,
|
||||
config: this.config,
|
||||
extra: this.extra,
|
||||
version: LiteGraph.VERSION,
|
||||
version: LiteGraph.VERSION
|
||||
}
|
||||
|
||||
this.onSerialize?.(data)
|
||||
@@ -1240,7 +1237,8 @@ export class LGraph {
|
||||
//copy all stored fields
|
||||
for (const i in data) {
|
||||
//links must be accepted
|
||||
if (i == 'nodes' || i == 'groups' || i == 'links') continue
|
||||
if (i == "nodes" || i == "groups" || i == "links")
|
||||
continue
|
||||
this[i] = data[i]
|
||||
}
|
||||
|
||||
@@ -1253,7 +1251,7 @@ export class LGraph {
|
||||
const n_info = nodesData[i] //stored info
|
||||
let node = LiteGraph.createNode(n_info.type, n_info.title)
|
||||
if (!node) {
|
||||
if (LiteGraph.debug) console.log('Node not found or has errors: ' + n_info.type)
|
||||
if (LiteGraph.debug) console.log("Node not found or has errors: " + n_info.type)
|
||||
|
||||
//in case of error we create a replacement node to avoid losing info
|
||||
node = new LGraphNode(undefined)
|
||||
@@ -1314,11 +1312,11 @@ export class LGraph {
|
||||
|
||||
//is a string, then an URL
|
||||
const req = new XMLHttpRequest()
|
||||
req.open('GET', url, true)
|
||||
req.open("GET", url, true)
|
||||
req.send(null)
|
||||
req.onload = function () {
|
||||
if (req.status !== 200) {
|
||||
console.error('Error loading graph:', req.status, req.response)
|
||||
console.error("Error loading graph:", req.status, req.response)
|
||||
return
|
||||
}
|
||||
const data = JSON.parse(req.response)
|
||||
@@ -1326,7 +1324,7 @@ export class LGraph {
|
||||
callback?.()
|
||||
}
|
||||
req.onerror = function (err) {
|
||||
console.error('Error loading graph:', err)
|
||||
console.error("Error loading graph:", err)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
@@ -1,82 +1,90 @@
|
||||
export enum BadgePosition {
|
||||
TopLeft = 'top-left',
|
||||
TopRight = 'top-right',
|
||||
TopLeft = "top-left",
|
||||
TopRight = "top-right",
|
||||
}
|
||||
|
||||
export interface LGraphBadgeOptions {
|
||||
text: string
|
||||
fgColor?: string
|
||||
bgColor?: string
|
||||
fontSize?: number
|
||||
padding?: number
|
||||
height?: number
|
||||
cornerRadius?: number
|
||||
text: string;
|
||||
fgColor?: string;
|
||||
bgColor?: string;
|
||||
fontSize?: number;
|
||||
padding?: number;
|
||||
height?: number;
|
||||
cornerRadius?: number;
|
||||
}
|
||||
|
||||
export class LGraphBadge {
|
||||
text: string
|
||||
fgColor: string
|
||||
bgColor: string
|
||||
fontSize: number
|
||||
padding: number
|
||||
height: number
|
||||
cornerRadius: number
|
||||
text: string;
|
||||
fgColor: string;
|
||||
bgColor: string;
|
||||
fontSize: number;
|
||||
padding: number;
|
||||
height: number;
|
||||
cornerRadius: number;
|
||||
|
||||
constructor({
|
||||
text,
|
||||
fgColor = 'white',
|
||||
bgColor = '#0F1F0F',
|
||||
fgColor = "white",
|
||||
bgColor = "#0F1F0F",
|
||||
fontSize = 12,
|
||||
padding = 6,
|
||||
height = 20,
|
||||
cornerRadius = 5,
|
||||
}: LGraphBadgeOptions) {
|
||||
this.text = text
|
||||
this.fgColor = fgColor
|
||||
this.bgColor = bgColor
|
||||
this.fontSize = fontSize
|
||||
this.padding = padding
|
||||
this.height = height
|
||||
this.cornerRadius = cornerRadius
|
||||
this.text = text;
|
||||
this.fgColor = fgColor;
|
||||
this.bgColor = bgColor;
|
||||
this.fontSize = fontSize;
|
||||
this.padding = padding;
|
||||
this.height = height;
|
||||
this.cornerRadius = cornerRadius;
|
||||
}
|
||||
|
||||
get visible() {
|
||||
return this.text.length > 0
|
||||
return this.text.length > 0;
|
||||
}
|
||||
|
||||
getWidth(ctx: CanvasRenderingContext2D) {
|
||||
if (!this.visible) return 0
|
||||
if (!this.visible) return 0;
|
||||
|
||||
ctx.save()
|
||||
ctx.font = `${this.fontSize}px sans-serif`
|
||||
const textWidth = ctx.measureText(this.text).width
|
||||
ctx.restore()
|
||||
return textWidth + this.padding * 2
|
||||
ctx.save();
|
||||
ctx.font = `${this.fontSize}px sans-serif`;
|
||||
const textWidth = ctx.measureText(this.text).width;
|
||||
ctx.restore();
|
||||
return textWidth + this.padding * 2;
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D, x: number, y: number): void {
|
||||
if (!this.visible) return
|
||||
draw(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x: number,
|
||||
y: number,
|
||||
): void {
|
||||
if (!this.visible) return;
|
||||
|
||||
ctx.save()
|
||||
ctx.font = `${this.fontSize}px sans-serif`
|
||||
const badgeWidth = this.getWidth(ctx)
|
||||
const badgeX = 0
|
||||
ctx.save();
|
||||
ctx.font = `${this.fontSize}px sans-serif`;
|
||||
const badgeWidth = this.getWidth(ctx);
|
||||
const badgeX = 0;
|
||||
|
||||
// Draw badge background
|
||||
ctx.fillStyle = this.bgColor
|
||||
ctx.beginPath()
|
||||
ctx.fillStyle = this.bgColor;
|
||||
ctx.beginPath();
|
||||
if (ctx.roundRect) {
|
||||
ctx.roundRect(x + badgeX, y, badgeWidth, this.height, this.cornerRadius)
|
||||
ctx.roundRect(x + badgeX, y, badgeWidth, this.height, this.cornerRadius);
|
||||
} else {
|
||||
// Fallback for browsers that don't support roundRect
|
||||
ctx.rect(x + badgeX, y, badgeWidth, this.height)
|
||||
ctx.rect(x + badgeX, y, badgeWidth, this.height);
|
||||
}
|
||||
ctx.fill()
|
||||
ctx.fill();
|
||||
|
||||
// Draw badge text
|
||||
ctx.fillStyle = this.fgColor
|
||||
ctx.fillText(this.text, x + badgeX + this.padding, y + this.height - this.padding)
|
||||
ctx.fillStyle = this.fgColor;
|
||||
ctx.fillText(
|
||||
this.text,
|
||||
x + badgeX + this.padding,
|
||||
y + this.height - this.padding
|
||||
);
|
||||
|
||||
ctx.restore()
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
2899
src/LGraphCanvas.ts
2899
src/LGraphCanvas.ts
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
||||
import type { IContextMenuValue, Point, Size } from './interfaces'
|
||||
import type { LGraph } from './LGraph'
|
||||
import type { ISerialisedGroup } from './types/serialisation'
|
||||
import { LiteGraph } from './litegraph'
|
||||
import { LGraphCanvas } from './LGraphCanvas'
|
||||
import { isInsideRectangle, overlapBounding } from './measure'
|
||||
import { LGraphNode } from './LGraphNode'
|
||||
import { RenderShape, TitleMode } from './types/globalEnums'
|
||||
import type { IContextMenuValue, Point, Size } from "./interfaces"
|
||||
import type { LGraph } from "./LGraph"
|
||||
import type { ISerialisedGroup } from "./types/serialisation"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
import { LGraphCanvas } from "./LGraphCanvas"
|
||||
import { isInsideRectangle, overlapBounding } from "./measure"
|
||||
import { LGraphNode } from "./LGraphNode"
|
||||
import { RenderShape, TitleMode } from "./types/globalEnums"
|
||||
|
||||
export interface IGraphGroupFlags extends Record<string, unknown> {
|
||||
pinned?: true
|
||||
@@ -25,8 +25,10 @@ export class LGraphGroup {
|
||||
selected?: boolean
|
||||
|
||||
constructor(title?: string) {
|
||||
this.title = title || 'Group'
|
||||
this.color = LGraphCanvas.node_colors.pale_blue ? LGraphCanvas.node_colors.pale_blue.groupcolor : '#AAA'
|
||||
this.title = title || "Group"
|
||||
this.color = LGraphCanvas.node_colors.pale_blue
|
||||
? LGraphCanvas.node_colors.pale_blue.groupcolor
|
||||
: "#AAA"
|
||||
}
|
||||
|
||||
/** Position of the group, as x,y co-ordinates in graph space */
|
||||
@@ -83,7 +85,12 @@ export class LGraphGroup {
|
||||
const b = this._bounding
|
||||
return {
|
||||
title: this.title,
|
||||
bounding: [Math.round(b[0]), Math.round(b[1]), Math.round(b[2]), Math.round(b[3])],
|
||||
bounding: [
|
||||
Math.round(b[0]),
|
||||
Math.round(b[1]),
|
||||
Math.round(b[2]),
|
||||
Math.round(b[3])
|
||||
],
|
||||
color: this.color,
|
||||
font_size: this.font_size,
|
||||
flags: this.flags,
|
||||
@@ -116,9 +123,9 @@ export class LGraphGroup {
|
||||
ctx.fill()
|
||||
|
||||
const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE
|
||||
ctx.font = font_size + 'px Arial'
|
||||
ctx.textAlign = 'left'
|
||||
ctx.fillText(this.title + (this.pinned ? '📌' : ''), x + padding, y + font_size)
|
||||
ctx.font = font_size + "px Arial"
|
||||
ctx.textAlign = "left"
|
||||
ctx.fillText(this.title + (this.pinned ? "📌" : ""), x + padding, y + font_size)
|
||||
|
||||
if (LiteGraph.highlight_selected_group && this.selected) {
|
||||
graphCanvas.drawSelectionBounding(ctx, this._bounding, {
|
||||
@@ -161,7 +168,8 @@ export class LGraphGroup {
|
||||
const node = nodes[i]
|
||||
node.getBounding(node_bounding)
|
||||
//out of the visible area
|
||||
if (!overlapBounding(this._bounding, node_bounding)) continue
|
||||
if (!overlapBounding(this._bounding, node_bounding))
|
||||
continue
|
||||
|
||||
this._nodes.push(node)
|
||||
}
|
||||
@@ -178,11 +186,10 @@ export class LGraphGroup {
|
||||
|
||||
const allNodes = [...(this._nodes || []), ...nodes]
|
||||
|
||||
const bounds = allNodes.reduce(
|
||||
(acc, node) => {
|
||||
const bounds = allNodes.reduce((acc, node) => {
|
||||
const [x, y] = node.pos
|
||||
const [width, height] = node.size
|
||||
const isReroute = node.type === 'Reroute'
|
||||
const isReroute = node.type === "Reroute"
|
||||
const isCollapsed = node.flags?.collapsed
|
||||
|
||||
const top = y - (isReroute ? 0 : LiteGraph.NODE_TITLE_HEIGHT)
|
||||
@@ -193,21 +200,25 @@ export class LGraphGroup {
|
||||
left: Math.min(acc.left, x),
|
||||
top: Math.min(acc.top, top),
|
||||
right: Math.max(acc.right, right),
|
||||
bottom: Math.max(acc.bottom, bottom),
|
||||
bottom: Math.max(acc.bottom, bottom)
|
||||
}
|
||||
},
|
||||
{ left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity },
|
||||
)
|
||||
}, { left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity })
|
||||
|
||||
this.pos = [bounds.left - padding, bounds.top - padding - this.titleHeight]
|
||||
this.pos = [
|
||||
bounds.left - padding,
|
||||
bounds.top - padding - this.titleHeight
|
||||
]
|
||||
|
||||
this.size = [bounds.right - bounds.left + padding * 2, bounds.bottom - bounds.top + padding * 2 + this.titleHeight]
|
||||
this.size = [
|
||||
bounds.right - bounds.left + padding * 2,
|
||||
bounds.bottom - bounds.top + padding * 2 + this.titleHeight
|
||||
]
|
||||
}
|
||||
|
||||
getMenuOptions(): IContextMenuValue[] {
|
||||
return [
|
||||
{
|
||||
content: this.pinned ? 'Unpin' : 'Pin',
|
||||
content: this.pinned ? "Unpin" : "Pin",
|
||||
callback: () => {
|
||||
if (this.pinned) this.unpin()
|
||||
else this.pin()
|
||||
@@ -215,20 +226,20 @@ export class LGraphGroup {
|
||||
},
|
||||
},
|
||||
null,
|
||||
{ content: 'Title', callback: LGraphCanvas.onShowPropertyEditor },
|
||||
{ content: "Title", callback: LGraphCanvas.onShowPropertyEditor },
|
||||
{
|
||||
content: 'Color',
|
||||
content: "Color",
|
||||
has_submenu: true,
|
||||
callback: LGraphCanvas.onMenuNodeColors,
|
||||
callback: LGraphCanvas.onMenuNodeColors
|
||||
},
|
||||
{
|
||||
content: 'Font size',
|
||||
property: 'font_size',
|
||||
type: 'Number',
|
||||
callback: LGraphCanvas.onShowPropertyEditor,
|
||||
content: "Font size",
|
||||
property: "font_size",
|
||||
type: "Number",
|
||||
callback: LGraphCanvas.onShowPropertyEditor
|
||||
},
|
||||
null,
|
||||
{ content: 'Remove', callback: LGraphCanvas.onMenuNodeRemove },
|
||||
{ content: "Remove", callback: LGraphCanvas.onMenuNodeRemove }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
23
src/LLink.ts
23
src/LLink.ts
@@ -1,6 +1,6 @@
|
||||
import type { CanvasColour, ISlotType } from './interfaces'
|
||||
import type { NodeId } from './LGraphNode'
|
||||
import type { Serialisable, SerialisableLLink } from './types/serialisation'
|
||||
import type { CanvasColour, ISlotType } from "./interfaces"
|
||||
import type { NodeId } from "./LGraphNode"
|
||||
import type { Serialisable, SerialisableLLink } from "./types/serialisation"
|
||||
|
||||
export type LinkId = number | string
|
||||
|
||||
@@ -30,11 +30,9 @@ export class LLink implements Serialisable<SerialisableLLink> {
|
||||
|
||||
#color?: CanvasColour
|
||||
/** Custom colour for this link only */
|
||||
public get color(): CanvasColour {
|
||||
return this.#color
|
||||
}
|
||||
public get color(): CanvasColour { return this.#color }
|
||||
public set color(value: CanvasColour) {
|
||||
this.#color = value === '' ? null : value
|
||||
this.#color = value === "" ? null : value
|
||||
}
|
||||
|
||||
constructor(id: LinkId, type: ISlotType, origin_id: NodeId, origin_slot: number, target_id: NodeId, target_slot: number) {
|
||||
@@ -86,7 +84,14 @@ export class LLink implements Serialisable<SerialisableLLink> {
|
||||
* @returns An array representing this LLink
|
||||
*/
|
||||
serialize(): SerialisedLLinkArray {
|
||||
return [this.id, this.origin_id, this.origin_slot, this.target_id, this.target_slot, this.type]
|
||||
return [
|
||||
this.id,
|
||||
this.origin_id,
|
||||
this.origin_slot,
|
||||
this.target_id,
|
||||
this.target_slot,
|
||||
this.type
|
||||
]
|
||||
}
|
||||
|
||||
asSerialisable(): SerialisableLLink {
|
||||
@@ -96,7 +101,7 @@ export class LLink implements Serialisable<SerialisableLLink> {
|
||||
origin_slot: this.origin_slot,
|
||||
target_id: this.target_id,
|
||||
target_slot: this.target_slot,
|
||||
type: this.type,
|
||||
type: this.type
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { LGraph } from './LGraph'
|
||||
import { LLink } from './LLink'
|
||||
import { LGraphGroup } from './LGraphGroup'
|
||||
import { DragAndScale } from './DragAndScale'
|
||||
import { LGraphCanvas } from './LGraphCanvas'
|
||||
import { ContextMenu } from './ContextMenu'
|
||||
import { CurveEditor } from './CurveEditor'
|
||||
import { LGraphEventMode, LinkDirection, LinkRenderType, NodeSlotType, RenderShape, TitleMode } from './types/globalEnums'
|
||||
import { LGraphNode } from './LGraphNode'
|
||||
import { SlotShape, SlotDirection, SlotType, LabelPosition } from './draw'
|
||||
import type { Dictionary, ISlotType, Rect } from './interfaces'
|
||||
import { distance, isInsideRectangle, overlapBounding } from './measure'
|
||||
import { LGraph } from "./LGraph"
|
||||
import { LLink } from "./LLink"
|
||||
import { LGraphGroup } from "./LGraphGroup"
|
||||
import { DragAndScale } from "./DragAndScale"
|
||||
import { LGraphCanvas } from "./LGraphCanvas"
|
||||
import { ContextMenu } from "./ContextMenu"
|
||||
import { CurveEditor } from "./CurveEditor"
|
||||
import { LGraphEventMode, LinkDirection, LinkRenderType, NodeSlotType, RenderShape, TitleMode } from "./types/globalEnums"
|
||||
import { LGraphNode } from "./LGraphNode"
|
||||
import { SlotShape, SlotDirection, SlotType, LabelPosition } from "./draw"
|
||||
import type { Dictionary, ISlotType, Rect } from "./interfaces"
|
||||
import { distance, isInsideRectangle, overlapBounding } from "./measure"
|
||||
|
||||
/**
|
||||
* The Global Scope. It contains all the registered node classes.
|
||||
@@ -33,35 +33,35 @@ export class LiteGraphGlobal {
|
||||
NODE_MIN_WIDTH = 50
|
||||
NODE_COLLAPSED_RADIUS = 10
|
||||
NODE_COLLAPSED_WIDTH = 80
|
||||
NODE_TITLE_COLOR = '#999'
|
||||
NODE_SELECTED_TITLE_COLOR = '#FFF'
|
||||
NODE_TITLE_COLOR = "#999"
|
||||
NODE_SELECTED_TITLE_COLOR = "#FFF"
|
||||
NODE_TEXT_SIZE = 14
|
||||
NODE_TEXT_COLOR = '#AAA'
|
||||
NODE_TEXT_HIGHLIGHT_COLOR = '#EEE'
|
||||
NODE_TEXT_COLOR = "#AAA"
|
||||
NODE_TEXT_HIGHLIGHT_COLOR = "#EEE"
|
||||
NODE_SUBTEXT_SIZE = 12
|
||||
NODE_DEFAULT_COLOR = '#333'
|
||||
NODE_DEFAULT_BGCOLOR = '#353535'
|
||||
NODE_DEFAULT_BOXCOLOR = '#666'
|
||||
NODE_DEFAULT_SHAPE = 'box'
|
||||
NODE_BOX_OUTLINE_COLOR = '#FFF'
|
||||
DEFAULT_SHADOW_COLOR = 'rgba(0,0,0,0.5)'
|
||||
NODE_DEFAULT_COLOR = "#333"
|
||||
NODE_DEFAULT_BGCOLOR = "#353535"
|
||||
NODE_DEFAULT_BOXCOLOR = "#666"
|
||||
NODE_DEFAULT_SHAPE = "box"
|
||||
NODE_BOX_OUTLINE_COLOR = "#FFF"
|
||||
DEFAULT_SHADOW_COLOR = "rgba(0,0,0,0.5)"
|
||||
DEFAULT_GROUP_FONT = 24
|
||||
DEFAULT_GROUP_FONT_SIZE?: any
|
||||
|
||||
WIDGET_BGCOLOR = '#222'
|
||||
WIDGET_OUTLINE_COLOR = '#666'
|
||||
WIDGET_TEXT_COLOR = '#DDD'
|
||||
WIDGET_SECONDARY_TEXT_COLOR = '#999'
|
||||
WIDGET_BGCOLOR = "#222"
|
||||
WIDGET_OUTLINE_COLOR = "#666"
|
||||
WIDGET_TEXT_COLOR = "#DDD"
|
||||
WIDGET_SECONDARY_TEXT_COLOR = "#999"
|
||||
|
||||
LINK_COLOR = '#9A9'
|
||||
LINK_COLOR = "#9A9"
|
||||
// TODO: This is a workaround until LGraphCanvas.link_type_colors is no longer static.
|
||||
static DEFAULT_EVENT_LINK_COLOR = '#A86'
|
||||
EVENT_LINK_COLOR = '#A86'
|
||||
CONNECTING_LINK_COLOR = '#AFA'
|
||||
static DEFAULT_EVENT_LINK_COLOR = "#A86"
|
||||
EVENT_LINK_COLOR = "#A86"
|
||||
CONNECTING_LINK_COLOR = "#AFA"
|
||||
|
||||
MAX_NUMBER_OF_NODES = 10000 //avoid infinite loops
|
||||
DEFAULT_POSITION = [100, 100] //default node position
|
||||
VALID_SHAPES = ['default', 'box', 'round', 'card'] //,'circle'
|
||||
VALID_SHAPES = ["default", "box", "round", "card"] //,"circle"
|
||||
|
||||
//shapes are used for nodes but also for slots
|
||||
BOX_SHAPE = RenderShape.BOX
|
||||
@@ -79,8 +79,8 @@ export class LiteGraphGlobal {
|
||||
EVENT = -1 as const //for outputs
|
||||
ACTION = -1 as const //for inputs
|
||||
|
||||
NODE_MODES = ['Always', 'On Event', 'Never', 'On Trigger'] // helper, will add "On Request" and more in the future
|
||||
NODE_MODES_COLORS = ['#666', '#422', '#333', '#224', '#626'] // use with node_box_coloured_by_mode
|
||||
NODE_MODES = ["Always", "On Event", "Never", "On Trigger"] // helper, will add "On Request" and more in the future
|
||||
NODE_MODES_COLORS = ["#666", "#422", "#333", "#224", "#626"] // use with node_box_coloured_by_mode
|
||||
ALWAYS = LGraphEventMode.ALWAYS
|
||||
ON_EVENT = LGraphEventMode.ON_EVENT
|
||||
NEVER = LGraphEventMode.NEVER
|
||||
@@ -92,7 +92,7 @@ export class LiteGraphGlobal {
|
||||
RIGHT = LinkDirection.RIGHT
|
||||
CENTER = LinkDirection.CENTER
|
||||
|
||||
LINK_RENDER_MODES = ['Straight', 'Linear', 'Spline'] // helper
|
||||
LINK_RENDER_MODES = ["Straight", "Linear", "Spline"] // helper
|
||||
HIDDEN_LINK = LinkRenderType.HIDDEN_LINK
|
||||
STRAIGHT_LINK = LinkRenderType.STRAIGHT_LINK
|
||||
LINEAR_LINK = LinkRenderType.LINEAR_LINK
|
||||
@@ -103,10 +103,10 @@ export class LiteGraphGlobal {
|
||||
TRANSPARENT_TITLE = TitleMode.TRANSPARENT_TITLE
|
||||
AUTOHIDE_TITLE = TitleMode.AUTOHIDE_TITLE
|
||||
|
||||
VERTICAL_LAYOUT = 'vertical' // arrange nodes vertically
|
||||
VERTICAL_LAYOUT = "vertical" // arrange nodes vertically
|
||||
|
||||
proxy = null //used to redirect calls
|
||||
node_images_path = ''
|
||||
node_images_path = ""
|
||||
|
||||
debug = false
|
||||
catch_exceptions = true
|
||||
@@ -156,7 +156,7 @@ export class LiteGraphGlobal {
|
||||
|
||||
release_link_on_empty_shows_menu = false //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
|
||||
|
||||
pointerevents_method = 'pointer' // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
|
||||
pointerevents_method = "pointer" // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
|
||||
|
||||
// TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary)
|
||||
ctrl_shift_v_paste_connect_unselected_outputs = true //[true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes
|
||||
@@ -184,19 +184,19 @@ export class LiteGraphGlobal {
|
||||
// Avoid circular dependency from original single-module
|
||||
static {
|
||||
LGraphCanvas.link_type_colors = {
|
||||
'-1': LiteGraphGlobal.DEFAULT_EVENT_LINK_COLOR,
|
||||
number: '#AAA',
|
||||
node: '#DCA',
|
||||
"-1": LiteGraphGlobal.DEFAULT_EVENT_LINK_COLOR,
|
||||
number: "#AAA",
|
||||
node: "#DCA"
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
//timer that works everywhere
|
||||
if (typeof performance != 'undefined') {
|
||||
if (typeof performance != "undefined") {
|
||||
this.getTime = performance.now.bind(performance)
|
||||
} else if (typeof Date != 'undefined' && Date.now) {
|
||||
} else if (typeof Date != "undefined" && Date.now) {
|
||||
this.getTime = Date.now.bind(Date)
|
||||
} else if (typeof process !== 'undefined') {
|
||||
} else if (typeof process != "undefined") {
|
||||
this.getTime = function () {
|
||||
const t = process.hrtime()
|
||||
return t[0] * 0.001 + t[1] * 1e-6
|
||||
@@ -214,14 +214,15 @@ export class LiteGraphGlobal {
|
||||
* @param {Class} base_class class containing the structure of a node
|
||||
*/
|
||||
registerNodeType(type: string, base_class: typeof LGraphNode): void {
|
||||
if (!base_class.prototype) throw 'Cannot register a simple object, it must be a class with a prototype'
|
||||
if (!base_class.prototype)
|
||||
throw "Cannot register a simple object, it must be a class with a prototype"
|
||||
base_class.type = type
|
||||
|
||||
if (this.debug) console.log('Node registered: ' + type)
|
||||
if (this.debug) console.log("Node registered: " + type)
|
||||
|
||||
const classname = base_class.name
|
||||
|
||||
const pos = type.lastIndexOf('/')
|
||||
const pos = type.lastIndexOf("/")
|
||||
base_class.category = type.substring(0, pos)
|
||||
|
||||
base_class.title ||= classname
|
||||
@@ -233,25 +234,25 @@ export class LiteGraphGlobal {
|
||||
|
||||
const prev = this.registered_node_types[type]
|
||||
if (prev) {
|
||||
console.log('replacing node type: ' + type)
|
||||
console.log("replacing node type: " + type)
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(base_class.prototype, 'shape')) {
|
||||
Object.defineProperty(base_class.prototype, 'shape', {
|
||||
set(this: LGraphNode, v: RenderShape | 'default' | 'box' | 'round' | 'circle' | 'card') {
|
||||
if (!Object.prototype.hasOwnProperty.call(base_class.prototype, "shape")) {
|
||||
Object.defineProperty(base_class.prototype, "shape", {
|
||||
set(this: LGraphNode, v: RenderShape | "default" | "box" | "round" | "circle" | "card") {
|
||||
switch (v) {
|
||||
case 'default':
|
||||
case "default":
|
||||
delete this._shape
|
||||
break
|
||||
case 'box':
|
||||
case "box":
|
||||
this._shape = RenderShape.BOX
|
||||
break
|
||||
case 'round':
|
||||
case "round":
|
||||
this._shape = RenderShape.ROUND
|
||||
break
|
||||
case 'circle':
|
||||
case "circle":
|
||||
this._shape = RenderShape.CIRCLE
|
||||
break
|
||||
case 'card':
|
||||
case "card":
|
||||
this._shape = RenderShape.CARD
|
||||
break
|
||||
default:
|
||||
@@ -269,7 +270,7 @@ export class LiteGraphGlobal {
|
||||
if (base_class.supported_extensions) {
|
||||
for (const i in base_class.supported_extensions) {
|
||||
const ext = base_class.supported_extensions[i]
|
||||
if (ext && typeof ext === 'string') {
|
||||
if (ext && typeof ext === "string") {
|
||||
this.node_types_by_file_extension[ext.toLowerCase()] = base_class
|
||||
}
|
||||
}
|
||||
@@ -287,7 +288,7 @@ export class LiteGraphGlobal {
|
||||
console.warn(`LiteGraph node class ${type} has onPropertyChange method, it must be called onPropertyChanged with d at the end`)
|
||||
|
||||
// TODO one would want to know input and ouput :: this would allow through registerNodeAndSlotType to get all the slots types
|
||||
if (this.auto_load_slot_types) new base_class(base_class.title || 'tmpnode')
|
||||
if (this.auto_load_slot_types) new base_class(base_class.title || "tmpnode")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,8 +296,10 @@ export class LiteGraphGlobal {
|
||||
* @param {String|Object} type name of the node or the node constructor itself
|
||||
*/
|
||||
unregisterNodeType(type: string | typeof LGraphNode): void {
|
||||
const base_class = typeof type === 'string' ? this.registered_node_types[type] : type
|
||||
if (!base_class) throw 'node type not found: ' + type
|
||||
const base_class = typeof type === "string"
|
||||
? this.registered_node_types[type]
|
||||
: type
|
||||
if (!base_class) throw "node type not found: " + type
|
||||
|
||||
delete this.registered_node_types[base_class.type]
|
||||
|
||||
@@ -312,30 +315,38 @@ export class LiteGraphGlobal {
|
||||
registerNodeAndSlotType(type: LGraphNode, slot_type: ISlotType, out?: boolean): void {
|
||||
out ||= false
|
||||
// @ts-expect-error Confirm this function no longer supports string types - base_class should always be an instance not a constructor.
|
||||
const base_class = typeof type === 'string' && this.registered_node_types[type] !== 'anonymous' ? this.registered_node_types[type] : type
|
||||
const base_class = typeof type === "string" && this.registered_node_types[type] !== "anonymous"
|
||||
? this.registered_node_types[type]
|
||||
: type
|
||||
|
||||
// @ts-expect-error Confirm this function no longer supports string types - base_class should always be an instance not a constructor.
|
||||
const class_type = base_class.constructor.type
|
||||
|
||||
let allTypes = []
|
||||
if (typeof slot_type === 'string') {
|
||||
allTypes = slot_type.split(',')
|
||||
if (typeof slot_type === "string") {
|
||||
allTypes = slot_type.split(",")
|
||||
} else if (slot_type == this.EVENT || slot_type == this.ACTION) {
|
||||
allTypes = ['_event_']
|
||||
allTypes = ["_event_"]
|
||||
} else {
|
||||
allTypes = ['*']
|
||||
allTypes = ["*"]
|
||||
}
|
||||
|
||||
for (let i = 0; i < allTypes.length; ++i) {
|
||||
let slotType = allTypes[i]
|
||||
if (slotType === '') slotType = '*'
|
||||
if (slotType === "") slotType = "*"
|
||||
|
||||
const registerTo = out ? 'registered_slot_out_types' : 'registered_slot_in_types'
|
||||
if (this[registerTo][slotType] === undefined) this[registerTo][slotType] = { nodes: [] }
|
||||
if (!this[registerTo][slotType].nodes.includes(class_type)) this[registerTo][slotType].nodes.push(class_type)
|
||||
const registerTo = out
|
||||
? "registered_slot_out_types"
|
||||
: "registered_slot_in_types"
|
||||
if (this[registerTo][slotType] === undefined)
|
||||
this[registerTo][slotType] = { nodes: [] }
|
||||
if (!this[registerTo][slotType].nodes.includes(class_type))
|
||||
this[registerTo][slotType].nodes.push(class_type)
|
||||
|
||||
// check if is a new type
|
||||
const types = out ? this.slot_types_out : this.slot_types_in
|
||||
const types = out
|
||||
? this.slot_types_out
|
||||
: this.slot_types_in
|
||||
if (!types.includes(slotType.toLowerCase())) {
|
||||
types.push(slotType.toLowerCase())
|
||||
types.sort()
|
||||
@@ -352,21 +363,27 @@ export class LiteGraphGlobal {
|
||||
* @param {String} return_type [optional] string with the return type, otherwise it will be generic
|
||||
* @param {Object} properties [optional] properties to be configurable
|
||||
*/
|
||||
wrapFunctionAsNode(name: string, func: (...args: any) => any, param_types: string[], return_type: string, properties: unknown) {
|
||||
wrapFunctionAsNode(
|
||||
name: string,
|
||||
func: (...args: any) => any,
|
||||
param_types: string[],
|
||||
return_type: string,
|
||||
properties: unknown
|
||||
) {
|
||||
const params = Array(func.length)
|
||||
let code = ''
|
||||
let code = ""
|
||||
const names = this.getParameterNames(func)
|
||||
for (let i = 0; i < names.length; ++i) {
|
||||
code += `this.addInput('${names[i]}',${param_types && param_types[i] ? `'${param_types[i]}'` : '0'});\n`
|
||||
code += `this.addInput('${names[i]}',${param_types && param_types[i] ? `'${param_types[i]}'` : "0"});\n`
|
||||
}
|
||||
code += `this.addOutput('out',${return_type ? `'${return_type}'` : 0});\n`
|
||||
if (properties) code += `this.properties = ${JSON.stringify(properties)};\n`
|
||||
|
||||
const classobj = Function(code)
|
||||
// @ts-ignore
|
||||
classobj.title = name.split('/').pop()
|
||||
classobj.title = name.split("/").pop()
|
||||
// @ts-ignore
|
||||
classobj.desc = 'Generated from ' + func.name
|
||||
classobj.desc = "Generated from " + func.name
|
||||
classobj.prototype.onExecute = function onExecute() {
|
||||
for (let i = 0; i < params.length; ++i) {
|
||||
params[i] = this.getInputData(i)
|
||||
@@ -399,7 +416,7 @@ export class LiteGraphGlobal {
|
||||
for (const i in this.registered_node_types) {
|
||||
const type = this.registered_node_types[i]
|
||||
//keep old in case of replacing
|
||||
if (type.prototype[name]) type.prototype['_' + name] = type.prototype[name]
|
||||
if (type.prototype[name]) type.prototype["_" + name] = type.prototype[name]
|
||||
type.prototype[name] = func
|
||||
}
|
||||
}
|
||||
@@ -475,7 +492,7 @@ export class LiteGraphGlobal {
|
||||
const type = this.registered_node_types[i]
|
||||
if (type.filter != filter) continue
|
||||
|
||||
if (category == '') {
|
||||
if (category == "") {
|
||||
if (type.category == null) r.push(type)
|
||||
} else if (type.category == category) {
|
||||
r.push(type)
|
||||
@@ -497,11 +514,12 @@ export class LiteGraphGlobal {
|
||||
* @return {Array} array with all the names of the categories
|
||||
*/
|
||||
getNodeTypesCategories(filter: string): string[] {
|
||||
const categories = { '': 1 }
|
||||
const categories = { "": 1 }
|
||||
for (const i in this.registered_node_types) {
|
||||
const type = this.registered_node_types[i]
|
||||
if (type.category && !type.skip_list) {
|
||||
if (type.filter != filter) continue
|
||||
if (type.filter != filter)
|
||||
continue
|
||||
categories[type.category] = 1
|
||||
}
|
||||
}
|
||||
@@ -514,34 +532,35 @@ export class LiteGraphGlobal {
|
||||
|
||||
//debug purposes: reloads all the js scripts that matches a wildcard
|
||||
reloadNodes(folder_wildcard: string): void {
|
||||
const tmp = document.getElementsByTagName('script')
|
||||
const tmp = document.getElementsByTagName("script")
|
||||
//weird, this array changes by its own, so we use a copy
|
||||
const script_files = []
|
||||
for (let i = 0; i < tmp.length; i++) {
|
||||
script_files.push(tmp[i])
|
||||
}
|
||||
|
||||
const docHeadObj = document.getElementsByTagName('head')[0]
|
||||
const docHeadObj = document.getElementsByTagName("head")[0]
|
||||
folder_wildcard = document.location.href + folder_wildcard
|
||||
|
||||
for (let i = 0; i < script_files.length; i++) {
|
||||
const src = script_files[i].src
|
||||
if (!src || src.substr(0, folder_wildcard.length) != folder_wildcard) continue
|
||||
if (!src || src.substr(0, folder_wildcard.length) != folder_wildcard)
|
||||
continue
|
||||
|
||||
try {
|
||||
if (this.debug) console.log('Reloading: ' + src)
|
||||
const dynamicScript = document.createElement('script')
|
||||
dynamicScript.type = 'text/javascript'
|
||||
if (this.debug) console.log("Reloading: " + src)
|
||||
const dynamicScript = document.createElement("script")
|
||||
dynamicScript.type = "text/javascript"
|
||||
dynamicScript.src = src
|
||||
docHeadObj.appendChild(dynamicScript)
|
||||
docHeadObj.removeChild(script_files[i])
|
||||
} catch (err) {
|
||||
if (this.throw_errors) throw err
|
||||
if (this.debug) console.log('Error while reloading ' + src)
|
||||
if (this.debug) console.log("Error while reloading " + src)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.debug) console.log('Nodes reloaded')
|
||||
if (this.debug) console.log("Nodes reloaded")
|
||||
}
|
||||
|
||||
//separated just to improve if it doesn't work
|
||||
@@ -562,7 +581,7 @@ export class LiteGraphGlobal {
|
||||
*/
|
||||
uuidv4(): string {
|
||||
// @ts-ignore
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (a) => (a ^ ((Math.random() * 16) >> (a / 4))).toString(16))
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, a => (a ^ Math.random() * 16 >> a / 4).toString(16))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -572,10 +591,11 @@ export class LiteGraphGlobal {
|
||||
* @return {Boolean} true if they can be connected
|
||||
*/
|
||||
isValidConnection(type_a: ISlotType, type_b: ISlotType): boolean {
|
||||
if (type_a == '' || type_a === '*') type_a = 0
|
||||
if (type_b == '' || type_b === '*') type_b = 0
|
||||
if (type_a == "" || type_a === "*") type_a = 0
|
||||
if (type_b == "" || type_b === "*") type_b = 0
|
||||
// If generic in/output, matching types (valid for triggers), or event/action types
|
||||
if (!type_a || !type_b || type_a == type_b || (type_a == this.EVENT && type_b == this.ACTION)) return true
|
||||
if (!type_a || !type_b || type_a == type_b || (type_a == this.EVENT && type_b == this.ACTION))
|
||||
return true
|
||||
|
||||
// Enforce string type to handle toLowerCase call (-1 number not ok)
|
||||
type_a = String(type_a)
|
||||
@@ -584,14 +604,16 @@ export class LiteGraphGlobal {
|
||||
type_b = type_b.toLowerCase()
|
||||
|
||||
// For nodes supporting multiple connection types
|
||||
if (type_a.indexOf(',') == -1 && type_b.indexOf(',') == -1) return type_a == type_b
|
||||
if (type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1)
|
||||
return type_a == type_b
|
||||
|
||||
// Check all permutations to see if one is valid
|
||||
const supported_types_a = type_a.split(',')
|
||||
const supported_types_b = type_b.split(',')
|
||||
const supported_types_a = type_a.split(",")
|
||||
const supported_types_b = type_b.split(",")
|
||||
for (let i = 0; i < supported_types_a.length; ++i) {
|
||||
for (let j = 0; j < supported_types_b.length; ++j) {
|
||||
if (this.isValidConnection(supported_types_a[i], supported_types_b[j])) return true
|
||||
if (this.isValidConnection(supported_types_a[i], supported_types_b[j]))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +631,7 @@ export class LiteGraphGlobal {
|
||||
this.searchbox_extras[description.toLowerCase()] = {
|
||||
type: node_type,
|
||||
desc: description,
|
||||
data: data,
|
||||
data: data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,128 +643,125 @@ export class LiteGraphGlobal {
|
||||
* @param {Function} on_error in case of an error
|
||||
* @return {FileReader|Promise} returns the object used to
|
||||
*/
|
||||
fetchFile(
|
||||
url: string | URL | Request | Blob,
|
||||
type: string,
|
||||
on_complete: (data: string | ArrayBuffer) => void,
|
||||
on_error: (error: unknown) => void,
|
||||
): void | Promise<void> {
|
||||
fetchFile(url: string | URL | Request | Blob, type: string, on_complete: (data: string | ArrayBuffer) => void, on_error: (error: unknown) => void): void | Promise<void> {
|
||||
if (!url) return null
|
||||
|
||||
type = type || 'text'
|
||||
if (typeof url === 'string') {
|
||||
if (url.substr(0, 4) == 'http' && this.proxy) url = this.proxy + url.substr(url.indexOf(':') + 3)
|
||||
type = type || "text"
|
||||
if (typeof url === "string") {
|
||||
if (url.substr(0, 4) == "http" && this.proxy)
|
||||
url = this.proxy + url.substr(url.indexOf(":") + 3)
|
||||
|
||||
return fetch(url)
|
||||
.then(function (response) {
|
||||
if (!response.ok) throw new Error('File not found') //it will be catch below
|
||||
if (type == 'arraybuffer') return response.arrayBuffer()
|
||||
else if (type == 'text' || type == 'string') return response.text()
|
||||
else if (type == 'json') return response.json()
|
||||
else if (type == 'blob') return response.blob()
|
||||
if (!response.ok)
|
||||
throw new Error("File not found") //it will be catch below
|
||||
if (type == "arraybuffer")
|
||||
return response.arrayBuffer()
|
||||
else if (type == "text" || type == "string")
|
||||
return response.text()
|
||||
else if (type == "json")
|
||||
return response.json()
|
||||
else if (type == "blob")
|
||||
return response.blob()
|
||||
})
|
||||
.then(function (data: string | ArrayBuffer): void {
|
||||
on_complete?.(data)
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('error fetching file:', url)
|
||||
console.error("error fetching file:", url)
|
||||
on_error?.(error)
|
||||
})
|
||||
} else if (url instanceof File || url instanceof Blob) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
let v = e.target.result
|
||||
if (type == 'json')
|
||||
if (type == "json")
|
||||
// @ts-ignore
|
||||
v = JSON.parse(v)
|
||||
on_complete?.(v)
|
||||
}
|
||||
if (type == 'arraybuffer') return reader.readAsArrayBuffer(url)
|
||||
else if (type == 'text' || type == 'json') return reader.readAsText(url)
|
||||
else if (type == 'blob') return reader.readAsBinaryString(url)
|
||||
if (type == "arraybuffer")
|
||||
return reader.readAsArrayBuffer(url)
|
||||
else if (type == "text" || type == "json")
|
||||
return reader.readAsText(url)
|
||||
else if (type == "blob")
|
||||
return reader.readAsBinaryString(url)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
//used to create nodes from wrapping functions
|
||||
getParameterNames(func: (...args: any) => any): string[] {
|
||||
return (func + '')
|
||||
.replace(/[/][/].*$/gm, '') // strip single-line comments
|
||||
.replace(/\s+/g, '') // strip white space
|
||||
.replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments /**/
|
||||
.split('){', 1)[0]
|
||||
.replace(/^[^(]*[(]/, '') // extract the parameters
|
||||
.replace(/=[^,]+/g, '') // strip any ES6 defaults
|
||||
.split(',')
|
||||
return (func + "")
|
||||
.replace(/[/][/].*$/gm, "") // strip single-line comments
|
||||
.replace(/\s+/g, "") // strip white space
|
||||
.replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/
|
||||
.split("){", 1)[0]
|
||||
.replace(/^[^(]*[(]/, "") // extract the parameters
|
||||
.replace(/=[^,]+/g, "") // strip any ES6 defaults
|
||||
.split(",")
|
||||
.filter(Boolean) // split & filter [""]
|
||||
}
|
||||
|
||||
/* helper for interaction: pointer, touch, mouse Listeners
|
||||
used by LGraphCanvas DragAndScale ContextMenu*/
|
||||
pointerListenerAdd(oDOM: Node, sEvIn: string, fCall: (e: Event) => boolean | void, capture = false): void {
|
||||
if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall !== 'function') return
|
||||
if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall !== "function") return
|
||||
|
||||
let sMethod = this.pointerevents_method
|
||||
let sEvent = sEvIn
|
||||
|
||||
// UNDER CONSTRUCTION
|
||||
// convert pointerevents to touch event when not available
|
||||
if (sMethod == 'pointer' && !window.PointerEvent) {
|
||||
if (sMethod == "pointer" && !window.PointerEvent) {
|
||||
console.warn("sMethod=='pointer' && !window.PointerEvent")
|
||||
console.log('Converting pointer[' + sEvent + '] : down move up cancel enter TO touchstart touchmove touchend, etc ..')
|
||||
console.log("Converting pointer[" + sEvent + "] : down move up cancel enter TO touchstart touchmove touchend, etc ..")
|
||||
switch (sEvent) {
|
||||
case 'down': {
|
||||
sMethod = 'touch'
|
||||
sEvent = 'start'
|
||||
case "down": {
|
||||
sMethod = "touch"
|
||||
sEvent = "start"
|
||||
break
|
||||
}
|
||||
case 'move': {
|
||||
sMethod = 'touch'
|
||||
case "move": {
|
||||
sMethod = "touch"
|
||||
//sEvent = "move";
|
||||
break
|
||||
}
|
||||
case 'up': {
|
||||
sMethod = 'touch'
|
||||
sEvent = 'end'
|
||||
case "up": {
|
||||
sMethod = "touch"
|
||||
sEvent = "end"
|
||||
break
|
||||
}
|
||||
case 'cancel': {
|
||||
sMethod = 'touch'
|
||||
case "cancel": {
|
||||
sMethod = "touch"
|
||||
//sEvent = "cancel";
|
||||
break
|
||||
}
|
||||
case 'enter': {
|
||||
console.log('debug: Should I send a move event?') // ???
|
||||
case "enter": {
|
||||
console.log("debug: Should I send a move event?") // ???
|
||||
break
|
||||
}
|
||||
// case "over": case "out": not used at now
|
||||
default: {
|
||||
console.warn('PointerEvent not available in this browser ? The event ' + sEvent + ' would not be called')
|
||||
console.warn("PointerEvent not available in this browser ? The event " + sEvent + " would not be called")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (sEvent) {
|
||||
// @ts-expect-error
|
||||
//both pointer and move events
|
||||
case 'down':
|
||||
case 'up':
|
||||
case 'move':
|
||||
case 'over':
|
||||
case 'out':
|
||||
case 'enter': {
|
||||
case "down": case "up": case "move": case "over": case "out": case "enter":
|
||||
{
|
||||
oDOM.addEventListener(sMethod + sEvent, fCall, capture)
|
||||
return; // Add return to prevent fallthrough
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
// only pointerevents
|
||||
case 'leave':
|
||||
case 'cancel':
|
||||
case 'gotpointercapture':
|
||||
case 'lostpointercapture': {
|
||||
if (sMethod != 'mouse') {
|
||||
case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture":
|
||||
{
|
||||
if (sMethod != "mouse") {
|
||||
return oDOM.addEventListener(sMethod + sEvent, fCall, capture)
|
||||
}
|
||||
return; // Add return to prevent fallthrough
|
||||
}
|
||||
// not "pointer" || "mouse"
|
||||
default:
|
||||
@@ -750,31 +769,24 @@ export class LiteGraphGlobal {
|
||||
}
|
||||
}
|
||||
pointerListenerRemove(oDOM: Node, sEvent: string, fCall: (e: Event) => boolean | void, capture = false): void {
|
||||
if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall !== 'function') return
|
||||
if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall !== "function") return
|
||||
|
||||
switch (sEvent) {
|
||||
// @ts-expect-error
|
||||
//both pointer and move events
|
||||
case 'down':
|
||||
case 'up':
|
||||
case 'move':
|
||||
case 'over':
|
||||
case 'out':
|
||||
case 'enter': {
|
||||
if (this.pointerevents_method == 'pointer' || this.pointerevents_method == 'mouse') {
|
||||
case "down": case "up": case "move": case "over": case "out": case "enter":
|
||||
{
|
||||
if (this.pointerevents_method == "pointer" || this.pointerevents_method == "mouse") {
|
||||
oDOM.removeEventListener(this.pointerevents_method + sEvent, fCall, capture)
|
||||
}
|
||||
return; // Add return to prevent fallthrough
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
// only pointerevents
|
||||
case 'leave':
|
||||
case 'cancel':
|
||||
case 'gotpointercapture':
|
||||
case 'lostpointercapture': {
|
||||
if (this.pointerevents_method == 'pointer') {
|
||||
case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture":
|
||||
{
|
||||
if (this.pointerevents_method == "pointer") {
|
||||
return oDOM.removeEventListener(this.pointerevents_method + sEvent, fCall, capture)
|
||||
}
|
||||
return; // Add return to prevent fallthrough
|
||||
}
|
||||
// not "pointer" || "mouse"
|
||||
default:
|
||||
@@ -795,15 +807,15 @@ export class LiteGraphGlobal {
|
||||
|
||||
colorToString(c: [number, number, number, number]): string {
|
||||
return (
|
||||
'rgba(' +
|
||||
"rgba(" +
|
||||
Math.round(c[0] * 255).toFixed() +
|
||||
',' +
|
||||
"," +
|
||||
Math.round(c[1] * 255).toFixed() +
|
||||
',' +
|
||||
"," +
|
||||
Math.round(c[2] * 255).toFixed() +
|
||||
',' +
|
||||
(c.length == 4 ? c[3].toFixed(2) : '1.0') +
|
||||
')'
|
||||
"," +
|
||||
(c.length == 4 ? c[3].toFixed(2) : "1.0") +
|
||||
")"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -828,7 +840,12 @@ export class LiteGraphGlobal {
|
||||
|
||||
//point inside bounding box
|
||||
isInsideBounding(p: number[], bb: number[][]): boolean {
|
||||
if (p[0] < bb[0][0] || p[1] < bb[0][1] || p[0] > bb[1][0] || p[1] > bb[1][1]) {
|
||||
if (
|
||||
p[0] < bb[0][0] ||
|
||||
p[1] < bb[0][1] ||
|
||||
p[0] > bb[1][0] ||
|
||||
p[1] > bb[1][1]
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -838,11 +855,11 @@ export class LiteGraphGlobal {
|
||||
// format of a hex triplet - the kind we use for HTML colours. The function
|
||||
// will return an array with three values.
|
||||
hex2num(hex: string): number[] {
|
||||
if (hex.charAt(0) == '#') {
|
||||
if (hex.charAt(0) == "#") {
|
||||
hex = hex.slice(1)
|
||||
} //Remove the '#' char - if there is one.
|
||||
hex = hex.toUpperCase()
|
||||
const hex_alphabets = '0123456789ABCDEF'
|
||||
const hex_alphabets = "0123456789ABCDEF"
|
||||
const value = new Array(3)
|
||||
let k = 0
|
||||
let int1, int2
|
||||
@@ -858,8 +875,8 @@ export class LiteGraphGlobal {
|
||||
//Give a array with three values as the argument and the function will return
|
||||
// the corresponding hex triplet.
|
||||
num2hex(triplet: number[]): string {
|
||||
const hex_alphabets = '0123456789ABCDEF'
|
||||
let hex = '#'
|
||||
const hex_alphabets = "0123456789ABCDEF"
|
||||
let hex = "#"
|
||||
let int1, int2
|
||||
for (let i = 0; i < 3; i++) {
|
||||
int1 = triplet[i] / 16
|
||||
@@ -873,7 +890,7 @@ export class LiteGraphGlobal {
|
||||
closeAllContextMenus(ref_window: Window): void {
|
||||
ref_window = ref_window || window
|
||||
|
||||
const elements = ref_window.document.querySelectorAll('.litecontextmenu')
|
||||
const elements = ref_window.document.querySelectorAll(".litecontextmenu")
|
||||
if (!elements.length) return
|
||||
|
||||
const result = []
|
||||
@@ -908,14 +925,20 @@ export class LiteGraphGlobal {
|
||||
|
||||
//copy getters
|
||||
if (origin.prototype.__lookupGetter__(i)) {
|
||||
target.prototype.__defineGetter__(i, origin.prototype.__lookupGetter__(i))
|
||||
target.prototype.__defineGetter__(
|
||||
i,
|
||||
origin.prototype.__lookupGetter__(i)
|
||||
)
|
||||
} else {
|
||||
target.prototype[i] = origin.prototype[i]
|
||||
}
|
||||
|
||||
//and setters
|
||||
if (origin.prototype.__lookupSetter__(i)) {
|
||||
target.prototype.__defineSetter__(i, origin.prototype.__lookupSetter__(i))
|
||||
target.prototype.__defineSetter__(
|
||||
i,
|
||||
origin.prototype.__lookupSetter__(i)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
export class MapProxyHandler<V> implements ProxyHandler<Map<number | string, V>> {
|
||||
getOwnPropertyDescriptor(target: Map<number | string, V>, p: string | symbol): PropertyDescriptor | undefined {
|
||||
const value = this.get(target, p)
|
||||
if (value)
|
||||
return {
|
||||
if (value) return {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value
|
||||
@@ -11,27 +10,27 @@ export class MapProxyHandler<V> implements ProxyHandler<Map<number | string, V>>
|
||||
}
|
||||
|
||||
has(target: Map<number | string, V>, p: string | symbol): boolean {
|
||||
if (typeof p === 'symbol') return false
|
||||
if (typeof p === "symbol") return false
|
||||
|
||||
const int = parseInt(p, 10)
|
||||
return target.has(!isNaN(int) ? int : p)
|
||||
}
|
||||
|
||||
ownKeys(target: Map<number | string, V>): ArrayLike<string | symbol> {
|
||||
return [...target.keys()].map((x) => String(x))
|
||||
return [...target.keys()].map(x => String(x))
|
||||
}
|
||||
|
||||
get(target: Map<number | string, V>, p: string | symbol): any {
|
||||
// Workaround does not support link IDs of "values", "entries", "constructor", etc.
|
||||
if (p in target) return Reflect.get(target, p, target)
|
||||
if (typeof p === 'symbol') return
|
||||
if (typeof p === "symbol") return
|
||||
|
||||
const int = parseInt(p, 10)
|
||||
return target.get(!isNaN(int) ? int : p)
|
||||
}
|
||||
|
||||
set(target: Map<number | string, V>, p: string | symbol, newValue: any): boolean {
|
||||
if (typeof p === 'symbol') return false
|
||||
if (typeof p === "symbol") return false
|
||||
|
||||
const int = parseInt(p, 10)
|
||||
target.set(!isNaN(int) ? int : p, newValue)
|
||||
|
||||
110
src/draw.ts
110
src/draw.ts
@@ -1,9 +1,9 @@
|
||||
import type { Vector2 } from './litegraph'
|
||||
import type { INodeSlot } from './interfaces'
|
||||
import { LinkDirection, RenderShape } from './types/globalEnums'
|
||||
import type { Vector2 } from "./litegraph";
|
||||
import type { INodeSlot } from "./interfaces"
|
||||
import { LinkDirection, RenderShape } from "./types/globalEnums"
|
||||
|
||||
export enum SlotType {
|
||||
Array = 'array',
|
||||
Array = "array",
|
||||
Event = -1,
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ export enum SlotDirection {
|
||||
}
|
||||
|
||||
export enum LabelPosition {
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
Left = "left",
|
||||
Right = "right",
|
||||
}
|
||||
|
||||
export function drawSlot(
|
||||
@@ -34,7 +34,7 @@ export function drawSlot(
|
||||
slot: Partial<INodeSlot>,
|
||||
pos: Vector2,
|
||||
{
|
||||
label_color = '#AAA',
|
||||
label_color = "#AAA",
|
||||
label_position = LabelPosition.Right,
|
||||
horizontal = false,
|
||||
low_quality = false,
|
||||
@@ -42,42 +42,44 @@ export function drawSlot(
|
||||
do_stroke = false,
|
||||
highlight = false,
|
||||
}: {
|
||||
label_color?: string
|
||||
label_position?: LabelPosition
|
||||
horizontal?: boolean
|
||||
low_quality?: boolean
|
||||
render_text?: boolean
|
||||
do_stroke?: boolean
|
||||
highlight?: boolean
|
||||
} = {},
|
||||
label_color?: string;
|
||||
label_position?: LabelPosition;
|
||||
horizontal?: boolean;
|
||||
low_quality?: boolean;
|
||||
render_text?: boolean;
|
||||
do_stroke?: boolean;
|
||||
highlight?: boolean;
|
||||
} = {}
|
||||
) {
|
||||
// Save the current fillStyle and strokeStyle
|
||||
const originalFillStyle = ctx.fillStyle
|
||||
const originalStrokeStyle = ctx.strokeStyle
|
||||
const originalLineWidth = ctx.lineWidth
|
||||
const originalFillStyle = ctx.fillStyle;
|
||||
const originalStrokeStyle = ctx.strokeStyle;
|
||||
const originalLineWidth = ctx.lineWidth;
|
||||
|
||||
const slot_type = slot.type as SlotType
|
||||
const slot_shape = (slot_type === SlotType.Array ? SlotShape.Grid : slot.shape) as SlotShape
|
||||
const slot_type = slot.type as SlotType;
|
||||
const slot_shape = (
|
||||
slot_type === SlotType.Array ? SlotShape.Grid : slot.shape
|
||||
) as SlotShape;
|
||||
|
||||
ctx.beginPath()
|
||||
let doStroke = do_stroke
|
||||
let doFill = true
|
||||
ctx.beginPath();
|
||||
let doStroke = do_stroke;
|
||||
let doFill = true;
|
||||
|
||||
if (slot_type === SlotType.Event || slot_shape === SlotShape.Box) {
|
||||
if (horizontal) {
|
||||
ctx.rect(pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, 14)
|
||||
ctx.rect(pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, 14);
|
||||
} else {
|
||||
ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10)
|
||||
ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10);
|
||||
}
|
||||
} else if (slot_shape === SlotShape.Arrow) {
|
||||
ctx.moveTo(pos[0] + 8, pos[1] + 0.5)
|
||||
ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5)
|
||||
ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5)
|
||||
ctx.closePath()
|
||||
ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
|
||||
ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5);
|
||||
ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5);
|
||||
ctx.closePath();
|
||||
} else if (slot_shape === SlotShape.Grid) {
|
||||
const gridSize = 3
|
||||
const cellSize = 2
|
||||
const spacing = 3
|
||||
const gridSize = 3;
|
||||
const cellSize = 2;
|
||||
const spacing = 3;
|
||||
|
||||
for (let x = 0; x < gridSize; x++) {
|
||||
for (let y = 0; y < gridSize; y++) {
|
||||
@@ -86,58 +88,58 @@ export function drawSlot(
|
||||
pos[1] - 4 + y * spacing,
|
||||
cellSize,
|
||||
cellSize
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
doStroke = false
|
||||
doStroke = false;
|
||||
} else {
|
||||
// Default rendering for circle, hollow circle.
|
||||
if (low_quality) {
|
||||
ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8)
|
||||
ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8);
|
||||
} else {
|
||||
let radius: number
|
||||
let radius: number;
|
||||
if (slot_shape === SlotShape.HollowCircle) {
|
||||
doFill = false
|
||||
doStroke = true
|
||||
ctx.lineWidth = 3
|
||||
ctx.strokeStyle = ctx.fillStyle
|
||||
radius = highlight ? 4 : 3
|
||||
doFill = false;
|
||||
doStroke = true;
|
||||
ctx.lineWidth = 3;
|
||||
ctx.strokeStyle = ctx.fillStyle;
|
||||
radius = highlight ? 4 : 3;
|
||||
} else {
|
||||
// Normal circle
|
||||
radius = highlight ? 5 : 4
|
||||
radius = highlight ? 5 : 4;
|
||||
}
|
||||
ctx.arc(pos[0], pos[1], radius, 0, Math.PI * 2)
|
||||
ctx.arc(pos[0], pos[1], radius, 0, Math.PI * 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (doFill) ctx.fill()
|
||||
if (!low_quality && doStroke) ctx.stroke()
|
||||
if (doFill) ctx.fill();
|
||||
if (!low_quality && doStroke) ctx.stroke();
|
||||
|
||||
// render slot label
|
||||
if (render_text) {
|
||||
const text = slot.label != null ? slot.label : slot.name
|
||||
const text = slot.label != null ? slot.label : slot.name;
|
||||
if (text) {
|
||||
// TODO: Finish impl. Highlight text on mouseover unless we're connecting links.
|
||||
ctx.fillStyle = label_color
|
||||
ctx.fillStyle = label_color;
|
||||
|
||||
if (label_position === LabelPosition.Right) {
|
||||
if (horizontal || slot.dir == LinkDirection.UP) {
|
||||
ctx.fillText(text, pos[0], pos[1] - 10)
|
||||
ctx.fillText(text, pos[0], pos[1] - 10);
|
||||
} else {
|
||||
ctx.fillText(text, pos[0] + 10, pos[1] + 5)
|
||||
ctx.fillText(text, pos[0] + 10, pos[1] + 5);
|
||||
}
|
||||
} else {
|
||||
if (horizontal || slot.dir == LinkDirection.DOWN) {
|
||||
ctx.fillText(text, pos[0], pos[1] - 8)
|
||||
ctx.fillText(text, pos[0], pos[1] - 8);
|
||||
} else {
|
||||
ctx.fillText(text, pos[0] - 10, pos[1] + 5)
|
||||
ctx.fillText(text, pos[0] - 10, pos[1] + 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the original fillStyle and strokeStyle
|
||||
ctx.fillStyle = originalFillStyle
|
||||
ctx.strokeStyle = originalStrokeStyle
|
||||
ctx.lineWidth = originalLineWidth
|
||||
ctx.fillStyle = originalFillStyle;
|
||||
ctx.strokeStyle = originalStrokeStyle;
|
||||
ctx.lineWidth = originalLineWidth;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ContextMenu } from './ContextMenu'
|
||||
import type { LGraphNode } from './LGraphNode'
|
||||
import type { LinkDirection, RenderShape } from './types/globalEnums'
|
||||
import type { LinkId } from './LLink'
|
||||
import type { ContextMenu } from "./ContextMenu"
|
||||
import type { LGraphNode } from "./LGraphNode"
|
||||
import type { LinkDirection, RenderShape } from "./types/globalEnums"
|
||||
import type { LinkId } from "./LLink"
|
||||
|
||||
export type Dictionary<T> = { [key: string]: T }
|
||||
|
||||
@@ -42,31 +42,13 @@ export type Rect = ArRect | Float32Array | Float64Array
|
||||
export type Rect32 = Float32Array
|
||||
|
||||
/** A point represented as `[x, y]` co-ordinates that will not be modified */
|
||||
export type ReadOnlyPoint =
|
||||
| readonly [x: number, y: number]
|
||||
| ReadOnlyTypedArray<Float32Array>
|
||||
| ReadOnlyTypedArray<Float64Array>
|
||||
export type ReadOnlyPoint = readonly [x: number, y: number] | ReadOnlyTypedArray<Float32Array> | ReadOnlyTypedArray<Float64Array>
|
||||
/** A rectangle starting at top-left coordinates `[x, y, width, height]` that will not be modified */
|
||||
export type ReadOnlyRect =
|
||||
| readonly [x: number, y: number, width: number, height: number]
|
||||
| ReadOnlyTypedArray<Float32Array>
|
||||
| ReadOnlyTypedArray<Float64Array>
|
||||
export type ReadOnlyRect = readonly [x: number, y: number, width: number, height: number] | ReadOnlyTypedArray<Float32Array> | ReadOnlyTypedArray<Float64Array>
|
||||
|
||||
type TypedArrays =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
type TypedArrays = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array
|
||||
type TypedBigIntArrays = BigInt64Array | BigUint64Array
|
||||
type ReadOnlyTypedArray<T extends TypedArrays | TypedBigIntArrays> = Omit<
|
||||
T,
|
||||
'fill' | 'copyWithin' | 'reverse' | 'set' | 'sort' | 'subarray'
|
||||
>
|
||||
type ReadOnlyTypedArray<T extends TypedArrays | TypedBigIntArrays> = Omit<T, "fill" | "copyWithin" | "reverse" | "set" | "sort" | "subarray">
|
||||
|
||||
/** 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]
|
||||
@@ -84,7 +66,7 @@ export interface IBoundaryNodes {
|
||||
left: LGraphNode
|
||||
}
|
||||
|
||||
export type Direction = 'top' | 'bottom' | 'left' | 'right'
|
||||
export type Direction = "top" | "bottom" | "left" | "right"
|
||||
|
||||
export interface IOptionalSlotData<TSlot extends INodeInputSlot | INodeOutputSlot> {
|
||||
content: string
|
||||
@@ -140,13 +122,7 @@ export interface ConnectingLink extends IInputOrOutput {
|
||||
interface IContextMenuBase {
|
||||
title?: string
|
||||
className?: string
|
||||
callback?(
|
||||
value?: unknown,
|
||||
options?: unknown,
|
||||
event?: MouseEvent,
|
||||
previous_menu?: ContextMenu,
|
||||
node?: LGraphNode,
|
||||
): void | boolean
|
||||
callback?(value?: unknown, options?: unknown, event?: MouseEvent, previous_menu?: ContextMenu, node?: LGraphNode): void | boolean
|
||||
}
|
||||
|
||||
/** ContextMenu */
|
||||
|
||||
@@ -1,73 +1,32 @@
|
||||
import type { Point, ConnectingLink } from './interfaces'
|
||||
import type {
|
||||
INodeSlot,
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
CanvasColour,
|
||||
Direction,
|
||||
IBoundaryNodes,
|
||||
IContextMenuOptions,
|
||||
IContextMenuValue,
|
||||
IFoundSlot,
|
||||
IInputOrOutput,
|
||||
INodeFlags,
|
||||
IOptionalSlotData,
|
||||
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 { RenderShape, TitleMode } from './types/globalEnums'
|
||||
import type { CanvasEventDetail } from './types/events'
|
||||
import { LiteGraphGlobal } from './LiteGraphGlobal'
|
||||
import { loadPolyfills } from './polyfills'
|
||||
import type { Point, ConnectingLink } from "./interfaces"
|
||||
import type { INodeSlot, INodeInputSlot, INodeOutputSlot, CanvasColour, Direction, IBoundaryNodes, IContextMenuOptions, IContextMenuValue, IFoundSlot, IInputOrOutput, INodeFlags, IOptionalSlotData, 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 { RenderShape, TitleMode } from "./types/globalEnums"
|
||||
import type { CanvasEventDetail } from "./types/events"
|
||||
import { LiteGraphGlobal } from "./LiteGraphGlobal"
|
||||
import { loadPolyfills } from "./polyfills"
|
||||
|
||||
import { LGraph } from './LGraph'
|
||||
import { LGraphCanvas, type LGraphCanvasState } 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'
|
||||
import { LGraph } from "./LGraph"
|
||||
import { LGraphCanvas, type LGraphCanvasState } 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, LGraphCanvasState, DragAndScale, LGraphNode, LGraphGroup, LLink, ContextMenu, CurveEditor }
|
||||
export {
|
||||
INodeSlot,
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
ConnectingLink,
|
||||
CanvasColour,
|
||||
Direction,
|
||||
IBoundaryNodes,
|
||||
IContextMenuOptions,
|
||||
IContextMenuValue,
|
||||
IFoundSlot,
|
||||
IInputOrOutput,
|
||||
INodeFlags,
|
||||
IOptionalSlotData,
|
||||
ISlotType,
|
||||
KeysOfType,
|
||||
MethodNames,
|
||||
PickByType,
|
||||
Rect,
|
||||
Rect32,
|
||||
Size,
|
||||
}
|
||||
export { INodeSlot, INodeInputSlot, INodeOutputSlot, ConnectingLink, CanvasColour, Direction, IBoundaryNodes, IContextMenuOptions, IContextMenuValue, IFoundSlot, IInputOrOutput, INodeFlags, IOptionalSlotData, ISlotType, KeysOfType, MethodNames, PickByType, Rect, Rect32, Size }
|
||||
export { IWidget }
|
||||
export { LGraphBadge, BadgePosition }
|
||||
export { SlotShape, LabelPosition, SlotDirection, SlotType }
|
||||
|
||||
export function clamp(v: number, a: number, b: number): number {
|
||||
return a > v ? a : b < v ? b : v
|
||||
}
|
||||
};
|
||||
|
||||
// Load legacy polyfills
|
||||
loadPolyfills()
|
||||
@@ -98,7 +57,7 @@ export type ContextMenuEventListener = (
|
||||
options: IContextMenuOptions,
|
||||
event: MouseEvent,
|
||||
parentMenu: ContextMenu | undefined,
|
||||
node: LGraphNode,
|
||||
node: LGraphNode
|
||||
) => boolean | void
|
||||
|
||||
export interface LinkReleaseContext {
|
||||
@@ -114,13 +73,12 @@ export interface LinkReleaseContextExtended {
|
||||
}
|
||||
|
||||
/** @deprecated Confirm no downstream consumers, then remove. */
|
||||
export type LiteGraphCanvasEventType = 'empty-release' | 'empty-double-click' | 'group-double-click'
|
||||
export type LiteGraphCanvasEventType = "empty-release" | "empty-double-click" | "group-double-click"
|
||||
|
||||
export interface LiteGraphCanvasEvent extends CustomEvent<CanvasEventDetail> { }
|
||||
|
||||
export interface LiteGraphCanvasGroupEvent
|
||||
extends CustomEvent<{
|
||||
subType: 'group-double-click'
|
||||
export interface LiteGraphCanvasGroupEvent extends CustomEvent<{
|
||||
subType: "group-double-click"
|
||||
originalEvent: MouseEvent
|
||||
group: LGraphGroup
|
||||
}> { }
|
||||
@@ -141,7 +99,6 @@ export interface LGraphNodeConstructor<T extends LGraphNode = LGraphNode> {
|
||||
title_mode?: TitleMode
|
||||
title_color?: string
|
||||
title_text_color?: string
|
||||
desc?: string
|
||||
nodeData: any
|
||||
new(): T
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Point, ReadOnlyPoint, ReadOnlyRect } from './interfaces'
|
||||
import { LinkDirection } from './types/globalEnums'
|
||||
import type { Point, ReadOnlyPoint, ReadOnlyRect } from "./interfaces"
|
||||
import { LinkDirection } from "./types/globalEnums"
|
||||
|
||||
/**
|
||||
* Calculates the distance between two points (2D vector)
|
||||
@@ -21,9 +21,7 @@ export function distance(a: ReadOnlyPoint, b: ReadOnlyPoint): number {
|
||||
* @returns Distance2 (squared) between point {@link a} & {@link b}
|
||||
*/
|
||||
export function dist2(a: ReadOnlyPoint, b: ReadOnlyPoint): number {
|
||||
return (
|
||||
(b[0] - a[0]) * (b[0] - a[0])) + ((b[1] - a[1]) * (b[1] - a[1])
|
||||
)
|
||||
return ((b[0] - a[0]) * (b[0] - a[0])) + ((b[1] - a[1]) * (b[1] - a[1]))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,12 +31,10 @@ export function dist2(a: ReadOnlyPoint, b: ReadOnlyPoint): number {
|
||||
* @returns `true` if the point is inside the rect, otherwise `false`
|
||||
*/
|
||||
export function isPointInRectangle(point: ReadOnlyPoint, rect: ReadOnlyRect): boolean {
|
||||
return (
|
||||
rect[0] < point[0] &&
|
||||
rect[0] + rect[2] > point[0] &&
|
||||
rect[1] < point[1] &&
|
||||
rect[1] + rect[3] > point[1]
|
||||
)
|
||||
return rect[0] < point[0]
|
||||
&& rect[0] + rect[2] > point[0]
|
||||
&& rect[1] < point[1]
|
||||
&& rect[1] + rect[3] > point[1]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +48,10 @@ export function isPointInRectangle(point: ReadOnlyPoint, rect: ReadOnlyRect): bo
|
||||
* @returns `true` if the point is inside the rect, otherwise `false`
|
||||
*/
|
||||
export function isInsideRectangle(x: number, y: number, left: number, top: number, width: number, height: number): boolean {
|
||||
return left < x && left + width > x && top < y && top + height > y
|
||||
return left < x
|
||||
&& left + width > x
|
||||
&& top < y
|
||||
&& top + height > y
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,12 +78,12 @@ export function overlapBounding(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
||||
const bRight = b[0] + b[2]
|
||||
const bBottom = b[1] + b[3]
|
||||
|
||||
return (
|
||||
a[0] > bRight ||
|
||||
a[1] > bBottom ||
|
||||
aRight < b[0] ||
|
||||
aBottom < b[1]
|
||||
) ? false : true
|
||||
return a[0] > bRight
|
||||
|| a[1] > bBottom
|
||||
|| aRight < b[0]
|
||||
|| aBottom < b[1]
|
||||
? false
|
||||
: true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,12 +110,10 @@ export function containsRect(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
||||
const bRight = b[0] + b[2]
|
||||
const bBottom = b[1] + b[3]
|
||||
|
||||
return (
|
||||
a[0] < b[0] &&
|
||||
a[1] < b[1] &&
|
||||
aRight > bRight &&
|
||||
aBottom > bBottom
|
||||
)
|
||||
return a[0] < b[0]
|
||||
&& a[1] < b[1]
|
||||
&& aRight > bRight
|
||||
&& aBottom > bBottom
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,9 +214,7 @@ export function rotateLink(offset: Point, from: LinkDirection, to: LinkDirection
|
||||
* @returns 0 if all three points are in a straight line, a negative value if point is to the left of the projected line, or positive if the point is to the right
|
||||
*/
|
||||
export function getOrientation(lineStart: ReadOnlyPoint, lineEnd: ReadOnlyPoint, x: number, y: number): number {
|
||||
return (
|
||||
(lineEnd[1] - lineStart[1]) * (x - lineEnd[0])) - ((lineEnd[0] - lineStart[0]) * (y - lineEnd[1])
|
||||
)
|
||||
return ((lineEnd[1] - lineStart[1]) * (x - lineEnd[0])) - ((lineEnd[0] - lineStart[0]) * (y - lineEnd[1]))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +232,7 @@ export function findPointOnCurve(
|
||||
b: ReadOnlyPoint,
|
||||
controlA: ReadOnlyPoint,
|
||||
controlB: ReadOnlyPoint,
|
||||
t: number = 0.5
|
||||
t: number = 0.5,
|
||||
): void {
|
||||
const iT = 1 - t
|
||||
|
||||
|
||||
115
src/polyfills.ts
115
src/polyfills.ts
@@ -1,80 +1,85 @@
|
||||
|
||||
//API *************************************************
|
||||
//like rect but rounded corners
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
webkitRequestAnimationFrame?: (callback: FrameRequestCallback) => number
|
||||
mozRequestAnimationFrame?: (callback: FrameRequestCallback) => number
|
||||
}
|
||||
}
|
||||
|
||||
export function loadPolyfills() {
|
||||
if (typeof window != 'undefined' && window.CanvasRenderingContext2D && !window.CanvasRenderingContext2D.prototype.roundRect) {
|
||||
if (typeof (window) != "undefined" && window.CanvasRenderingContext2D && !window.CanvasRenderingContext2D.prototype.roundRect) {
|
||||
// @ts-expect-error Slightly broken polyfill - radius_low not impl. anywhere
|
||||
window.CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, radius, radius_low) {
|
||||
let top_left_radius = 0
|
||||
let top_right_radius = 0
|
||||
let bottom_left_radius = 0
|
||||
let bottom_right_radius = 0
|
||||
window.CanvasRenderingContext2D.prototype.roundRect = function (
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
radius,
|
||||
radius_low
|
||||
) {
|
||||
let top_left_radius = 0;
|
||||
let top_right_radius = 0;
|
||||
let bottom_left_radius = 0;
|
||||
let bottom_right_radius = 0;
|
||||
|
||||
if (radius === 0) {
|
||||
this.rect(x, y, w, h)
|
||||
return
|
||||
this.rect(x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
if (radius_low === undefined) {
|
||||
radius_low = radius
|
||||
}
|
||||
if (radius_low === undefined)
|
||||
radius_low = radius;
|
||||
|
||||
//make it compatible with official one
|
||||
if (radius != null && radius.constructor === Array) {
|
||||
if (radius.length == 1) {
|
||||
top_left_radius = top_right_radius = bottom_left_radius = bottom_right_radius = radius[0]
|
||||
} else if (radius.length == 2) {
|
||||
top_left_radius = bottom_right_radius = radius[0]
|
||||
top_right_radius = bottom_left_radius = radius[1]
|
||||
} else if (radius.length == 4) {
|
||||
top_left_radius = radius[0]
|
||||
top_right_radius = radius[1]
|
||||
bottom_left_radius = radius[2]
|
||||
bottom_right_radius = radius[3]
|
||||
} else return
|
||||
} //old using numbers
|
||||
else {
|
||||
top_left_radius = radius || 0
|
||||
top_right_radius = radius || 0
|
||||
bottom_left_radius = radius_low || 0
|
||||
bottom_right_radius = radius_low || 0
|
||||
if (radius.length == 1)
|
||||
top_left_radius = top_right_radius = bottom_left_radius = bottom_right_radius = radius[0];
|
||||
else if (radius.length == 2) {
|
||||
top_left_radius = bottom_right_radius = radius[0];
|
||||
top_right_radius = bottom_left_radius = radius[1];
|
||||
}
|
||||
else if (radius.length == 4) {
|
||||
top_left_radius = radius[0];
|
||||
top_right_radius = radius[1];
|
||||
bottom_left_radius = radius[2];
|
||||
bottom_right_radius = radius[3];
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
else //old using numbers
|
||||
{
|
||||
top_left_radius = radius || 0;
|
||||
top_right_radius = radius || 0;
|
||||
bottom_left_radius = radius_low || 0;
|
||||
bottom_right_radius = radius_low || 0;
|
||||
}
|
||||
|
||||
//top right
|
||||
this.moveTo(x + top_left_radius, y)
|
||||
this.lineTo(x + w - top_right_radius, y)
|
||||
this.quadraticCurveTo(x + w, y, x + w, y + top_right_radius)
|
||||
this.moveTo(x + top_left_radius, y);
|
||||
this.lineTo(x + w - top_right_radius, y);
|
||||
this.quadraticCurveTo(x + w, y, x + w, y + top_right_radius);
|
||||
|
||||
//bottom right
|
||||
this.lineTo(x + w, y + h - bottom_right_radius)
|
||||
this.quadraticCurveTo(x + w, y + h, x + w - bottom_right_radius, y + h)
|
||||
this.lineTo(x + w, y + h - bottom_right_radius);
|
||||
this.quadraticCurveTo(
|
||||
x + w,
|
||||
y + h,
|
||||
x + w - bottom_right_radius,
|
||||
y + h
|
||||
);
|
||||
|
||||
//bottom left
|
||||
this.lineTo(x + bottom_right_radius, y + h)
|
||||
this.quadraticCurveTo(x, y + h, x, y + h - bottom_left_radius)
|
||||
this.lineTo(x + bottom_right_radius, y + h);
|
||||
this.quadraticCurveTo(x, y + h, x, y + h - bottom_left_radius);
|
||||
|
||||
//top left
|
||||
this.lineTo(x, y + bottom_left_radius)
|
||||
this.quadraticCurveTo(x, y, x + top_left_radius, y)
|
||||
}
|
||||
this.lineTo(x, y + bottom_left_radius);
|
||||
this.quadraticCurveTo(x, y, x + top_left_radius, y);
|
||||
};
|
||||
}//if
|
||||
|
||||
if (typeof window != 'undefined' && !window['requestAnimationFrame']) {
|
||||
const RAF = (
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
if (typeof window != "undefined" && !window["requestAnimationFrame"]) {
|
||||
window.requestAnimationFrame =
|
||||
// @ts-expect-error Legacy code
|
||||
window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
window.setTimeout(callback, 1000 / 60)
|
||||
}
|
||||
) as typeof window.requestAnimationFrame
|
||||
|
||||
window.requestAnimationFrame = RAF
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,5 @@ export function stringOrNull(value: unknown): string | null {
|
||||
* @returns String(value) or ""
|
||||
*/
|
||||
export function stringOrEmpty(value: unknown): string {
|
||||
return value == null ? '' : String(value)
|
||||
return value == null ? "" : String(value)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
* Event interfaces for event extension
|
||||
*/
|
||||
|
||||
import type { ConnectingLink, LinkReleaseContextExtended } from '@/litegraph'
|
||||
import type { IWidget } from '@/types/widgets'
|
||||
import type { LGraphNode } from '@/LGraphNode'
|
||||
import type { LGraphGroup } from '@/LGraphGroup'
|
||||
import type { ConnectingLink, LinkReleaseContextExtended } from "@/litegraph"
|
||||
import type { IWidget } from "@/types/widgets"
|
||||
import type { LGraphNode } from "@/LGraphNode"
|
||||
import type { LGraphGroup } from "@/LGraphGroup"
|
||||
|
||||
/** For Canvas*Event - adds graph space co-ordinates (property names are shipped) */
|
||||
export interface ICanvasPosition {
|
||||
@@ -25,10 +25,7 @@ export interface IDeltaPosition {
|
||||
export interface CanvasPointerEvent extends PointerEvent, CanvasMouseEvent { }
|
||||
|
||||
/** MouseEvent with canvasX/Y and deltaX/Y properties */
|
||||
export interface CanvasMouseEvent
|
||||
extends MouseEvent,
|
||||
ICanvasPosition,
|
||||
IDeltaPosition {
|
||||
export interface CanvasMouseEvent extends MouseEvent, ICanvasPosition, IDeltaPosition {
|
||||
/** @deprecated Part of DragAndScale mouse API - incomplete / not maintained */
|
||||
dragging?: boolean
|
||||
click_time?: number
|
||||
@@ -43,19 +40,13 @@ export interface CanvasWheelEvent extends WheelEvent, ICanvasPosition {
|
||||
}
|
||||
|
||||
/** DragEvent with canvasX/Y and deltaX/Y properties */
|
||||
export interface CanvasDragEvent
|
||||
extends DragEvent,
|
||||
ICanvasPosition,
|
||||
IDeltaPosition {}
|
||||
export interface CanvasDragEvent extends DragEvent, ICanvasPosition, IDeltaPosition { }
|
||||
|
||||
/** TouchEvent with canvasX/Y and deltaX/Y properties */
|
||||
export interface CanvasTouchEvent
|
||||
extends TouchEvent,
|
||||
ICanvasPosition,
|
||||
IDeltaPosition {}
|
||||
export interface CanvasTouchEvent extends TouchEvent, ICanvasPosition, IDeltaPosition { }
|
||||
|
||||
export type CanvasEventDetail =
|
||||
| GenericEventDetail
|
||||
GenericEventDetail
|
||||
| DragggingCanvasEventDetail
|
||||
| ReadOnlyEventDetail
|
||||
| GroupDoubleClickEventDetail
|
||||
@@ -64,40 +55,40 @@ export type CanvasEventDetail =
|
||||
| EmptyReleaseEventDetail
|
||||
|
||||
export interface GenericEventDetail {
|
||||
subType: 'before-change' | 'after-change'
|
||||
subType: "before-change" | "after-change"
|
||||
}
|
||||
|
||||
export interface OriginalEvent {
|
||||
originalEvent: CanvasPointerEvent
|
||||
originalEvent: CanvasPointerEvent,
|
||||
}
|
||||
|
||||
export interface EmptyReleaseEventDetail extends OriginalEvent {
|
||||
subType: 'empty-release'
|
||||
linkReleaseContext: LinkReleaseContextExtended
|
||||
subType: "empty-release",
|
||||
linkReleaseContext: LinkReleaseContextExtended,
|
||||
}
|
||||
|
||||
export interface ConnectingWidgetLinkEventDetail {
|
||||
subType: 'connectingWidgetLink'
|
||||
subType: "connectingWidgetLink"
|
||||
link: ConnectingLink
|
||||
node: LGraphNode
|
||||
widget: IWidget
|
||||
}
|
||||
|
||||
export interface EmptyDoubleClickEventDetail extends OriginalEvent {
|
||||
subType: 'empty-double-click'
|
||||
subType: "empty-double-click"
|
||||
}
|
||||
|
||||
export interface GroupDoubleClickEventDetail extends OriginalEvent {
|
||||
subType: 'group-double-click'
|
||||
subType: "group-double-click"
|
||||
group: LGraphGroup
|
||||
}
|
||||
|
||||
export interface DragggingCanvasEventDetail {
|
||||
subType: 'dragging-canvas'
|
||||
subType: "dragging-canvas"
|
||||
draggingCanvas: boolean
|
||||
}
|
||||
|
||||
export interface ReadOnlyEventDetail {
|
||||
subType: 'read-only'
|
||||
subType: "read-only"
|
||||
readOnly: boolean
|
||||
}
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import type {
|
||||
ISlotType,
|
||||
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'
|
||||
import { RenderShape } from './globalEnums'
|
||||
import type { ISlotType, 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"
|
||||
import { RenderShape } from "./globalEnums"
|
||||
|
||||
/**
|
||||
* An object that implements custom pre-serialization logic via {@link Serialisable.asSerialisable}.
|
||||
@@ -51,17 +42,17 @@ export interface ISerialisedNode {
|
||||
|
||||
/** Contains serialised graph elements */
|
||||
export type ISerialisedGraph<
|
||||
TNode = ReturnType<LGraphNode['serialize']>,
|
||||
TLink = ReturnType<LLink['serialize']>,
|
||||
TGroup = ReturnType<LGraphGroup['serialize']>,
|
||||
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']
|
||||
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[]
|
||||
groups: TGroup[]
|
||||
config: LGraph['config']
|
||||
config: LGraph["config"]
|
||||
version: typeof LiteGraph.VERSION
|
||||
extra?: unknown
|
||||
}
|
||||
@@ -75,13 +66,7 @@ export interface ISerialisedGroup {
|
||||
flags?: IGraphGroupFlags
|
||||
}
|
||||
|
||||
export type TClipboardLink = [
|
||||
targetRelativeIndex: number,
|
||||
originSlot: number,
|
||||
nodeRelativeIndex: number,
|
||||
targetSlot: number,
|
||||
targetNodeId: NodeId,
|
||||
]
|
||||
export type TClipboardLink = [targetRelativeIndex: number, originSlot: number, nodeRelativeIndex: number, targetSlot: number, targetNodeId: NodeId]
|
||||
|
||||
/** */
|
||||
export interface IClipboardContents {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { CanvasColour, Point, Size } from '@/interfaces'
|
||||
import type { LGraphCanvas, LGraphNode } from '@/litegraph'
|
||||
import type { CanvasMouseEvent } from './events'
|
||||
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> {
|
||||
export interface IWidgetOptions<TValue = unknown> extends Record<string, unknown> {
|
||||
on?: string
|
||||
off?: string
|
||||
max?: number
|
||||
@@ -21,7 +20,7 @@ export interface IWidgetOptions<TValue = unknown>
|
||||
hasOwnProperty?(arg0: string): any
|
||||
// values?(widget?: IWidget, node?: LGraphNode): any
|
||||
values?: TValue[]
|
||||
callback?: IWidget['callback']
|
||||
callback?: IWidget["callback"]
|
||||
|
||||
onHide?(widget: IWidget): void
|
||||
}
|
||||
@@ -35,47 +34,37 @@ export interface IWidgetOptions<TValue = unknown>
|
||||
* 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 type IWidget = IBooleanWidget | INumericWidget | IStringWidget | IMultilineStringWidget | IComboWidget | ICustomWidget
|
||||
|
||||
export interface IBooleanWidget extends IBaseWidget {
|
||||
type?: 'toggle'
|
||||
type?: "toggle"
|
||||
value: boolean
|
||||
}
|
||||
|
||||
/** Any widget that uses a numeric backing */
|
||||
export interface INumericWidget extends IBaseWidget {
|
||||
type?: 'slider' | 'number'
|
||||
type?: "slider" | "number"
|
||||
value: number
|
||||
}
|
||||
|
||||
/** A combo-box widget (dropdown, select, etc) */
|
||||
export interface IComboWidget extends IBaseWidget {
|
||||
type?: 'combo'
|
||||
type?: "combo"
|
||||
value: string | number
|
||||
options: IWidgetOptions<string>
|
||||
}
|
||||
|
||||
export type IStringWidgetType =
|
||||
| IStringWidget['type']
|
||||
| IMultilineStringWidget['type']
|
||||
export type IStringWidgetType = IStringWidget["type"] | IMultilineStringWidget["type"]
|
||||
|
||||
/** A widget with a string value */
|
||||
export interface IStringWidget extends IBaseWidget {
|
||||
type?: 'string' | 'text' | 'button'
|
||||
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'
|
||||
export interface IMultilineStringWidget<TElement extends HTMLElement = HTMLTextAreaElement> extends IBaseWidget {
|
||||
type?: "multiline"
|
||||
value: string
|
||||
|
||||
/** HTML textarea element */
|
||||
@@ -83,21 +72,21 @@ export interface IMultilineStringWidget<
|
||||
}
|
||||
|
||||
/** 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'
|
||||
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']
|
||||
export type TWidgetType = IWidget["type"]
|
||||
export type TWidgetValue = IWidget["value"]
|
||||
|
||||
/**
|
||||
* The base type for all widgets. Should not be implemented directly.
|
||||
@@ -128,23 +117,11 @@ export interface IBaseWidget<TElement extends HTMLElement = HTMLElement> {
|
||||
element?: TElement
|
||||
|
||||
// TODO: Confirm this format
|
||||
callback?(
|
||||
value: any,
|
||||
canvas?: LGraphCanvas,
|
||||
node?: LGraphNode,
|
||||
pos?: Point,
|
||||
e?: CanvasMouseEvent,
|
||||
): void
|
||||
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
|
||||
draw?(ctx: CanvasRenderingContext2D, node: LGraphNode, widget_width: number, y: number, H: number): void
|
||||
computeSize?(width: number): Size
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Dictionary, Direction, IBoundaryNodes } from '@/interfaces'
|
||||
import type { LGraphNode } from '@/LGraphNode'
|
||||
import type { Dictionary, Direction, IBoundaryNodes } from "@/interfaces"
|
||||
import type { LGraphNode } from "@/LGraphNode"
|
||||
|
||||
/**
|
||||
* Finds the nodes that are farthest in all four directions, representing the boundary of the nodes.
|
||||
@@ -7,7 +7,7 @@ import type { LGraphNode } from '@/LGraphNode'
|
||||
* @returns An object listing the furthest node (edge) in all four directions. `null` if no nodes were supplied or the first node was falsy.
|
||||
*/
|
||||
export function getBoundaryNodes(nodes: LGraphNode[]): IBoundaryNodes | null {
|
||||
const valid = nodes?.find((x) => x)
|
||||
const valid = nodes?.find(x => x)
|
||||
if (!valid) return null
|
||||
|
||||
let top = valid
|
||||
@@ -30,7 +30,7 @@ export function getBoundaryNodes(nodes: LGraphNode[]): IBoundaryNodes | null {
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
left
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +57,11 @@ export function distributeNodes(nodes: LGraphNode[], horizontal?: boolean): void
|
||||
const sorted = [...nodes].sort((a, b) => a.pos[index] - b.pos[index])
|
||||
const lowest = sorted[0].pos[index]
|
||||
|
||||
const gap = (highest - lowest - total) / (nodeCount - 1)
|
||||
const gap = ((highest - lowest) - total) / (nodeCount - 1)
|
||||
let startAt = lowest
|
||||
for (let i = 0; i < nodeCount; i++) {
|
||||
const node = sorted[i]
|
||||
node.pos[index] = startAt + gap * i
|
||||
node.pos[index] = startAt + (gap * i)
|
||||
startAt += node.size[index]
|
||||
}
|
||||
}
|
||||
@@ -75,30 +75,29 @@ export function distributeNodes(nodes: LGraphNode[], horizontal?: boolean): void
|
||||
export function alignNodes(nodes: LGraphNode[], direction: Direction, align_to?: LGraphNode): void {
|
||||
if (!nodes) return
|
||||
|
||||
const boundary =
|
||||
align_to === undefined
|
||||
const boundary = align_to === undefined
|
||||
? getBoundaryNodes(nodes)
|
||||
: {
|
||||
top: align_to,
|
||||
right: align_to,
|
||||
bottom: align_to,
|
||||
left: align_to,
|
||||
left: align_to
|
||||
}
|
||||
|
||||
if (boundary === null) return
|
||||
|
||||
for (const node of nodes) {
|
||||
switch (direction) {
|
||||
case 'right':
|
||||
case "right":
|
||||
node.pos[0] = boundary.right.pos[0] + boundary.right.size[0] - node.size[0]
|
||||
break
|
||||
case 'left':
|
||||
case "left":
|
||||
node.pos[0] = boundary.left.pos[0]
|
||||
break
|
||||
case 'top':
|
||||
case "top":
|
||||
node.pos[1] = boundary.top.pos[1]
|
||||
break
|
||||
case 'bottom':
|
||||
case "bottom":
|
||||
node.pos[1] = boundary.bottom.pos[1] + boundary.bottom.size[1] - node.size[1]
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
import { LGraph, LGraphGroup, LGraphNode, LiteGraph } from '../src/litegraph'
|
||||
import { LiteGraphGlobal } from '../src/LiteGraphGlobal'
|
||||
import { LGraph, LGraphGroup, LGraphNode, LiteGraph } from "../src/litegraph"
|
||||
import { LiteGraphGlobal } from "../src/LiteGraphGlobal"
|
||||
|
||||
function makeGraph() {
|
||||
const LiteGraph = new LiteGraphGlobal()
|
||||
LiteGraph.registerNodeType('TestNode', LGraphNode)
|
||||
LiteGraph.registerNodeType('OtherNode', LGraphNode)
|
||||
LiteGraph.registerNodeType('', LGraphNode)
|
||||
LiteGraph.registerNodeType("TestNode", LGraphNode)
|
||||
LiteGraph.registerNodeType("OtherNode", LGraphNode)
|
||||
LiteGraph.registerNodeType("", LGraphNode)
|
||||
return new LGraph()
|
||||
}
|
||||
|
||||
describe('LGraph', () => {
|
||||
it('can be instantiated', () => {
|
||||
describe("LGraph", () => {
|
||||
it("can be instantiated", () => {
|
||||
// @ts-ignore TODO: Remove once relative imports fix goes in.
|
||||
const graph = new LGraph({ extra: 'TestGraph' })
|
||||
const graph = new LGraph({ extra: "TestGraph" })
|
||||
expect(graph).toBeInstanceOf(LGraph)
|
||||
expect(graph.extra).toBe('TestGraph')
|
||||
expect(graph.extra).toBe("TestGraph")
|
||||
})
|
||||
})
|
||||
|
||||
describe('Legacy LGraph Compatibility Layer', () => {
|
||||
it('can be extended via prototype', () => {
|
||||
describe("Legacy LGraph Compatibility Layer", () => {
|
||||
it("can be extended via prototype", () => {
|
||||
const graph = new LGraph()
|
||||
// @ts-expect-error Should always be an error.
|
||||
LGraph.prototype.newMethod = function () {
|
||||
return 'New method added via prototype'
|
||||
return "New method added via prototype"
|
||||
}
|
||||
// @ts-expect-error Should always be an error.
|
||||
expect(graph.newMethod()).toBe('New method added via prototype')
|
||||
expect(graph.newMethod()).toBe("New method added via prototype")
|
||||
})
|
||||
|
||||
it('is correctly assigned to LiteGraph', () => {
|
||||
it("is correctly assigned to LiteGraph", () => {
|
||||
expect(LiteGraph.LGraph).toBe(LGraph)
|
||||
})
|
||||
})
|
||||
|
||||
describe('LGraph Serialisation', () => {
|
||||
it('should serialise', () => {
|
||||
describe("LGraph Serialisation", () => {
|
||||
it("should serialise", () => {
|
||||
const graph = new LGraph()
|
||||
graph.add(new LGraphNode('Test Node'))
|
||||
graph.add(new LGraphGroup('Test Group'))
|
||||
graph.add(new LGraphNode("Test Node"))
|
||||
graph.add(new LGraphGroup("Test Group"))
|
||||
expect(graph.nodes.length).toBe(1)
|
||||
expect(graph.groups.length).toBe(1)
|
||||
})
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { LGraphNode } from '../src/litegraph'
|
||||
import {
|
||||
LGraphNode,
|
||||
} from "../src/litegraph"
|
||||
|
||||
describe('LGraphNode', () => {
|
||||
it('should serialize position correctly', () => {
|
||||
const node = new LGraphNode('TestNode')
|
||||
describe("LGraphNode", () => {
|
||||
it("should serialize position correctly", () => {
|
||||
const node = new LGraphNode("TestNode")
|
||||
node.pos = [10, 10]
|
||||
expect(node.pos).toEqual(new Float32Array([10, 10]))
|
||||
expect(node.serialize().pos).toEqual(new Float32Array([10, 10]))
|
||||
})
|
||||
|
||||
})
|
||||
Reference in New Issue
Block a user