mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-03 14:54:37 +00:00
refactor: migrate ES private fields to TypeScript private for Vue Proxy compatibility (#8440)
## Summary Migrates ECMAScript private fields (`#`) to TypeScript private (`private`) across LiteGraph to fix Vue Proxy reactivity incompatibility. ## Problem ES private fields (`#field`) are incompatible with Vue's Proxy-based reactivity system - accessing `#field` through a Proxy throws `TypeError: Cannot read private member from an object whose class did not declare it`. ## Solution - Converted all `#field` to `private _field` across 10 phases - Added `toJSON()` methods to `LGraph`, `NodeSlot`, `NodeInputSlot`, and `NodeOutputSlot` to prevent circular reference errors during serialization (TypeScript private fields are visible to `JSON.stringify` unlike true ES private fields) - Made `DragAndScale.element.data` non-enumerable to break canvas circular reference chain ## Testing - All 4027 unit tests pass - Added 9 new serialization tests to catch future circular reference issues - Browser tests (undo/redo, save workflows) verified working ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8440-refactor-migrate-ES-private-fields-to-TypeScript-private-for-Vue-Proxy-compatibility-2f76d73d365081a3bd82d429a3e0fcb7) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -292,10 +292,10 @@ export class GroupNodeConfig {
|
||||
this.processNode(node, seenInputs, seenOutputs)
|
||||
}
|
||||
|
||||
for (const p of this.#convertedToProcess) {
|
||||
for (const p of this._convertedToProcess) {
|
||||
p()
|
||||
}
|
||||
this.#convertedToProcess = []
|
||||
this._convertedToProcess = []
|
||||
if (!this.nodeDef) return
|
||||
await app.registerNodeDef(`${PREFIX}${SEPARATOR}` + this.name, this.nodeDef)
|
||||
useNodeDefStore().addNodeDef(this.nodeDef)
|
||||
@@ -773,7 +773,7 @@ export class GroupNodeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#convertedToProcess: (() => void)[] = []
|
||||
private _convertedToProcess: (() => void)[] = []
|
||||
processNodeInputs(
|
||||
node: GroupNodeData,
|
||||
seenInputs: Record<string, number>,
|
||||
@@ -804,7 +804,7 @@ export class GroupNodeConfig {
|
||||
)
|
||||
|
||||
// Converted inputs have to be processed after all other nodes as they'll be at the end of the list
|
||||
this.#convertedToProcess.push(() =>
|
||||
this._convertedToProcess.push(() =>
|
||||
this.processConvertedWidgets(
|
||||
inputs,
|
||||
node,
|
||||
|
||||
@@ -103,7 +103,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
|
||||
override onAfterGraphConfigured() {
|
||||
if (this.outputs[0].links?.length && !this.widgets?.length) {
|
||||
this.#onFirstConnection()
|
||||
this._onFirstConnection()
|
||||
|
||||
// Populate widget values from config data
|
||||
if (this.widgets && this.widgets_values) {
|
||||
@@ -116,7 +116,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
}
|
||||
|
||||
// Merge values if required
|
||||
this.#mergeWidgetConfig()
|
||||
this._mergeWidgetConfig()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,11 +133,11 @@ export class PrimitiveNode extends LGraphNode {
|
||||
const links = this.outputs[0].links
|
||||
if (connected) {
|
||||
if (links?.length && !this.widgets?.length) {
|
||||
this.#onFirstConnection()
|
||||
this._onFirstConnection()
|
||||
}
|
||||
} else {
|
||||
// We may have removed a link that caused the constraints to change
|
||||
this.#mergeWidgetConfig()
|
||||
this._mergeWidgetConfig()
|
||||
|
||||
if (!links?.length) {
|
||||
this.onLastDisconnect()
|
||||
@@ -159,7 +159,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
}
|
||||
|
||||
if (this.outputs[slot].links?.length) {
|
||||
const valid = this.#isValidConnection(input)
|
||||
const valid = this._isValidConnection(input)
|
||||
if (valid) {
|
||||
// On connect of additional outputs, copy our value to their widget
|
||||
this.applyToGraph([{ target_id: target_node.id, target_slot } as LLink])
|
||||
@@ -170,7 +170,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
return true
|
||||
}
|
||||
|
||||
#onFirstConnection(recreating?: boolean) {
|
||||
private _onFirstConnection(recreating?: boolean) {
|
||||
// First connection can fire before the graph is ready on initial load so random things can be missing
|
||||
if (!this.outputs[0].links || !this.graph) {
|
||||
this.onLastDisconnect()
|
||||
@@ -204,7 +204,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
this.outputs[0].name = type
|
||||
this.outputs[0].widget = widget
|
||||
|
||||
this.#createWidget(
|
||||
this._createWidget(
|
||||
widget[CONFIG] ?? config,
|
||||
theirNode,
|
||||
widget.name,
|
||||
@@ -213,7 +213,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
)
|
||||
}
|
||||
|
||||
#createWidget(
|
||||
private _createWidget(
|
||||
inputData: InputSpec,
|
||||
node: LGraphNode,
|
||||
widgetName: string,
|
||||
@@ -307,8 +307,8 @@ export class PrimitiveNode extends LGraphNode {
|
||||
|
||||
recreateWidget() {
|
||||
const values = this.widgets?.map((w) => w.value)
|
||||
this.#removeWidgets()
|
||||
this.#onFirstConnection(true)
|
||||
this._removeWidgets()
|
||||
this._onFirstConnection(true)
|
||||
if (values?.length && this.widgets) {
|
||||
for (let i = 0; i < this.widgets.length; i++)
|
||||
this.widgets[i].value = values[i]
|
||||
@@ -316,7 +316,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
return this.widgets?.[0]
|
||||
}
|
||||
|
||||
#mergeWidgetConfig() {
|
||||
private _mergeWidgetConfig() {
|
||||
// Merge widget configs if the node has multiple outputs
|
||||
const output = this.outputs[0]
|
||||
const links = output.links ?? []
|
||||
@@ -348,11 +348,11 @@ export class PrimitiveNode extends LGraphNode {
|
||||
const theirInput = theirNode.inputs[link.target_slot]
|
||||
|
||||
// Call is valid connection so it can merge the configs when validating
|
||||
this.#isValidConnection(theirInput, hasConfig)
|
||||
this._isValidConnection(theirInput, hasConfig)
|
||||
}
|
||||
}
|
||||
|
||||
#isValidConnection(input: INodeInputSlot, forceUpdate?: boolean) {
|
||||
private _isValidConnection(input: INodeInputSlot, forceUpdate?: boolean) {
|
||||
// Only allow connections where the configs match
|
||||
const output = this.outputs?.[0]
|
||||
const config2 = (input.widget?.[GET_CONFIG] as () => InputSpec)?.()
|
||||
@@ -367,7 +367,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
)
|
||||
}
|
||||
|
||||
#removeWidgets() {
|
||||
private _removeWidgets() {
|
||||
if (this.widgets) {
|
||||
// Allow widgets to cleanup
|
||||
for (const w of this.widgets) {
|
||||
@@ -398,7 +398,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
this.outputs[0].name = 'connect to widget input'
|
||||
delete this.outputs[0].widget
|
||||
|
||||
this.#removeWidgets()
|
||||
this._removeWidgets()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,17 +31,17 @@ export class CanvasPointer {
|
||||
|
||||
/** Maximum offset from click location */
|
||||
static get maxClickDrift() {
|
||||
return this.#maxClickDrift
|
||||
return this._maxClickDrift
|
||||
}
|
||||
|
||||
static set maxClickDrift(value) {
|
||||
this.#maxClickDrift = value
|
||||
this.#maxClickDrift2 = value * value
|
||||
this._maxClickDrift = value
|
||||
this._maxClickDrift2 = value * value
|
||||
}
|
||||
|
||||
static #maxClickDrift = 6
|
||||
private static _maxClickDrift = 6
|
||||
/** {@link maxClickDrift} squared. Used to calculate click drift without `sqrt`. */
|
||||
static #maxClickDrift2 = this.#maxClickDrift ** 2
|
||||
private static _maxClickDrift2 = this._maxClickDrift ** 2
|
||||
|
||||
/** Assume that "wheel" events with both deltaX and deltaY less than this value are trackpad gestures. */
|
||||
static trackpadThreshold = 60
|
||||
@@ -153,18 +153,18 @@ export class CanvasPointer {
|
||||
* Therefore, simply setting this value twice will execute the first callback.
|
||||
*/
|
||||
get finally() {
|
||||
return this.#finally
|
||||
return this._finally
|
||||
}
|
||||
|
||||
set finally(value) {
|
||||
try {
|
||||
this.#finally?.()
|
||||
this._finally?.()
|
||||
} finally {
|
||||
this.#finally = value
|
||||
this._finally = value
|
||||
}
|
||||
}
|
||||
|
||||
#finally?: () => unknown
|
||||
private _finally?: () => unknown
|
||||
|
||||
constructor(element: Element) {
|
||||
this.element = element
|
||||
@@ -197,7 +197,7 @@ export class CanvasPointer {
|
||||
|
||||
// Primary button released - treat as pointerup.
|
||||
if (!(e.buttons & eDown.buttons)) {
|
||||
this.#completeClick(e)
|
||||
this._completeClick(e)
|
||||
this.reset()
|
||||
return
|
||||
}
|
||||
@@ -209,8 +209,8 @@ export class CanvasPointer {
|
||||
|
||||
const longerThanBufferTime =
|
||||
e.timeStamp - eDown.timeStamp > CanvasPointer.bufferTime
|
||||
if (longerThanBufferTime || !this.#hasSamePosition(e, eDown)) {
|
||||
this.#setDragStarted(e)
|
||||
if (longerThanBufferTime || !this._hasSamePosition(e, eDown)) {
|
||||
this._setDragStarted(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,13 +221,13 @@ export class CanvasPointer {
|
||||
up(e: CanvasPointerEvent): boolean {
|
||||
if (e.button !== this.eDown?.button) return false
|
||||
|
||||
this.#completeClick(e)
|
||||
this._completeClick(e)
|
||||
const { dragStarted } = this
|
||||
this.reset()
|
||||
return !dragStarted
|
||||
}
|
||||
|
||||
#completeClick(e: CanvasPointerEvent): void {
|
||||
private _completeClick(e: CanvasPointerEvent): void {
|
||||
const { eDown } = this
|
||||
if (!eDown) return
|
||||
|
||||
@@ -236,11 +236,11 @@ export class CanvasPointer {
|
||||
if (this.dragStarted) {
|
||||
// A move event already started drag
|
||||
this.onDragEnd?.(e)
|
||||
} else if (!this.#hasSamePosition(e, eDown)) {
|
||||
} else if (!this._hasSamePosition(e, eDown)) {
|
||||
// Teleport without a move event (e.g. tab out, move, tab back)
|
||||
this.#setDragStarted()
|
||||
this._setDragStarted()
|
||||
this.onDragEnd?.(e)
|
||||
} else if (this.onDoubleClick && this.#isDoubleClick()) {
|
||||
} else if (this.onDoubleClick && this._isDoubleClick()) {
|
||||
// Double-click event
|
||||
this.onDoubleClick(e)
|
||||
this.eLastDown = undefined
|
||||
@@ -258,10 +258,10 @@ export class CanvasPointer {
|
||||
* @param tolerance2 The maximum distance (squared) before the positions are considered different
|
||||
* @returns `true` if the two events were no more than {@link maxClickDrift} apart, otherwise `false`
|
||||
*/
|
||||
#hasSamePosition(
|
||||
private _hasSamePosition(
|
||||
a: PointerEvent,
|
||||
b: PointerEvent,
|
||||
tolerance2 = CanvasPointer.#maxClickDrift2
|
||||
tolerance2 = CanvasPointer._maxClickDrift2
|
||||
): boolean {
|
||||
const drift = dist2(a.clientX, a.clientY, b.clientX, b.clientY)
|
||||
return drift <= tolerance2
|
||||
@@ -271,21 +271,21 @@ export class CanvasPointer {
|
||||
* Checks whether the pointer is currently past the max click drift threshold.
|
||||
* @returns `true` if the latest pointer event is past the the click drift threshold
|
||||
*/
|
||||
#isDoubleClick(): boolean {
|
||||
private _isDoubleClick(): boolean {
|
||||
const { eDown, eLastDown } = this
|
||||
if (!eDown || !eLastDown) return false
|
||||
|
||||
// Use thrice the drift distance for double-click gap
|
||||
const tolerance2 = (3 * CanvasPointer.#maxClickDrift) ** 2
|
||||
const tolerance2 = (3 * CanvasPointer._maxClickDrift) ** 2
|
||||
const diff = eDown.timeStamp - eLastDown.timeStamp
|
||||
return (
|
||||
diff > 0 &&
|
||||
diff < CanvasPointer.doubleClickTime &&
|
||||
this.#hasSamePosition(eDown, eLastDown, tolerance2)
|
||||
this._hasSamePosition(eDown, eLastDown, tolerance2)
|
||||
)
|
||||
}
|
||||
|
||||
#setDragStarted(eMove?: CanvasPointerEvent): void {
|
||||
private _setDragStarted(eMove?: CanvasPointerEvent): void {
|
||||
this.dragStarted = true
|
||||
this.onDragStart?.(this, eMove)
|
||||
delete this.onDragStart
|
||||
@@ -303,14 +303,14 @@ export class CanvasPointer {
|
||||
const timeSinceLastEvent = Math.max(0, now - this.lastWheelEventTime)
|
||||
this.lastWheelEventTime = now
|
||||
|
||||
if (this.#isHighResWheelEvent(e, now)) {
|
||||
if (this._isHighResWheelEvent(e, now)) {
|
||||
this.detectedDevice = 'mouse'
|
||||
} else if (this.#isWithinCooldown(timeSinceLastEvent)) {
|
||||
if (this.#shouldBufferLinuxEvent(e)) {
|
||||
this.#bufferLinuxEvent(e, now)
|
||||
} else if (this._isWithinCooldown(timeSinceLastEvent)) {
|
||||
if (this._shouldBufferLinuxEvent(e)) {
|
||||
this._bufferLinuxEvent(e, now)
|
||||
}
|
||||
} else {
|
||||
this.#updateDeviceMode(e, now)
|
||||
this._updateDeviceMode(e, now)
|
||||
this.hasReceivedWheelEvent = true
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ export class CanvasPointer {
|
||||
* Validates buffered high res wheel events and switches to mouse mode if pattern matches.
|
||||
* @returns `true` if switched to mouse mode
|
||||
*/
|
||||
#isHighResWheelEvent(event: WheelEvent, now: number): boolean {
|
||||
private _isHighResWheelEvent(event: WheelEvent, now: number): boolean {
|
||||
if (!this.bufferedLinuxEvent || this.bufferedLinuxEventTime <= 0) {
|
||||
return false
|
||||
}
|
||||
@@ -329,15 +329,15 @@ export class CanvasPointer {
|
||||
const timeSinceBuffer = now - this.bufferedLinuxEventTime
|
||||
|
||||
if (timeSinceBuffer > CanvasPointer.maxHighResBufferTime) {
|
||||
this.#clearLinuxBuffer()
|
||||
this._clearLinuxBuffer()
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
event.deltaX === 0 &&
|
||||
this.#isLinuxWheelPattern(this.bufferedLinuxEvent.deltaY, event.deltaY)
|
||||
this._isLinuxWheelPattern(this.bufferedLinuxEvent.deltaY, event.deltaY)
|
||||
) {
|
||||
this.#clearLinuxBuffer()
|
||||
this._clearLinuxBuffer()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ export class CanvasPointer {
|
||||
/**
|
||||
* Checks if we're within the cooldown period where mode switching is disabled.
|
||||
*/
|
||||
#isWithinCooldown(timeSinceLastEvent: number): boolean {
|
||||
private _isWithinCooldown(timeSinceLastEvent: number): boolean {
|
||||
const isFirstEvent = !this.hasReceivedWheelEvent
|
||||
const cooldownExpired = timeSinceLastEvent >= CanvasPointer.trackpadMaxGap
|
||||
return !isFirstEvent && !cooldownExpired
|
||||
@@ -356,23 +356,23 @@ export class CanvasPointer {
|
||||
/**
|
||||
* Updates the device mode based on event patterns.
|
||||
*/
|
||||
#updateDeviceMode(event: WheelEvent, now: number): void {
|
||||
if (this.#isTrackpadPattern(event)) {
|
||||
private _updateDeviceMode(event: WheelEvent, now: number): void {
|
||||
if (this._isTrackpadPattern(event)) {
|
||||
this.detectedDevice = 'trackpad'
|
||||
} else if (this.#isMousePattern(event)) {
|
||||
} else if (this._isMousePattern(event)) {
|
||||
this.detectedDevice = 'mouse'
|
||||
} else if (
|
||||
this.detectedDevice === 'trackpad' &&
|
||||
this.#shouldBufferLinuxEvent(event)
|
||||
this._shouldBufferLinuxEvent(event)
|
||||
) {
|
||||
this.#bufferLinuxEvent(event, now)
|
||||
this._bufferLinuxEvent(event, now)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the buffered Linux wheel event and associated timer.
|
||||
*/
|
||||
#clearLinuxBuffer(): void {
|
||||
private _clearLinuxBuffer(): void {
|
||||
this.bufferedLinuxEvent = undefined
|
||||
this.bufferedLinuxEventTime = 0
|
||||
if (this.linuxBufferTimeoutId !== undefined) {
|
||||
@@ -385,7 +385,7 @@ export class CanvasPointer {
|
||||
* Checks if the event matches trackpad input patterns.
|
||||
* @param event The wheel event to check
|
||||
*/
|
||||
#isTrackpadPattern(event: WheelEvent): boolean {
|
||||
private _isTrackpadPattern(event: WheelEvent): boolean {
|
||||
// Two-finger panning: non-zero deltaX AND deltaY
|
||||
if (event.deltaX !== 0 && event.deltaY !== 0) return true
|
||||
|
||||
@@ -399,7 +399,7 @@ export class CanvasPointer {
|
||||
* Checks if the event matches mouse wheel input patterns.
|
||||
* @param event The wheel event to check
|
||||
*/
|
||||
#isMousePattern(event: WheelEvent): boolean {
|
||||
private _isMousePattern(event: WheelEvent): boolean {
|
||||
const absoluteDeltaY = Math.abs(event.deltaY)
|
||||
|
||||
// Primary threshold for switching from trackpad to mouse
|
||||
@@ -417,7 +417,7 @@ export class CanvasPointer {
|
||||
* Checks if the event should be buffered as a potential Linux wheel event.
|
||||
* @param event The wheel event to check
|
||||
*/
|
||||
#shouldBufferLinuxEvent(event: WheelEvent): boolean {
|
||||
private _shouldBufferLinuxEvent(event: WheelEvent): boolean {
|
||||
const absoluteDeltaY = Math.abs(event.deltaY)
|
||||
const isInLinuxRange = absoluteDeltaY >= 10 && absoluteDeltaY < 60
|
||||
const isVerticalOnly = event.deltaX === 0
|
||||
@@ -436,7 +436,7 @@ export class CanvasPointer {
|
||||
* @param event The event to buffer
|
||||
* @param now The current timestamp
|
||||
*/
|
||||
#bufferLinuxEvent(event: WheelEvent, now: number): void {
|
||||
private _bufferLinuxEvent(event: WheelEvent, now: number): void {
|
||||
if (this.linuxBufferTimeoutId !== undefined) {
|
||||
clearTimeout(this.linuxBufferTimeoutId)
|
||||
}
|
||||
@@ -446,7 +446,7 @@ export class CanvasPointer {
|
||||
|
||||
// Set timeout to clear buffer after 10ms
|
||||
this.linuxBufferTimeoutId = setTimeout(() => {
|
||||
this.#clearLinuxBuffer()
|
||||
this._clearLinuxBuffer()
|
||||
}, CanvasPointer.maxHighResBufferTime)
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ export class CanvasPointer {
|
||||
* @param deltaY1 The first deltaY value
|
||||
* @param deltaY2 The second deltaY value
|
||||
*/
|
||||
#isLinuxWheelPattern(deltaY1: number, deltaY2: number): boolean {
|
||||
private _isLinuxWheelPattern(deltaY1: number, deltaY2: number): boolean {
|
||||
const absolute1 = Math.abs(deltaY1)
|
||||
const absolute2 = Math.abs(deltaY2)
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ export class DragAndScale {
|
||||
* Returns `true` if the current state has changed from the previous state.
|
||||
* @returns `true` if the current state has changed from the previous state, otherwise `false`.
|
||||
*/
|
||||
#stateHasChanged(): boolean {
|
||||
private _stateHasChanged(): boolean {
|
||||
const current = this.state
|
||||
const previous = this.lastState
|
||||
|
||||
@@ -95,7 +95,7 @@ export class DragAndScale {
|
||||
computeVisibleArea(viewport: Rect | undefined): void {
|
||||
const { scale, offset, visible_area } = this
|
||||
|
||||
if (this.#stateHasChanged()) {
|
||||
if (this._stateHasChanged()) {
|
||||
this.onChanged?.(scale, offset)
|
||||
copyState(this.state, this.lastState)
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ export class LGraph
|
||||
}
|
||||
|
||||
/** Internal only. Not required for serialisation; calculated on deserialise. */
|
||||
#lastFloatingLinkId: number = 0
|
||||
private _lastFloatingLinkId: number = 0
|
||||
|
||||
private readonly floatingLinksInternal: Map<LinkId, LLink> = new Map()
|
||||
get floatingLinks(): ReadonlyMap<LinkId, LLink> {
|
||||
@@ -365,7 +365,7 @@ export class LGraph
|
||||
this.reroutes.clear()
|
||||
this.floatingLinksInternal.clear()
|
||||
|
||||
this.#lastFloatingLinkId = 0
|
||||
this._lastFloatingLinkId = 0
|
||||
|
||||
// other scene stuff
|
||||
this._groups = []
|
||||
@@ -1304,7 +1304,7 @@ export class LGraph
|
||||
|
||||
addFloatingLink(link: LLink): LLink {
|
||||
if (link.id === -1) {
|
||||
link.id = ++this.#lastFloatingLinkId
|
||||
link.id = ++this._lastFloatingLinkId
|
||||
}
|
||||
this.floatingLinksInternal.set(link.id, link)
|
||||
|
||||
@@ -2175,8 +2175,16 @@ export class LGraph
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom JSON serialization to prevent circular reference errors.
|
||||
* Called automatically by JSON.stringify().
|
||||
*/
|
||||
toJSON(): ISerialisedGraph {
|
||||
return this.serialize()
|
||||
}
|
||||
|
||||
/** @returns The drag and scale state of the first attached canvas, otherwise `undefined`. */
|
||||
#getDragAndScale(): DragAndScaleState | undefined {
|
||||
private _getDragAndScale(): DragAndScaleState | undefined {
|
||||
const ds = this.list_of_graphcanvas?.at(0)?.ds
|
||||
if (ds) return { scale: ds.scale, offset: ds.offset }
|
||||
}
|
||||
@@ -2216,7 +2224,7 @@ export class LGraph
|
||||
|
||||
// Save scale and offset
|
||||
const extra = { ...this.extra }
|
||||
if (LiteGraph.saveViewportWithGraph) extra.ds = this.#getDragAndScale()
|
||||
if (LiteGraph.saveViewportWithGraph) extra.ds = this._getDragAndScale()
|
||||
if (!extra.ds) delete extra.ds
|
||||
|
||||
const data: ReturnType<typeof this.asSerialisable> = {
|
||||
@@ -2406,8 +2414,8 @@ export class LGraph
|
||||
const floatingLink = LLink.create(linkData)
|
||||
this.addFloatingLink(floatingLink)
|
||||
|
||||
if (floatingLink.id > this.#lastFloatingLinkId)
|
||||
this.#lastFloatingLinkId = floatingLink.id
|
||||
if (floatingLink.id > this._lastFloatingLinkId)
|
||||
this._lastFloatingLinkId = floatingLink.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -316,17 +316,17 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
selectionChanged: false
|
||||
}
|
||||
|
||||
#subgraph?: Subgraph
|
||||
private _subgraph?: Subgraph
|
||||
get subgraph(): Subgraph | undefined {
|
||||
return this.#subgraph
|
||||
return this._subgraph
|
||||
}
|
||||
|
||||
set subgraph(value: Subgraph | undefined) {
|
||||
if (value !== this.#subgraph) {
|
||||
this.#subgraph = value
|
||||
if (value !== this._subgraph) {
|
||||
this._subgraph = value
|
||||
if (value)
|
||||
this.dispatch('litegraph:set-graph', {
|
||||
oldGraph: this.#subgraph,
|
||||
oldGraph: this._subgraph,
|
||||
newGraph: value
|
||||
})
|
||||
}
|
||||
@@ -361,7 +361,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
this.canvas.dispatchEvent(new CustomEvent(type, { detail }))
|
||||
}
|
||||
|
||||
#updateCursorStyle() {
|
||||
private _updateCursorStyle() {
|
||||
if (!this.state.shouldSetCursor) return
|
||||
|
||||
const crosshairItems =
|
||||
@@ -398,7 +398,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
set read_only(value: boolean) {
|
||||
this.state.readOnly = value
|
||||
this.#updateCursorStyle()
|
||||
this._updateCursorStyle()
|
||||
}
|
||||
|
||||
get isDragging(): boolean {
|
||||
@@ -415,7 +415,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
set hoveringOver(value: CanvasItem) {
|
||||
this.state.hoveringOver = value
|
||||
this.#updateCursorStyle()
|
||||
this._updateCursorStyle()
|
||||
}
|
||||
|
||||
/** @deprecated Replace all references with {@link pointer}.{@link CanvasPointer.isDown isDown}. */
|
||||
@@ -435,7 +435,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
set dragging_canvas(value: boolean) {
|
||||
this.state.draggingCanvas = value
|
||||
this.#updateCursorStyle()
|
||||
this._updateCursorStyle()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,16 +450,16 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
return `normal ${LiteGraph.NODE_SUBTEXT_SIZE}px ${LiteGraph.NODE_FONT}`
|
||||
}
|
||||
|
||||
#maximumFrameGap = 0
|
||||
private _maximumFrameGap = 0
|
||||
/** Maximum frames per second to render. 0: unlimited. Default: 0 */
|
||||
public get maximumFps() {
|
||||
return this.#maximumFrameGap > Number.EPSILON
|
||||
? this.#maximumFrameGap / 1000
|
||||
return this._maximumFrameGap > Number.EPSILON
|
||||
? this._maximumFrameGap / 1000
|
||||
: 0
|
||||
}
|
||||
|
||||
public set maximumFps(value) {
|
||||
this.#maximumFrameGap = value > Number.EPSILON ? 1000 / value : 0
|
||||
this._maximumFrameGap = value > Number.EPSILON ? 1000 / value : 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -660,12 +660,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* The IDs of the nodes that are currently visible on the canvas. More
|
||||
* performant than {@link visible_nodes} for visibility checks.
|
||||
*/
|
||||
#visible_node_ids: Set<NodeId> = new Set()
|
||||
private _visible_node_ids: Set<NodeId> = new Set()
|
||||
node_over?: LGraphNode
|
||||
node_capturing_input?: LGraphNode | null
|
||||
highlighted_links: Dictionary<boolean> = {}
|
||||
|
||||
#visibleReroutes: Set<Reroute> = new Set()
|
||||
private _visibleReroutes: Set<Reroute> = new Set()
|
||||
|
||||
dirty_canvas: boolean = true
|
||||
dirty_bgcanvas: boolean = true
|
||||
@@ -725,9 +725,9 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
NODEPANEL_IS_OPEN?: boolean
|
||||
|
||||
/** Once per frame check of snap to grid value. @todo Update on change. */
|
||||
#snapToGrid?: number
|
||||
private _snapToGrid?: number
|
||||
/** Set on keydown, keyup. @todo */
|
||||
#shiftDown: boolean = false
|
||||
private _shiftDown: boolean = false
|
||||
|
||||
/** Link rendering adapter for litegraph-to-canvas integration */
|
||||
linkRenderer: LitegraphLinkAdapter | null = null
|
||||
@@ -735,7 +735,11 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
/** If true, enable drag zoom. Ctrl+Shift+Drag Up/Down: zoom canvas. */
|
||||
dragZoomEnabled: boolean = false
|
||||
/** The start position of the drag zoom and original read-only state. */
|
||||
#dragZoomStart: { pos: Point; scale: number; readOnly: boolean } | null = null
|
||||
private _dragZoomStart: {
|
||||
pos: Point
|
||||
scale: number
|
||||
readOnly: boolean
|
||||
} | null = null
|
||||
|
||||
/** If true, enable live selection during drag. Nodes are selected/deselected in real-time. */
|
||||
liveSelection: boolean = false
|
||||
@@ -810,7 +814,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
this.linkConnector.events.addEventListener('link-created', () =>
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
)
|
||||
|
||||
// @deprecated Workaround: Keep until connecting_links is removed.
|
||||
@@ -1808,7 +1812,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
this.dragging_canvas = false
|
||||
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
this.dirty_area = null
|
||||
|
||||
this.node_in_panel = null
|
||||
@@ -1836,7 +1840,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
this.linkRenderer = new LitegraphLinkAdapter(false)
|
||||
|
||||
this.dispatch('litegraph:set-graph', { newGraph, oldGraph: graph })
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
}
|
||||
|
||||
openSubgraph(subgraph: Subgraph, fromNode: SubgraphNode): void {
|
||||
@@ -1873,7 +1877,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @returns The canvas element
|
||||
* @throws If {@link canvas} is an element ID that does not belong to a valid HTML canvas element
|
||||
*/
|
||||
#validateCanvas(
|
||||
private _validateCanvas(
|
||||
canvas: string | HTMLCanvasElement
|
||||
): HTMLCanvasElement & { data?: LGraphCanvas } {
|
||||
if (typeof canvas === 'string') {
|
||||
@@ -1892,7 +1896,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param skip_events If true, events on the previous canvas will not be removed. Has no effect on the first invocation.
|
||||
*/
|
||||
setCanvas(canvas: string | HTMLCanvasElement, skip_events?: boolean) {
|
||||
const element = this.#validateCanvas(canvas)
|
||||
const element = this._validateCanvas(canvas)
|
||||
if (element === this.canvas) return
|
||||
// maybe detach events from old_canvas
|
||||
if (!element && this.canvas && !skip_events) this.unbindEvents()
|
||||
@@ -1905,7 +1909,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
// TODO: classList.add
|
||||
element.className += ' lgraphcanvas'
|
||||
element.data = this
|
||||
Object.defineProperty(element, 'data', {
|
||||
value: this,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
})
|
||||
|
||||
// Background canvas: To render objects behind nodes (background, links, groups)
|
||||
this.bgcanvas = document.createElement('canvas')
|
||||
@@ -2026,12 +2035,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
/** Marks the entire canvas as dirty. */
|
||||
#dirty(): void {
|
||||
private _dirty(): void {
|
||||
this.dirty_canvas = true
|
||||
this.dirty_bgcanvas = true
|
||||
}
|
||||
|
||||
#linkConnectorDrop(): void {
|
||||
private _linkConnectorDrop(): void {
|
||||
const { graph, linkConnector, pointer } = this
|
||||
if (!graph) throw new NullGraphError()
|
||||
|
||||
@@ -2070,10 +2079,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
const window = this.getCanvasWindow()
|
||||
if (this.is_rendering) {
|
||||
if (this.#maximumFrameGap > 0) {
|
||||
if (this._maximumFrameGap > 0) {
|
||||
// Manual FPS limit
|
||||
const gap =
|
||||
this.#maximumFrameGap - (LiteGraph.getTime() - this.last_draw_time)
|
||||
this._maximumFrameGap - (LiteGraph.getTime() - this.last_draw_time)
|
||||
setTimeout(renderFrame.bind(this), Math.max(1, gap))
|
||||
} else {
|
||||
// FPS limited by refresh rate
|
||||
@@ -2161,7 +2170,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
!e.altKey &&
|
||||
e.buttons
|
||||
) {
|
||||
this.#dragZoomStart = {
|
||||
this._dragZoomStart = {
|
||||
pos: [e.x, e.y],
|
||||
scale: this.ds.scale,
|
||||
readOnly: this.read_only
|
||||
@@ -2208,9 +2217,9 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
// left button mouse / single finger
|
||||
if (e.button === 0 && !pointer.isDouble) {
|
||||
this.#processPrimaryButton(e, node)
|
||||
this._processPrimaryButton(e, node)
|
||||
} else if (e.button === 1) {
|
||||
this.#processMiddleButton(e, node)
|
||||
this._processMiddleButton(e, node)
|
||||
} else if (
|
||||
(e.button === 2 || pointer.isDouble) &&
|
||||
this.allow_interaction &&
|
||||
@@ -2246,7 +2255,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
reroute = graph.getRerouteOnPos(
|
||||
e.canvasX,
|
||||
e.canvasY,
|
||||
this.#visibleReroutes
|
||||
this._visibleReroutes
|
||||
)
|
||||
}
|
||||
if (reroute) {
|
||||
@@ -2302,18 +2311,24 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param y The y coordinate in canvas space
|
||||
* @returns The positionable item or undefined
|
||||
*/
|
||||
#getPositionableOnPos(x: number, y: number): Positionable | undefined {
|
||||
private _getPositionableOnPos(
|
||||
x: number,
|
||||
y: number
|
||||
): Positionable | undefined {
|
||||
const ioNode = this.subgraph?.getIoNodeOnPos(x, y)
|
||||
if (ioNode) return ioNode
|
||||
|
||||
for (const reroute of this.#visibleReroutes) {
|
||||
for (const reroute of this._visibleReroutes) {
|
||||
if (reroute.containsPoint([x, y])) return reroute
|
||||
}
|
||||
|
||||
return this.graph?.getGroupTitlebarOnPos(x, y)
|
||||
}
|
||||
|
||||
#processPrimaryButton(e: CanvasPointerEvent, node: LGraphNode | undefined) {
|
||||
private _processPrimaryButton(
|
||||
e: CanvasPointerEvent,
|
||||
node: LGraphNode | undefined
|
||||
) {
|
||||
const { pointer, graph, linkConnector, subgraph } = this
|
||||
if (!graph) throw new NullGraphError()
|
||||
|
||||
@@ -2329,7 +2344,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
!e.altKey &&
|
||||
LiteGraph.leftMouseClickBehavior === 'panning'
|
||||
) {
|
||||
this.#setupNodeSelectionDrag(e, pointer, node)
|
||||
this._setupNodeSelectionDrag(e, pointer, node)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -2360,16 +2375,16 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
if (this.allow_dragnodes) {
|
||||
pointer.onDragStart = (pointer) => {
|
||||
this.#startDraggingItems(cloned, pointer)
|
||||
this._startDraggingItems(cloned, pointer)
|
||||
}
|
||||
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
|
||||
pointer.onDragEnd = (e) => this._processDraggedItems(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Node clicked
|
||||
if (node && (this.allow_interaction || node.flags.allow_interaction)) {
|
||||
this.#processNodeClick(e, ctrlOrMeta, node)
|
||||
this._processNodeClick(e, ctrlOrMeta, node)
|
||||
} else {
|
||||
// Subgraph IO nodes
|
||||
if (subgraph) {
|
||||
@@ -2387,8 +2402,8 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
ioNode.onPointerDown(e, pointer, linkConnector)
|
||||
pointer.onClick ??= () => canvas.processSelect(ioNode, e)
|
||||
pointer.onDragStart ??= () =>
|
||||
canvas.#startDraggingItems(ioNode, pointer, true)
|
||||
pointer.onDragEnd ??= (eUp) => canvas.#processDraggedItems(eUp)
|
||||
canvas._startDraggingItems(ioNode, pointer, true)
|
||||
pointer.onDragEnd ??= (eUp) => canvas._processDraggedItems(eUp)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -2404,7 +2419,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
// Fallback to checking visible reroutes directly
|
||||
for (const reroute of this.#visibleReroutes) {
|
||||
for (const reroute of this._visibleReroutes) {
|
||||
const overReroute =
|
||||
foundReroute === reroute || reroute.containsPoint([x, y])
|
||||
if (!reroute.isSlotHovered && !overReroute) continue
|
||||
@@ -2413,19 +2428,19 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
pointer.onClick = () => this.processSelect(reroute, e)
|
||||
if (!e.shiftKey) {
|
||||
pointer.onDragStart = (pointer) =>
|
||||
this.#startDraggingItems(reroute, pointer, true)
|
||||
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
|
||||
this._startDraggingItems(reroute, pointer, true)
|
||||
pointer.onDragEnd = (e) => this._processDraggedItems(e)
|
||||
}
|
||||
}
|
||||
|
||||
if (reroute.isOutputHovered || (overReroute && e.shiftKey)) {
|
||||
linkConnector.dragFromReroute(graph, reroute)
|
||||
this.#linkConnectorDrop()
|
||||
this._linkConnectorDrop()
|
||||
}
|
||||
|
||||
if (reroute.isInputHovered) {
|
||||
linkConnector.dragFromRerouteToOutput(graph, reroute)
|
||||
this.#linkConnectorDrop()
|
||||
this._linkConnectorDrop()
|
||||
}
|
||||
|
||||
reroute.hideSlots()
|
||||
@@ -2470,14 +2485,14 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
if (e.shiftKey && !e.altKey) {
|
||||
linkConnector.dragFromLinkSegment(graph, linkSegment)
|
||||
this.#linkConnectorDrop()
|
||||
this._linkConnectorDrop()
|
||||
|
||||
return
|
||||
} else if (e.altKey && !e.shiftKey) {
|
||||
const newReroute = graph.createReroute([x, y], linkSegment)
|
||||
pointer.onDragStart = (pointer) =>
|
||||
this.#startDraggingItems(newReroute, pointer)
|
||||
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
|
||||
this._startDraggingItems(newReroute, pointer)
|
||||
pointer.onDragEnd = (e) => this._processDraggedItems(e)
|
||||
return
|
||||
}
|
||||
} else if (
|
||||
@@ -2519,7 +2534,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
eMove.canvasY - group.pos[1] - offsetY
|
||||
]
|
||||
// Unless snapping.
|
||||
if (this.#snapToGrid) snapPoint(pos, this.#snapToGrid)
|
||||
if (this._snapToGrid) snapPoint(pos, this._snapToGrid)
|
||||
|
||||
const resized = group.resize(pos[0], pos[1])
|
||||
if (resized) this.dirty_bgcanvas = true
|
||||
@@ -2542,9 +2557,9 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
pointer.onClick = () => this.processSelect(group, e)
|
||||
pointer.onDragStart = (pointer) => {
|
||||
group.recomputeInsideNodes()
|
||||
this.#startDraggingItems(group, pointer, true)
|
||||
this._startDraggingItems(group, pointer, true)
|
||||
}
|
||||
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
|
||||
pointer.onDragEnd = (e) => this._processDraggedItems(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2582,12 +2597,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
pointer.finally = () => (this.dragging_canvas = false)
|
||||
this.dragging_canvas = true
|
||||
} else {
|
||||
this.#setupNodeSelectionDrag(e, pointer)
|
||||
this._setupNodeSelectionDrag(e, pointer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#setupNodeSelectionDrag(
|
||||
private _setupNodeSelectionDrag(
|
||||
e: CanvasPointerEvent,
|
||||
pointer: CanvasPointer,
|
||||
node?: LGraphNode | undefined
|
||||
@@ -2602,7 +2617,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
pointer.onClick = (eUp) => {
|
||||
// Click, not drag
|
||||
const clickedItem =
|
||||
node ?? this.#getPositionableOnPos(eUp.canvasX, eUp.canvasY)
|
||||
node ?? this._getPositionableOnPos(eUp.canvasX, eUp.canvasY)
|
||||
this.processSelect(clickedItem, eUp)
|
||||
}
|
||||
pointer.onDragStart = () => (this.dragging_rectangle = dragRect)
|
||||
@@ -2617,7 +2632,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
} else {
|
||||
// Classic mode: select only when drag ends
|
||||
pointer.onDragEnd = (upEvent) =>
|
||||
this.#handleMultiSelect(upEvent, dragRect)
|
||||
this._handleMultiSelect(upEvent, dragRect)
|
||||
}
|
||||
|
||||
pointer.finally = () => (this.dragging_rectangle = null)
|
||||
@@ -2629,7 +2644,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param ctrlOrMeta Ctrl or meta key is pressed
|
||||
* @param node The node to process a click event for
|
||||
*/
|
||||
#processNodeClick(
|
||||
private _processNodeClick(
|
||||
e: CanvasPointerEvent,
|
||||
ctrlOrMeta: boolean,
|
||||
node: LGraphNode
|
||||
@@ -2685,13 +2700,13 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
// Drag multiple output links
|
||||
if (e.shiftKey && hasRelevantOutputLinks(output, graph)) {
|
||||
linkConnector.moveOutputLink(graph, output)
|
||||
this.#linkConnectorDrop()
|
||||
this._linkConnectorDrop()
|
||||
return
|
||||
}
|
||||
|
||||
// New output link
|
||||
linkConnector.dragNewFromOutput(graph, node, output)
|
||||
this.#linkConnectorDrop()
|
||||
this._linkConnectorDrop()
|
||||
|
||||
if (LiteGraph.shift_click_do_break_link_from) {
|
||||
if (e.shiftKey) {
|
||||
@@ -2744,7 +2759,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
linkConnector.dragNewFromInput(graph, node, input)
|
||||
}
|
||||
|
||||
this.#linkConnectorDrop()
|
||||
this._linkConnectorDrop()
|
||||
this.dirty_bgcanvas = true
|
||||
|
||||
return
|
||||
@@ -2874,7 +2889,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
// Apply snapping to position changes
|
||||
if (this.#snapToGrid) {
|
||||
if (this._snapToGrid) {
|
||||
if (
|
||||
resizeDirection.includes('N') ||
|
||||
resizeDirection.includes('W')
|
||||
@@ -2882,7 +2897,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const originalX = newBounds.x
|
||||
const originalY = newBounds.y
|
||||
|
||||
snapPoint(newBounds.pos, this.#snapToGrid)
|
||||
snapPoint(newBounds.pos, this._snapToGrid)
|
||||
|
||||
// Adjust size to compensate for snapped position
|
||||
if (resizeDirection.includes('N')) {
|
||||
@@ -2893,7 +2908,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
}
|
||||
|
||||
snapPoint(newBounds.size, this.#snapToGrid)
|
||||
snapPoint(newBounds.size, this._snapToGrid)
|
||||
}
|
||||
|
||||
// Apply snapping to size changes
|
||||
@@ -2918,11 +2933,11 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
node.pos = newBounds.pos
|
||||
node.setSize(newBounds.size)
|
||||
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
}
|
||||
|
||||
pointer.onDragEnd = () => {
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
graph.afterChange(node)
|
||||
}
|
||||
pointer.finally = () => {
|
||||
@@ -2938,8 +2953,8 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
|
||||
// Drag node
|
||||
pointer.onDragStart = (pointer) =>
|
||||
this.#startDraggingItems(node, pointer, true)
|
||||
pointer.onDragEnd = (e) => this.#processDraggedItems(e)
|
||||
this._startDraggingItems(node, pointer, true)
|
||||
pointer.onDragEnd = (e) => this._processDraggedItems(e)
|
||||
}
|
||||
|
||||
this.dirty_canvas = true
|
||||
@@ -3008,7 +3023,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param e The pointerdown event
|
||||
* @param node The node to process a click event for
|
||||
*/
|
||||
#processMiddleButton(e: CanvasPointerEvent, node: LGraphNode | undefined) {
|
||||
private _processMiddleButton(
|
||||
e: CanvasPointerEvent,
|
||||
node: LGraphNode | undefined
|
||||
) {
|
||||
const { pointer } = this
|
||||
|
||||
if (
|
||||
@@ -3105,14 +3123,14 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
}
|
||||
|
||||
#processDragZoom(e: PointerEvent): void {
|
||||
private _processDragZoom(e: PointerEvent): void {
|
||||
// stop canvas zoom action
|
||||
if (!e.buttons) {
|
||||
this.#finishDragZoom()
|
||||
this._finishDragZoom()
|
||||
return
|
||||
}
|
||||
|
||||
const start = this.#dragZoomStart
|
||||
const start = this._dragZoomStart
|
||||
if (!start) throw new TypeError('Drag-zoom state object was null')
|
||||
if (!this.graph) throw new NullGraphError()
|
||||
|
||||
@@ -3126,10 +3144,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
this.graph.change()
|
||||
}
|
||||
|
||||
#finishDragZoom(): void {
|
||||
const start = this.#dragZoomStart
|
||||
private _finishDragZoom(): void {
|
||||
const start = this._dragZoomStart
|
||||
if (!start) return
|
||||
this.#dragZoomStart = null
|
||||
this._dragZoomStart = null
|
||||
this.read_only = start.readOnly
|
||||
}
|
||||
|
||||
@@ -3141,9 +3159,9 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
this.dragZoomEnabled &&
|
||||
e.ctrlKey &&
|
||||
e.shiftKey &&
|
||||
this.#dragZoomStart
|
||||
this._dragZoomStart
|
||||
) {
|
||||
this.#processDragZoom(e)
|
||||
this._processDragZoom(e)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3210,7 +3228,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
} else if (this.dragging_canvas) {
|
||||
this.ds.offset[0] += delta[0] / this.ds.scale
|
||||
this.ds.offset[1] += delta[1] / this.ds.scale
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
} else if (
|
||||
(this.allow_interaction || node?.flags.allow_interaction) &&
|
||||
!this.read_only
|
||||
@@ -3258,7 +3276,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
this.node_over = node
|
||||
this.dirty_canvas = true
|
||||
|
||||
for (const reroute of this.#visibleReroutes) {
|
||||
for (const reroute of this._visibleReroutes) {
|
||||
reroute.hideSlots()
|
||||
this.dirty_bgcanvas = true
|
||||
}
|
||||
@@ -3382,10 +3400,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
} else {
|
||||
// Reroutes
|
||||
underPointer = this.#updateReroutes(underPointer)
|
||||
underPointer = this._updateReroutes(underPointer)
|
||||
|
||||
// Not over a node
|
||||
const segment = this.#getLinkCentreOnPos(e)
|
||||
const segment = this._getLinkCentreOnPos(e)
|
||||
if (this.over_link_center !== segment) {
|
||||
underPointer |= CanvasItem.Link
|
||||
this.over_link_center = segment
|
||||
@@ -3435,7 +3453,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
}
|
||||
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3449,14 +3467,14 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* Updates the hover / snap state of all visible reroutes.
|
||||
* @returns The original value of {@link underPointer}, with any found reroute items added.
|
||||
*/
|
||||
#updateReroutes(underPointer: CanvasItem): CanvasItem {
|
||||
private _updateReroutes(underPointer: CanvasItem): CanvasItem {
|
||||
const { graph, pointer, linkConnector } = this
|
||||
if (!graph) throw new NullGraphError()
|
||||
|
||||
// Update reroute hover state
|
||||
if (!pointer.isDown) {
|
||||
let anyChanges = false
|
||||
for (const reroute of this.#visibleReroutes) {
|
||||
for (const reroute of this._visibleReroutes) {
|
||||
anyChanges ||= reroute.updateVisibility(this.graph_mouse)
|
||||
|
||||
if (reroute.isSlotHovered) underPointer |= CanvasItem.RerouteSlot
|
||||
@@ -3464,7 +3482,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
if (anyChanges) this.dirty_bgcanvas = true
|
||||
} else if (linkConnector.isConnecting) {
|
||||
// Highlight the reroute that the mouse is over
|
||||
for (const reroute of this.#visibleReroutes) {
|
||||
for (const reroute of this._visibleReroutes) {
|
||||
if (reroute.containsPoint(this.graph_mouse)) {
|
||||
if (linkConnector.isRerouteValidDrop(reroute)) {
|
||||
linkConnector.overReroute = reroute
|
||||
@@ -3489,7 +3507,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param pointer The pointer event that initiated the drag, e.g. pointerdown
|
||||
* @param sticky If `true`, the item is added to the selection - see {@link processSelect}
|
||||
*/
|
||||
#startDraggingItems(
|
||||
private _startDraggingItems(
|
||||
item: Positionable,
|
||||
pointer: CanvasPointer,
|
||||
sticky = false
|
||||
@@ -3511,7 +3529,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* Handles shared clean up and placement after items have been dragged.
|
||||
* @param e The event that completed the drag, e.g. pointerup, pointermove
|
||||
*/
|
||||
#processDraggedItems(e: CanvasPointerEvent): void {
|
||||
private _processDraggedItems(e: CanvasPointerEvent): void {
|
||||
const { graph } = this
|
||||
if (e.shiftKey || LiteGraph.alwaysSnapToGrid)
|
||||
graph?.snapToGrid(this.selectedItems)
|
||||
@@ -3533,7 +3551,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const { graph, pointer } = this
|
||||
if (!graph) return
|
||||
|
||||
this.#finishDragZoom()
|
||||
this._finishDragZoom()
|
||||
|
||||
LGraphCanvas.active_canvas = this
|
||||
|
||||
@@ -3677,7 +3695,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
return
|
||||
}
|
||||
|
||||
#noItemsSelected(): void {
|
||||
private _noItemsSelected(): void {
|
||||
const event = new CustomEvent('litegraph:no-items-selected', {
|
||||
bubbles: true
|
||||
})
|
||||
@@ -3688,7 +3706,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* process a key event
|
||||
*/
|
||||
processKey(e: KeyboardEvent): void {
|
||||
this.#shiftDown = e.shiftKey
|
||||
this._shiftDown = e.shiftKey
|
||||
|
||||
const { graph } = this
|
||||
if (!graph) return
|
||||
@@ -3735,7 +3753,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
// @ts-expect-error EventTarget.localName is not in standard types
|
||||
if (e.target.localName != 'input' && e.target.localName != 'textarea') {
|
||||
if (this.selectedItems.size === 0) {
|
||||
this.#noItemsSelected()
|
||||
this._noItemsSelected()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4097,7 +4115,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param dragRect The drag rectangle to normalize (modified in place)
|
||||
* @returns The normalized rectangle
|
||||
*/
|
||||
#normalizeDragRect(dragRect: Rect): Rect {
|
||||
private _normalizeDragRect(dragRect: Rect): Rect {
|
||||
const w = Math.abs(dragRect[2])
|
||||
const h = Math.abs(dragRect[3])
|
||||
if (dragRect[2] < 0) dragRect[0] -= w
|
||||
@@ -4112,7 +4130,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param rect The rectangle to check against
|
||||
* @returns Set of positionable items that overlap with the rectangle
|
||||
*/
|
||||
#getItemsInRect(rect: Rect): Set<Positionable> {
|
||||
private _getItemsInRect(rect: Rect): Set<Positionable> {
|
||||
const { graph, subgraph } = this
|
||||
if (!graph) throw new NullGraphError()
|
||||
|
||||
@@ -4166,9 +4184,9 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
dragRect[2],
|
||||
dragRect[3]
|
||||
]
|
||||
this.#normalizeDragRect(normalizedRect)
|
||||
this._normalizeDragRect(normalizedRect)
|
||||
|
||||
const itemsInRect = this.#getItemsInRect(normalizedRect)
|
||||
const itemsInRect = this._getItemsInRect(normalizedRect)
|
||||
|
||||
const desired = new Set<Positionable>()
|
||||
if (e.shiftKey && !e.altKey) {
|
||||
@@ -4215,16 +4233,16 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @param e The pointer up event
|
||||
* @param dragRect The drag rectangle
|
||||
*/
|
||||
#handleMultiSelect(e: CanvasPointerEvent, dragRect: Rect): void {
|
||||
private _handleMultiSelect(e: CanvasPointerEvent, dragRect: Rect): void {
|
||||
const normalizedRect: Rect = [
|
||||
dragRect[0],
|
||||
dragRect[1],
|
||||
dragRect[2],
|
||||
dragRect[3]
|
||||
]
|
||||
this.#normalizeDragRect(normalizedRect)
|
||||
this._normalizeDragRect(normalizedRect)
|
||||
|
||||
const itemsInRect = this.#getItemsInRect(normalizedRect)
|
||||
const itemsInRect = this._getItemsInRect(normalizedRect)
|
||||
const { selectedItems } = this
|
||||
|
||||
if (e.shiftKey) {
|
||||
@@ -4588,7 +4606,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
*/
|
||||
setZoom(value: number, zooming_center: Point) {
|
||||
this.ds.changeScale(value, zooming_center)
|
||||
this.#dirty()
|
||||
this._dirty()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4671,7 +4689,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* @returns `true` if the node is visible, otherwise `false`
|
||||
*/
|
||||
isNodeVisible(node: LGraphNode): boolean {
|
||||
return this.#visible_node_ids.has(node.id)
|
||||
return this._visible_node_ids.has(node.id)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4692,7 +4710,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
if (this.dirty_canvas || force_canvas) {
|
||||
this.computeVisibleNodes(undefined, this.visible_nodes)
|
||||
// Update visible node IDs
|
||||
this.#visible_node_ids = new Set(
|
||||
this._visible_node_ids = new Set(
|
||||
this.visible_nodes.map((node) => node.id)
|
||||
)
|
||||
|
||||
@@ -4746,8 +4764,8 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
// TODO: Set snapping value when changed instead of once per frame
|
||||
this.#snapToGrid =
|
||||
this.#shiftDown || LiteGraph.alwaysSnapToGrid
|
||||
this._snapToGrid =
|
||||
this._shiftDown || LiteGraph.alwaysSnapToGrid
|
||||
? this.graph?.getSnapToGridSize()
|
||||
: undefined
|
||||
|
||||
@@ -4789,7 +4807,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
// draw nodes
|
||||
const { visible_nodes } = this
|
||||
const drawSnapGuides =
|
||||
this.#snapToGrid &&
|
||||
this._snapToGrid &&
|
||||
(this.isDragging || layoutStore.isDraggingVueNodes.value)
|
||||
|
||||
for (const node of visible_nodes) {
|
||||
@@ -4829,7 +4847,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
if (linkConnector.isConnecting) {
|
||||
// current connection (the one being dragged by the mouse)
|
||||
const { renderLinks } = linkConnector
|
||||
const highlightPos = this.#getHighlightPosition()
|
||||
const highlightPos = this._getHighlightPosition()
|
||||
ctx.lineWidth = this.connections_width
|
||||
|
||||
for (const renderLink of renderLinks) {
|
||||
@@ -4883,7 +4901,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
// Gradient half-border over target node
|
||||
this.#renderSnapHighlight(ctx, highlightPos)
|
||||
this._renderSnapHighlight(ctx, highlightPos)
|
||||
}
|
||||
|
||||
// on top of link center
|
||||
@@ -4909,7 +4927,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
/** @returns If the pointer is over a link centre marker, the link segment it belongs to. Otherwise, `undefined`. */
|
||||
#getLinkCentreOnPos(e: CanvasPointerEvent): LinkSegment | undefined {
|
||||
private _getLinkCentreOnPos(e: CanvasPointerEvent): LinkSegment | undefined {
|
||||
// Skip hit detection if center markers are disabled
|
||||
if (this.linkMarkerShape === LinkMarkerShape.None) {
|
||||
return undefined
|
||||
@@ -4928,7 +4946,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
/** Get the target snap / highlight point in graph space */
|
||||
#getHighlightPosition(): Readonly<Point> {
|
||||
private _getHighlightPosition(): Readonly<Point> {
|
||||
return LiteGraph.snaps_for_comfy
|
||||
? (this.linkConnector.state.snapLinksPos ??
|
||||
this._highlight_pos ??
|
||||
@@ -4941,7 +4959,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
* Partial border over target node and a highlight over the slot itself.
|
||||
* @param ctx Canvas 2D context
|
||||
*/
|
||||
#renderSnapHighlight(
|
||||
private _renderSnapHighlight(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
highlightPos: Readonly<Point>
|
||||
): void {
|
||||
@@ -5592,7 +5610,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
// Normalise boundingRect to pos to snap
|
||||
snapGuide[0] += offsetX
|
||||
snapGuide[1] += offsetY
|
||||
if (this.#snapToGrid) snapPoint(snapGuide, this.#snapToGrid)
|
||||
if (this._snapToGrid) snapPoint(snapGuide, this._snapToGrid)
|
||||
snapGuide[0] -= offsetX
|
||||
snapGuide[1] -= offsetY
|
||||
|
||||
@@ -5672,7 +5690,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const output = start_node.outputs[outputId]
|
||||
if (!output) continue
|
||||
|
||||
this.#renderAllLinkSegments(
|
||||
this._renderAllLinkSegments(
|
||||
ctx,
|
||||
link,
|
||||
startPos,
|
||||
@@ -5701,7 +5719,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
? getSlotPosition(inputNode, link.target_slot, true)
|
||||
: inputNode.getInputPos(link.target_slot)
|
||||
|
||||
this.#renderAllLinkSegments(
|
||||
this._renderAllLinkSegments(
|
||||
ctx,
|
||||
link,
|
||||
output.pos,
|
||||
@@ -5728,7 +5746,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
? getSlotPosition(outputNode, link.origin_slot, false)
|
||||
: outputNode.getOutputPos(link.origin_slot)
|
||||
|
||||
this.#renderAllLinkSegments(
|
||||
this._renderAllLinkSegments(
|
||||
ctx,
|
||||
link,
|
||||
startPos,
|
||||
@@ -5742,10 +5760,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
}
|
||||
|
||||
if (graph.floatingLinks.size > 0) {
|
||||
this.#renderFloatingLinks(ctx, graph, visibleReroutes, now)
|
||||
this._renderFloatingLinks(ctx, graph, visibleReroutes, now)
|
||||
}
|
||||
|
||||
const rerouteSet = this.#visibleReroutes
|
||||
const rerouteSet = this._visibleReroutes
|
||||
rerouteSet.clear()
|
||||
|
||||
// Render reroutes, ordered by number of non-floating links
|
||||
@@ -5754,7 +5772,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
rerouteSet.add(reroute)
|
||||
|
||||
if (
|
||||
this.#snapToGrid &&
|
||||
this._snapToGrid &&
|
||||
this.isDragging &&
|
||||
this.selectedItems.has(reroute)
|
||||
) {
|
||||
@@ -5776,7 +5794,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
: this.editor_alpha
|
||||
}
|
||||
|
||||
#renderFloatingLinks(
|
||||
private _renderFloatingLinks(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
graph: LGraph,
|
||||
visibleReroutes: Reroute[],
|
||||
@@ -5805,7 +5823,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const endDirection = node.inputs[link.target_slot]?.dir
|
||||
|
||||
firstReroute._dragging = true
|
||||
this.#renderAllLinkSegments(
|
||||
this._renderAllLinkSegments(
|
||||
ctx,
|
||||
link,
|
||||
startPos,
|
||||
@@ -5827,7 +5845,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const startDirection = node.outputs[link.origin_slot]?.dir
|
||||
|
||||
link._dragging = true
|
||||
this.#renderAllLinkSegments(
|
||||
this._renderAllLinkSegments(
|
||||
ctx,
|
||||
link,
|
||||
startPos,
|
||||
@@ -5843,7 +5861,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
ctx.globalAlpha = globalAlpha
|
||||
}
|
||||
|
||||
#renderAllLinkSegments(
|
||||
private _renderAllLinkSegments(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
link: LLink,
|
||||
startPos: Point,
|
||||
@@ -6146,7 +6164,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
ctx.save()
|
||||
ctx.globalAlpha = 0.5 * this.editor_alpha
|
||||
const drawSnapGuides =
|
||||
this.#snapToGrid &&
|
||||
this._snapToGrid &&
|
||||
(this.isDragging || layoutStore.isDraggingVueNodes.value)
|
||||
|
||||
for (const group of groups) {
|
||||
@@ -6513,7 +6531,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
},
|
||||
optPass || {}
|
||||
)
|
||||
const dirty = () => this.#dirty()
|
||||
const dirty = () => this._dirty()
|
||||
|
||||
const that = this
|
||||
const { graph } = this
|
||||
@@ -7522,7 +7540,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
function inner() {
|
||||
setValue(input?.value)
|
||||
}
|
||||
const dirty = () => this.#dirty()
|
||||
const dirty = () => this._dirty()
|
||||
|
||||
function setValue(value: string | number | undefined) {
|
||||
if (
|
||||
@@ -8356,7 +8374,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
reroute = this.graph.getRerouteOnPos(
|
||||
event.canvasX,
|
||||
event.canvasY,
|
||||
this.#visibleReroutes
|
||||
this._visibleReroutes
|
||||
)
|
||||
}
|
||||
if (reroute) {
|
||||
@@ -8646,4 +8664,17 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const mutations = this.initLayoutMutations()
|
||||
this.applyNodePositionUpdates(nodesToReposition, mutations)
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom JSON serialization to prevent circular reference errors.
|
||||
* LGraphCanvas should not be serialized directly - serialize the graph instead.
|
||||
*/
|
||||
toJSON(): { ds: { scale: number; offset: [number, number] } } {
|
||||
return {
|
||||
ds: {
|
||||
scale: this.ds.scale,
|
||||
offset: [...this.ds.offset] as [number, number]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,8 +273,8 @@ export class LGraphNode
|
||||
inputs: INodeInputSlot[] = []
|
||||
outputs: INodeOutputSlot[] = []
|
||||
|
||||
#concreteInputs: NodeInputSlot[] = []
|
||||
#concreteOutputs: NodeOutputSlot[] = []
|
||||
private _concreteInputs: NodeInputSlot[] = []
|
||||
private _concreteOutputs: NodeOutputSlot[] = []
|
||||
|
||||
properties: Dictionary<NodeProperty | undefined> = {}
|
||||
properties_info: INodePropertyInfo[] = []
|
||||
@@ -438,24 +438,24 @@ export class LGraphNode
|
||||
}
|
||||
|
||||
/** @inheritdoc {@link renderArea} */
|
||||
#renderArea = new Rectangle()
|
||||
private _renderArea = new Rectangle()
|
||||
/**
|
||||
* Rect describing the node area, including shadows and any protrusions.
|
||||
* Determines if the node is visible. Calculated once at the start of every frame.
|
||||
*/
|
||||
get renderArea(): ReadOnlyRect {
|
||||
return this.#renderArea
|
||||
return this._renderArea
|
||||
}
|
||||
|
||||
/** @inheritdoc {@link boundingRect} */
|
||||
#boundingRect: Rectangle = new Rectangle()
|
||||
private _boundingRect: Rectangle = new Rectangle()
|
||||
/**
|
||||
* Cached node position & area as `x, y, width, height`. Includes changes made by {@link onBounding}, if present.
|
||||
*
|
||||
* Determines the node hitbox and other rendering effects. Calculated once at the start of every frame.
|
||||
*/
|
||||
get boundingRect(): ReadOnlyRectangle {
|
||||
return this.#boundingRect
|
||||
return this._boundingRect
|
||||
}
|
||||
|
||||
/** The offset from {@link pos} to the top-left of {@link boundingRect}. */
|
||||
@@ -753,7 +753,9 @@ export class LGraphNode
|
||||
onPropertyChange?(this: LGraphNode): void
|
||||
updateOutputData?(this: LGraphNode, origin_slot: number): void
|
||||
|
||||
#getErrorStrokeStyle(this: LGraphNode): IDrawBoundingOptions | undefined {
|
||||
private _getErrorStrokeStyle(
|
||||
this: LGraphNode
|
||||
): IDrawBoundingOptions | undefined {
|
||||
if (this.has_errors) {
|
||||
return {
|
||||
padding: 12,
|
||||
@@ -763,7 +765,9 @@ export class LGraphNode
|
||||
}
|
||||
}
|
||||
|
||||
#getSelectedStrokeStyle(this: LGraphNode): IDrawBoundingOptions | undefined {
|
||||
private _getSelectedStrokeStyle(
|
||||
this: LGraphNode
|
||||
): IDrawBoundingOptions | undefined {
|
||||
if (this.selected) {
|
||||
return {
|
||||
padding: this.has_errors ? 20 : undefined
|
||||
@@ -778,8 +782,8 @@ export class LGraphNode
|
||||
this.size = [LiteGraph.NODE_WIDTH, 60]
|
||||
this.pos = [10, 10]
|
||||
this.strokeStyles = {
|
||||
error: this.#getErrorStrokeStyle,
|
||||
selected: this.#getSelectedStrokeStyle
|
||||
error: this._getErrorStrokeStyle,
|
||||
selected: this._getSelectedStrokeStyle
|
||||
}
|
||||
// Initialize property manager with tracked properties
|
||||
this.changeTracker = new LGraphNodeProperties(this)
|
||||
@@ -2067,11 +2071,11 @@ export class LGraphNode
|
||||
* Called automatically at the start of every frame.
|
||||
*/
|
||||
updateArea(ctx?: CanvasRenderingContext2D): void {
|
||||
const bounds = this.#boundingRect
|
||||
const bounds = this._boundingRect
|
||||
this.measure(bounds, ctx)
|
||||
this.onBounding?.(bounds)
|
||||
|
||||
const renderArea = this.#renderArea
|
||||
const renderArea = this._renderArea
|
||||
renderArea.set(bounds)
|
||||
// 4 offset for collapsed node connection points
|
||||
renderArea[0] -= 4
|
||||
@@ -2293,7 +2297,7 @@ export class LGraphNode
|
||||
optsIn?: FindFreeSlotOptions & { returnObj?: TReturn }
|
||||
): INodeInputSlot | -1
|
||||
findInputSlotFree(optsIn?: FindFreeSlotOptions) {
|
||||
return this.#findFreeSlot(this.inputs, optsIn)
|
||||
return this._findFreeSlot(this.inputs, optsIn)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2308,14 +2312,14 @@ export class LGraphNode
|
||||
optsIn?: FindFreeSlotOptions & { returnObj?: TReturn }
|
||||
): INodeOutputSlot | -1
|
||||
findOutputSlotFree(optsIn?: FindFreeSlotOptions) {
|
||||
return this.#findFreeSlot(this.outputs, optsIn)
|
||||
return this._findFreeSlot(this.outputs, optsIn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the next free slot
|
||||
* @param slots The slots to search, i.e. this.inputs or this.outputs
|
||||
*/
|
||||
#findFreeSlot<TSlot extends INodeInputSlot | INodeOutputSlot>(
|
||||
private _findFreeSlot<TSlot extends INodeInputSlot | INodeOutputSlot>(
|
||||
slots: TSlot[],
|
||||
options?: FindFreeSlotOptions
|
||||
): TSlot | number {
|
||||
@@ -2357,7 +2361,7 @@ export class LGraphNode
|
||||
preferFreeSlot?: boolean,
|
||||
doNotUseOccupied?: boolean
|
||||
) {
|
||||
return this.#findSlotByType(
|
||||
return this._findSlotByType(
|
||||
this.inputs,
|
||||
type,
|
||||
returnObj,
|
||||
@@ -2387,7 +2391,7 @@ export class LGraphNode
|
||||
preferFreeSlot?: boolean,
|
||||
doNotUseOccupied?: boolean
|
||||
) {
|
||||
return this.#findSlotByType(
|
||||
return this._findSlotByType(
|
||||
this.outputs,
|
||||
type,
|
||||
returnObj,
|
||||
@@ -2433,14 +2437,14 @@ export class LGraphNode
|
||||
doNotUseOccupied?: boolean
|
||||
): number | INodeOutputSlot | INodeInputSlot {
|
||||
return input
|
||||
? this.#findSlotByType(
|
||||
? this._findSlotByType(
|
||||
this.inputs,
|
||||
type,
|
||||
returnObj,
|
||||
preferFreeSlot,
|
||||
doNotUseOccupied
|
||||
)
|
||||
: this.#findSlotByType(
|
||||
: this._findSlotByType(
|
||||
this.outputs,
|
||||
type,
|
||||
returnObj,
|
||||
@@ -2461,7 +2465,7 @@ export class LGraphNode
|
||||
* @see {findInputSlotByType}
|
||||
* @returns If a match is found, the slot if returnObj is true, otherwise the index. If no matches are found, -1
|
||||
*/
|
||||
#findSlotByType<TSlot extends INodeInputSlot | INodeOutputSlot>(
|
||||
private _findSlotByType<TSlot extends INodeInputSlot | INodeOutputSlot>(
|
||||
slots: TSlot[],
|
||||
type: ISlotType,
|
||||
returnObj?: boolean,
|
||||
@@ -3310,8 +3314,8 @@ export class LGraphNode
|
||||
// default vertical slots
|
||||
const offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||
const slotIndex = is_input
|
||||
? this.#defaultVerticalInputs.indexOf(this.inputs[slot_number])
|
||||
: this.#defaultVerticalOutputs.indexOf(this.outputs[slot_number])
|
||||
? this._defaultVerticalInputs.indexOf(this.inputs[slot_number])
|
||||
: this._defaultVerticalOutputs.indexOf(this.outputs[slot_number])
|
||||
|
||||
out[0] = is_input ? nodeX + offset : nodeX + this.size[0] + 1 - offset
|
||||
out[1] =
|
||||
@@ -3324,7 +3328,7 @@ export class LGraphNode
|
||||
/**
|
||||
* @internal The inputs that are not positioned with absolute coordinates.
|
||||
*/
|
||||
get #defaultVerticalInputs() {
|
||||
private get _defaultVerticalInputs() {
|
||||
return this.inputs.filter(
|
||||
(slot) => !slot.pos && !(this.widgets?.length && isWidgetInputSlot(slot))
|
||||
)
|
||||
@@ -3333,7 +3337,7 @@ export class LGraphNode
|
||||
/**
|
||||
* @internal The outputs that are not positioned with absolute coordinates.
|
||||
*/
|
||||
get #defaultVerticalOutputs() {
|
||||
private get _defaultVerticalOutputs() {
|
||||
return this.outputs.filter((slot: INodeOutputSlot) => !slot.pos)
|
||||
}
|
||||
|
||||
@@ -3341,7 +3345,7 @@ export class LGraphNode
|
||||
* Get the context needed for slot position calculations
|
||||
* @internal
|
||||
*/
|
||||
#getSlotPositionContext(): SlotPositionContext {
|
||||
private _getSlotPositionContext(): SlotPositionContext {
|
||||
return {
|
||||
nodeX: this.pos[0],
|
||||
nodeY: this.pos[1],
|
||||
@@ -3373,7 +3377,7 @@ export class LGraphNode
|
||||
* @returns Position of the centre of the input slot in graph co-ordinates.
|
||||
*/
|
||||
getInputSlotPos(input: INodeInputSlot): Point {
|
||||
return calculateInputSlotPosFromSlot(this.#getSlotPositionContext(), input)
|
||||
return calculateInputSlotPosFromSlot(this._getSlotPositionContext(), input)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3916,13 +3920,13 @@ export class LGraphNode
|
||||
*/
|
||||
drawCollapsedSlots(ctx: CanvasRenderingContext2D): void {
|
||||
// Render the first connected slot only.
|
||||
for (const slot of this.#concreteInputs) {
|
||||
for (const slot of this._concreteInputs) {
|
||||
if (slot.link != null) {
|
||||
slot.drawCollapsed(ctx)
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const slot of this.#concreteOutputs) {
|
||||
for (const slot of this._concreteOutputs) {
|
||||
if (slot.links?.length) {
|
||||
slot.drawCollapsed(ctx)
|
||||
break
|
||||
@@ -3934,7 +3938,7 @@ export class LGraphNode
|
||||
return [...this.inputs, ...this.outputs]
|
||||
}
|
||||
|
||||
#measureSlot(
|
||||
private _measureSlot(
|
||||
slot: NodeInputSlot | NodeOutputSlot,
|
||||
slotIndex: number,
|
||||
isInput: boolean
|
||||
@@ -3951,27 +3955,27 @@ export class LGraphNode
|
||||
slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT
|
||||
}
|
||||
|
||||
#measureSlots(): ReadOnlyRect | null {
|
||||
private _measureSlots(): ReadOnlyRect | null {
|
||||
const slots: (NodeInputSlot | NodeOutputSlot)[] = []
|
||||
|
||||
for (const [slotIndex, slot] of this.#concreteInputs.entries()) {
|
||||
for (const [slotIndex, slot] of this._concreteInputs.entries()) {
|
||||
// Unrecognized nodes (Nodes with error) has inputs but no widgets. Treat
|
||||
// converted inputs as normal inputs.
|
||||
/** Widget input slots are handled in {@link layoutWidgetInputSlots} */
|
||||
if (this.widgets?.length && isWidgetInputSlot(slot)) continue
|
||||
|
||||
this.#measureSlot(slot, slotIndex, true)
|
||||
this._measureSlot(slot, slotIndex, true)
|
||||
slots.push(slot)
|
||||
}
|
||||
for (const [slotIndex, slot] of this.#concreteOutputs.entries()) {
|
||||
this.#measureSlot(slot, slotIndex, false)
|
||||
for (const [slotIndex, slot] of this._concreteOutputs.entries()) {
|
||||
this._measureSlot(slot, slotIndex, false)
|
||||
slots.push(slot)
|
||||
}
|
||||
|
||||
return slots.length ? createBounds(slots, 0) : null
|
||||
}
|
||||
|
||||
#getMouseOverSlot(slot: INodeSlot): INodeSlot | null {
|
||||
private _getMouseOverSlot(slot: INodeSlot): INodeSlot | null {
|
||||
const isInput = isINodeInputSlot(slot)
|
||||
const mouseOverId = this.mouseOver?.[isInput ? 'inputId' : 'outputId'] ?? -1
|
||||
if (mouseOverId === -1) {
|
||||
@@ -3980,11 +3984,11 @@ export class LGraphNode
|
||||
return isInput ? this.inputs[mouseOverId] : this.outputs[mouseOverId]
|
||||
}
|
||||
|
||||
#isMouseOverSlot(slot: INodeSlot): boolean {
|
||||
return this.#getMouseOverSlot(slot) === slot
|
||||
private _isMouseOverSlot(slot: INodeSlot): boolean {
|
||||
return this._getMouseOverSlot(slot) === slot
|
||||
}
|
||||
|
||||
#isMouseOverWidget(widget: IBaseWidget | undefined): boolean {
|
||||
private _isMouseOverWidget(widget: IBaseWidget | undefined): boolean {
|
||||
if (!widget) return false
|
||||
return this.mouseOver?.overWidget === widget
|
||||
}
|
||||
@@ -4016,9 +4020,9 @@ export class LGraphNode
|
||||
ctx: CanvasRenderingContext2D,
|
||||
{ fromSlot, colorContext, editorAlpha, lowQuality }: DrawSlotsOptions
|
||||
) {
|
||||
for (const slot of [...this.#concreteInputs, ...this.#concreteOutputs]) {
|
||||
for (const slot of [...this._concreteInputs, ...this._concreteOutputs]) {
|
||||
const isValidTarget = fromSlot && slot.isValidTarget(fromSlot)
|
||||
const isMouseOverSlot = this.#isMouseOverSlot(slot)
|
||||
const isMouseOverSlot = this._isMouseOverSlot(slot)
|
||||
|
||||
// change opacity of incompatible slots when dragging a connection
|
||||
const isValid = !fromSlot || isValidTarget
|
||||
@@ -4033,7 +4037,7 @@ export class LGraphNode
|
||||
isMouseOverSlot ||
|
||||
isValidTarget ||
|
||||
!slot.isWidgetInputSlot ||
|
||||
this.#isMouseOverWidget(this.getWidgetFromSlot(slot)) ||
|
||||
this._isMouseOverWidget(this.getWidgetFromSlot(slot)) ||
|
||||
slot.isConnected ||
|
||||
slot.alwaysVisible
|
||||
) {
|
||||
@@ -4054,7 +4058,7 @@ export class LGraphNode
|
||||
* - {@link IBaseWidget.y}
|
||||
* @param widgetStartY The y-coordinate of the first widget
|
||||
*/
|
||||
#arrangeWidgets(widgetStartY: number): void {
|
||||
private _arrangeWidgets(widgetStartY: number): void {
|
||||
if (!this.widgets || !this.widgets.length) return
|
||||
|
||||
const bodyHeight = this.bodyHeight
|
||||
@@ -4132,7 +4136,7 @@ export class LGraphNode
|
||||
/**
|
||||
* Arranges the layout of the node's widget input slots.
|
||||
*/
|
||||
#arrangeWidgetInputSlots(): void {
|
||||
private _arrangeWidgetInputSlots(): void {
|
||||
if (!this.widgets) return
|
||||
|
||||
const slotByWidgetName = new Map<
|
||||
@@ -4154,10 +4158,10 @@ export class LGraphNode
|
||||
const slot = slotByWidgetName.get(widget.name)
|
||||
if (!slot) continue
|
||||
|
||||
const actualSlot = this.#concreteInputs[slot.index]
|
||||
const actualSlot = this._concreteInputs[slot.index]
|
||||
const offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||
actualSlot.pos = [offset, widget.y + offset]
|
||||
this.#measureSlot(actualSlot, slot.index, true)
|
||||
this._measureSlot(actualSlot, slot.index, true)
|
||||
}
|
||||
} else {
|
||||
// For Vue positioning, just measure the slots without setting pos
|
||||
@@ -4165,7 +4169,7 @@ export class LGraphNode
|
||||
const slot = slotByWidgetName.get(widget.name)
|
||||
if (!slot) continue
|
||||
|
||||
this.#measureSlot(this.#concreteInputs[slot.index], slot.index, true)
|
||||
this._measureSlot(this._concreteInputs[slot.index], slot.index, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4178,10 +4182,10 @@ export class LGraphNode
|
||||
* have been removed from the ecosystem.
|
||||
*/
|
||||
_setConcreteSlots(): void {
|
||||
this.#concreteInputs = this.inputs.map((slot) =>
|
||||
this._concreteInputs = this.inputs.map((slot) =>
|
||||
toClass(NodeInputSlot, slot, this)
|
||||
)
|
||||
this.#concreteOutputs = this.outputs.map((slot) =>
|
||||
this._concreteOutputs = this.outputs.map((slot) =>
|
||||
toClass(NodeOutputSlot, slot, this)
|
||||
)
|
||||
}
|
||||
@@ -4190,12 +4194,12 @@ export class LGraphNode
|
||||
* Arranges node elements in preparation for rendering (slots & widgets).
|
||||
*/
|
||||
arrange(): void {
|
||||
const slotsBounds = this.#measureSlots()
|
||||
const slotsBounds = this._measureSlots()
|
||||
const widgetStartY = slotsBounds
|
||||
? slotsBounds[1] + slotsBounds[3] - this.pos[1]
|
||||
: 0
|
||||
this.#arrangeWidgets(widgetStartY)
|
||||
this.#arrangeWidgetInputSlots()
|
||||
this._arrangeWidgets(widgetStartY)
|
||||
this._arrangeWidgetInputSlots()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,24 +25,24 @@ export class LGraphNodeProperties {
|
||||
node: LGraphNode
|
||||
|
||||
/** Set of property paths that have been instrumented */
|
||||
#instrumentedPaths = new Set<string>()
|
||||
private _instrumentedPaths = new Set<string>()
|
||||
|
||||
constructor(node: LGraphNode) {
|
||||
this.node = node
|
||||
|
||||
this.#setupInstrumentation()
|
||||
this._setupInstrumentation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up property instrumentation for all tracked properties
|
||||
*/
|
||||
#setupInstrumentation(): void {
|
||||
private _setupInstrumentation(): void {
|
||||
for (const path of DEFAULT_TRACKED_PROPERTIES) {
|
||||
this.#instrumentProperty(path)
|
||||
this._instrumentProperty(path)
|
||||
}
|
||||
}
|
||||
|
||||
#resolveTargetObject(parts: string[]): {
|
||||
private _resolveTargetObject(parts: string[]): {
|
||||
targetObject: Record<string, unknown>
|
||||
propertyName: string
|
||||
} {
|
||||
@@ -73,14 +73,14 @@ export class LGraphNodeProperties {
|
||||
/**
|
||||
* Instruments a single property to track changes
|
||||
*/
|
||||
#instrumentProperty(path: string): void {
|
||||
private _instrumentProperty(path: string): void {
|
||||
const parts = path.split('.')
|
||||
|
||||
if (parts.length > 1) {
|
||||
this.#ensureNestedPath(path)
|
||||
this._ensureNestedPath(path)
|
||||
}
|
||||
|
||||
const { targetObject, propertyName } = this.#resolveTargetObject(parts)
|
||||
const { targetObject, propertyName } = this._resolveTargetObject(parts)
|
||||
|
||||
const hasProperty = Object.prototype.hasOwnProperty.call(
|
||||
targetObject,
|
||||
@@ -96,7 +96,7 @@ export class LGraphNodeProperties {
|
||||
set: (newValue: unknown) => {
|
||||
const oldValue = value
|
||||
value = newValue
|
||||
this.#emitPropertyChange(path, oldValue, newValue)
|
||||
this._emitPropertyChange(path, oldValue, newValue)
|
||||
|
||||
// Update enumerable: true for non-undefined values, false for undefined
|
||||
const shouldBeEnumerable = newValue !== undefined
|
||||
@@ -121,24 +121,24 @@ export class LGraphNodeProperties {
|
||||
Object.defineProperty(
|
||||
targetObject,
|
||||
propertyName,
|
||||
this.#createInstrumentedDescriptor(path, currentValue)
|
||||
this._createInstrumentedDescriptor(path, currentValue)
|
||||
)
|
||||
}
|
||||
|
||||
this.#instrumentedPaths.add(path)
|
||||
this._instrumentedPaths.add(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a property descriptor that emits change events
|
||||
*/
|
||||
#createInstrumentedDescriptor(
|
||||
private _createInstrumentedDescriptor(
|
||||
propertyPath: string,
|
||||
initialValue: unknown
|
||||
): PropertyDescriptor {
|
||||
return this.#createInstrumentedDescriptorTyped(propertyPath, initialValue)
|
||||
return this._createInstrumentedDescriptorTyped(propertyPath, initialValue)
|
||||
}
|
||||
|
||||
#createInstrumentedDescriptorTyped<TValue>(
|
||||
private _createInstrumentedDescriptorTyped<TValue>(
|
||||
propertyPath: string,
|
||||
initialValue: TValue
|
||||
): PropertyDescriptor {
|
||||
@@ -149,7 +149,7 @@ export class LGraphNodeProperties {
|
||||
set: (newValue: TValue) => {
|
||||
const oldValue = value
|
||||
value = newValue
|
||||
this.#emitPropertyChange(propertyPath, oldValue, newValue)
|
||||
this._emitPropertyChange(propertyPath, oldValue, newValue)
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
@@ -159,15 +159,15 @@ export class LGraphNodeProperties {
|
||||
/**
|
||||
* Emits a property change event if the node is connected to a graph
|
||||
*/
|
||||
#emitPropertyChange(
|
||||
private _emitPropertyChange(
|
||||
propertyPath: string,
|
||||
oldValue: unknown,
|
||||
newValue: unknown
|
||||
): void {
|
||||
this.#emitPropertyChangeTyped(propertyPath, oldValue, newValue)
|
||||
this._emitPropertyChangeTyped(propertyPath, oldValue, newValue)
|
||||
}
|
||||
|
||||
#emitPropertyChangeTyped<TValue>(
|
||||
private _emitPropertyChangeTyped<TValue>(
|
||||
propertyPath: string,
|
||||
oldValue: TValue,
|
||||
newValue: TValue
|
||||
@@ -183,7 +183,7 @@ export class LGraphNodeProperties {
|
||||
/**
|
||||
* Ensures parent objects exist for nested properties
|
||||
*/
|
||||
#ensureNestedPath(path: string): void {
|
||||
private _ensureNestedPath(path: string): void {
|
||||
const parts = path.split('.')
|
||||
// LGraphNode supports dynamic property access at runtime
|
||||
let current: Record<string, unknown> = this.node as unknown as Record<
|
||||
@@ -208,7 +208,7 @@ export class LGraphNodeProperties {
|
||||
* Checks if a property is being tracked
|
||||
*/
|
||||
isTracked(path: string): boolean {
|
||||
return this.#instrumentedPaths.has(path)
|
||||
return this._instrumentedPaths.has(path)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,301 +1,43 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`LGraph > supports schema v0.4 graphs > oldSchemaGraph 1`] = `
|
||||
LGraph {
|
||||
"_groups": [
|
||||
LGraphGroup {
|
||||
"_bounding": Rectangle [
|
||||
{
|
||||
"config": {},
|
||||
"definitions": undefined,
|
||||
"extra": {
|
||||
"reroutes": undefined,
|
||||
},
|
||||
"floatingLinks": undefined,
|
||||
"groups": [
|
||||
{
|
||||
"bounding": [
|
||||
20,
|
||||
20,
|
||||
1,
|
||||
3,
|
||||
],
|
||||
"_children": Set {},
|
||||
"_nodes": [],
|
||||
"_pos": Float64Array [
|
||||
20,
|
||||
20,
|
||||
],
|
||||
"_size": Float64Array [
|
||||
1,
|
||||
3,
|
||||
],
|
||||
"color": "#6029aa",
|
||||
"flags": {},
|
||||
"font": undefined,
|
||||
"font_size": 14,
|
||||
"graph": [Circular],
|
||||
"id": 123,
|
||||
"selected": undefined,
|
||||
"setDirtyCanvas": [Function],
|
||||
"title": "A group to test with",
|
||||
},
|
||||
],
|
||||
"_input_nodes": undefined,
|
||||
"_last_trigger_time": undefined,
|
||||
"_links": Map {},
|
||||
"_nodes": [
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": Float64Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": Rectangle [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": Float64Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"action_call": undefined,
|
||||
"action_triggered": undefined,
|
||||
"badgePosition": "top-left",
|
||||
"badges": [],
|
||||
"bgcolor": undefined,
|
||||
"block_delete": undefined,
|
||||
"boxcolor": undefined,
|
||||
"changeTracker": undefined,
|
||||
"clip_area": undefined,
|
||||
"clonable": undefined,
|
||||
"color": undefined,
|
||||
"console": undefined,
|
||||
"exec_version": undefined,
|
||||
"execute_triggered": undefined,
|
||||
"flags": {},
|
||||
"freeWidgetSpace": undefined,
|
||||
"gotFocusAt": undefined,
|
||||
"graph": [Circular],
|
||||
"has_errors": true,
|
||||
"id": 1,
|
||||
"ignore_remove": undefined,
|
||||
"inputs": [],
|
||||
"last_serialization": {
|
||||
"id": 1,
|
||||
},
|
||||
"locked": undefined,
|
||||
"lostFocusAt": undefined,
|
||||
"mode": 0,
|
||||
"mouseOver": undefined,
|
||||
"order": 0,
|
||||
"outputs": [],
|
||||
"progress": undefined,
|
||||
"properties": {},
|
||||
"properties_info": [],
|
||||
"redraw_on_mouse": undefined,
|
||||
"removable": undefined,
|
||||
"resizable": undefined,
|
||||
"selected": undefined,
|
||||
"serialize_widgets": undefined,
|
||||
"showAdvanced": undefined,
|
||||
"strokeStyles": {
|
||||
"error": [Function],
|
||||
"selected": [Function],
|
||||
},
|
||||
"title": undefined,
|
||||
"title_buttons": [],
|
||||
"type": "",
|
||||
"widgets": undefined,
|
||||
"widgets_start_y": undefined,
|
||||
"widgets_up": undefined,
|
||||
},
|
||||
],
|
||||
"_nodes_by_id": {
|
||||
"1": LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": Float64Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": Rectangle [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": Float64Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"action_call": undefined,
|
||||
"action_triggered": undefined,
|
||||
"badgePosition": "top-left",
|
||||
"badges": [],
|
||||
"bgcolor": undefined,
|
||||
"block_delete": undefined,
|
||||
"boxcolor": undefined,
|
||||
"changeTracker": undefined,
|
||||
"clip_area": undefined,
|
||||
"clonable": undefined,
|
||||
"color": undefined,
|
||||
"console": undefined,
|
||||
"exec_version": undefined,
|
||||
"execute_triggered": undefined,
|
||||
"flags": {},
|
||||
"freeWidgetSpace": undefined,
|
||||
"gotFocusAt": undefined,
|
||||
"graph": [Circular],
|
||||
"has_errors": true,
|
||||
"id": 1,
|
||||
"ignore_remove": undefined,
|
||||
"inputs": [],
|
||||
"last_serialization": {
|
||||
"id": 1,
|
||||
},
|
||||
"locked": undefined,
|
||||
"lostFocusAt": undefined,
|
||||
"mode": 0,
|
||||
"mouseOver": undefined,
|
||||
"order": 0,
|
||||
"outputs": [],
|
||||
"progress": undefined,
|
||||
"properties": {},
|
||||
"properties_info": [],
|
||||
"redraw_on_mouse": undefined,
|
||||
"removable": undefined,
|
||||
"resizable": undefined,
|
||||
"selected": undefined,
|
||||
"serialize_widgets": undefined,
|
||||
"showAdvanced": undefined,
|
||||
"strokeStyles": {
|
||||
"error": [Function],
|
||||
"selected": [Function],
|
||||
},
|
||||
"title": undefined,
|
||||
"title_buttons": [],
|
||||
"type": "",
|
||||
"widgets": undefined,
|
||||
"widgets_start_y": undefined,
|
||||
"widgets_up": undefined,
|
||||
},
|
||||
},
|
||||
"_nodes_executable": [],
|
||||
"_nodes_in_order": [
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": Float64Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": Rectangle [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": Float64Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"action_call": undefined,
|
||||
"action_triggered": undefined,
|
||||
"badgePosition": "top-left",
|
||||
"badges": [],
|
||||
"bgcolor": undefined,
|
||||
"block_delete": undefined,
|
||||
"boxcolor": undefined,
|
||||
"changeTracker": undefined,
|
||||
"clip_area": undefined,
|
||||
"clonable": undefined,
|
||||
"color": undefined,
|
||||
"console": undefined,
|
||||
"exec_version": undefined,
|
||||
"execute_triggered": undefined,
|
||||
"flags": {},
|
||||
"freeWidgetSpace": undefined,
|
||||
"gotFocusAt": undefined,
|
||||
"graph": [Circular],
|
||||
"has_errors": true,
|
||||
"id": 1,
|
||||
"ignore_remove": undefined,
|
||||
"inputs": [],
|
||||
"last_serialization": {
|
||||
"id": 1,
|
||||
},
|
||||
"locked": undefined,
|
||||
"lostFocusAt": undefined,
|
||||
"mode": 0,
|
||||
"mouseOver": undefined,
|
||||
"order": 0,
|
||||
"outputs": [],
|
||||
"progress": undefined,
|
||||
"properties": {},
|
||||
"properties_info": [],
|
||||
"redraw_on_mouse": undefined,
|
||||
"removable": undefined,
|
||||
"resizable": undefined,
|
||||
"selected": undefined,
|
||||
"serialize_widgets": undefined,
|
||||
"showAdvanced": undefined,
|
||||
"strokeStyles": {
|
||||
"error": [Function],
|
||||
"selected": [Function],
|
||||
},
|
||||
"title": undefined,
|
||||
"title_buttons": [],
|
||||
"type": "",
|
||||
"widgets": undefined,
|
||||
"widgets_start_y": undefined,
|
||||
"widgets_up": undefined,
|
||||
},
|
||||
],
|
||||
"_subgraphs": Map {},
|
||||
"_version": 3,
|
||||
"catch_errors": true,
|
||||
"config": {},
|
||||
"elapsed_time": 0.01,
|
||||
"errors_in_execution": undefined,
|
||||
"events": CustomEventTarget {
|
||||
Symbol(listeners): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
Symbol(listenerOptions): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
},
|
||||
"execution_time": undefined,
|
||||
"execution_timer_id": undefined,
|
||||
"extra": {},
|
||||
"filter": undefined,
|
||||
"fixedtime": 0,
|
||||
"fixedtime_lapse": 0.01,
|
||||
"floatingLinksInternal": Map {},
|
||||
"globaltime": 0,
|
||||
"id": "b4e984f1-b421-4d24-b8b4-ff895793af13",
|
||||
"iteration": 0,
|
||||
"last_update_time": 0,
|
||||
"links": Map {},
|
||||
"list_of_graphcanvas": null,
|
||||
"nodes_actioning": [],
|
||||
"nodes_executedAction": [],
|
||||
"nodes_executing": [],
|
||||
"onTrigger": undefined,
|
||||
"reroutesInternal": Map {},
|
||||
"last_link_id": 0,
|
||||
"last_node_id": 1,
|
||||
"links": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"mode": 0,
|
||||
"pos": [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
},
|
||||
],
|
||||
"revision": 0,
|
||||
"runningtime": 0,
|
||||
"starttime": 0,
|
||||
"state": {
|
||||
"lastGroupId": 123,
|
||||
"lastLinkId": 0,
|
||||
"lastNodeId": 1,
|
||||
"lastRerouteId": 0,
|
||||
},
|
||||
"status": 1,
|
||||
"vars": {},
|
||||
"version": 0.4,
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -51,11 +51,11 @@ export class InputIndicators implements Disposable {
|
||||
const element = canvas.canvas
|
||||
const options = { capture: true, signal } satisfies AddEventListenerOptions
|
||||
|
||||
element.addEventListener('pointerdown', this.#onPointerDownOrMove, options)
|
||||
element.addEventListener('pointermove', this.#onPointerDownOrMove, options)
|
||||
element.addEventListener('pointerup', this.#onPointerUp, options)
|
||||
element.addEventListener('keydown', this.#onKeyDownOrUp, options)
|
||||
document.addEventListener('keyup', this.#onKeyDownOrUp, options)
|
||||
element.addEventListener('pointerdown', this._onPointerDownOrMove, options)
|
||||
element.addEventListener('pointermove', this._onPointerDownOrMove, options)
|
||||
element.addEventListener('pointerup', this._onPointerUp, options)
|
||||
element.addEventListener('keydown', this._onKeyDownOrUp, options)
|
||||
document.addEventListener('keyup', this._onKeyDownOrUp, options)
|
||||
|
||||
const origDrawFrontCanvas = canvas.drawFrontCanvas.bind(canvas)
|
||||
signal.addEventListener('abort', () => {
|
||||
@@ -68,7 +68,7 @@ export class InputIndicators implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
#onPointerDownOrMove = this.onPointerDownOrMove.bind(this)
|
||||
private _onPointerDownOrMove = this.onPointerDownOrMove.bind(this)
|
||||
onPointerDownOrMove(e: MouseEvent): void {
|
||||
this.mouse0Down = (e.buttons & 1) === 1
|
||||
this.mouse1Down = (e.buttons & 4) === 4
|
||||
@@ -80,14 +80,14 @@ export class InputIndicators implements Disposable {
|
||||
this.canvas.setDirty(true)
|
||||
}
|
||||
|
||||
#onPointerUp = this.onPointerUp.bind(this)
|
||||
private _onPointerUp = this.onPointerUp.bind(this)
|
||||
onPointerUp(): void {
|
||||
this.mouse0Down = false
|
||||
this.mouse1Down = false
|
||||
this.mouse2Down = false
|
||||
}
|
||||
|
||||
#onKeyDownOrUp = this.onKeyDownOrUp.bind(this)
|
||||
private _onKeyDownOrUp = this.onKeyDownOrUp.bind(this)
|
||||
onKeyDownOrUp(e: KeyboardEvent): void {
|
||||
this.ctrlDown = e.ctrlKey
|
||||
this.altDown = e.altKey
|
||||
|
||||
@@ -115,10 +115,10 @@ export class LinkConnector {
|
||||
/** The reroute beneath the pointer, if it is a valid connection target. */
|
||||
overReroute?: Reroute
|
||||
|
||||
readonly #setConnectingLinks: (value: ConnectingLink[]) => void
|
||||
private readonly _setConnectingLinks: (value: ConnectingLink[]) => void
|
||||
|
||||
constructor(setConnectingLinks: (value: ConnectingLink[]) => void) {
|
||||
this.#setConnectingLinks = setConnectingLinks
|
||||
this._setConnectingLinks = setConnectingLinks
|
||||
}
|
||||
|
||||
get isConnecting() {
|
||||
@@ -253,7 +253,7 @@ export class LinkConnector {
|
||||
state.connectingTo = 'input'
|
||||
state.draggingExistingLinks = true
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
}
|
||||
|
||||
/** Drag all links from an output to a new output. */
|
||||
@@ -364,7 +364,7 @@ export class LinkConnector {
|
||||
state.multi = true
|
||||
state.connectingTo = 'output'
|
||||
|
||||
this.#setLegacyLinks(true)
|
||||
this._setLegacyLinks(true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,7 +387,7 @@ export class LinkConnector {
|
||||
|
||||
state.connectingTo = 'input'
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,7 +410,7 @@ export class LinkConnector {
|
||||
|
||||
state.connectingTo = 'output'
|
||||
|
||||
this.#setLegacyLinks(true)
|
||||
this._setLegacyLinks(true)
|
||||
}
|
||||
|
||||
dragNewFromSubgraphInput(
|
||||
@@ -431,7 +431,7 @@ export class LinkConnector {
|
||||
|
||||
this.state.connectingTo = 'input'
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
}
|
||||
|
||||
dragNewFromSubgraphOutput(
|
||||
@@ -452,7 +452,7 @@ export class LinkConnector {
|
||||
|
||||
this.state.connectingTo = 'output'
|
||||
|
||||
this.#setLegacyLinks(true)
|
||||
this._setLegacyLinks(true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,7 +489,7 @@ export class LinkConnector {
|
||||
|
||||
this.state.connectingTo = 'input'
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ export class LinkConnector {
|
||||
|
||||
this.state.connectingTo = 'input'
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,7 +553,7 @@ export class LinkConnector {
|
||||
|
||||
this.state.connectingTo = 'output'
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -581,7 +581,7 @@ export class LinkConnector {
|
||||
|
||||
this.state.connectingTo = 'output'
|
||||
|
||||
this.#setLegacyLinks(true)
|
||||
this._setLegacyLinks(true)
|
||||
}
|
||||
|
||||
dragFromLinkSegment(network: LinkNetwork, linkSegment: LinkSegment): void {
|
||||
@@ -603,7 +603,7 @@ export class LinkConnector {
|
||||
|
||||
state.connectingTo = 'input'
|
||||
|
||||
this.#setLegacyLinks(false)
|
||||
this._setLegacyLinks(false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -754,7 +754,7 @@ export class LinkConnector {
|
||||
const output = node.getOutputOnPos([canvasX, canvasY])
|
||||
|
||||
if (output) {
|
||||
this.#dropOnOutput(node, output)
|
||||
this._dropOnOutput(node, output)
|
||||
} else {
|
||||
this.connectToNode(node, event)
|
||||
}
|
||||
@@ -765,7 +765,7 @@ export class LinkConnector {
|
||||
|
||||
// Input slot
|
||||
if (inputOrSocket) {
|
||||
this.#dropOnInput(node, inputOrSocket)
|
||||
this._dropOnInput(node, inputOrSocket)
|
||||
} else {
|
||||
// Node background / title
|
||||
this.connectToNode(node, event)
|
||||
@@ -911,7 +911,7 @@ export class LinkConnector {
|
||||
return
|
||||
}
|
||||
|
||||
this.#dropOnOutput(node, output)
|
||||
this._dropOnOutput(node, output)
|
||||
} else if (connectingTo === 'input') {
|
||||
// Dropping new input link
|
||||
const input = node.findInputByType(firstLink.fromSlot.type)?.slot
|
||||
@@ -922,11 +922,11 @@ export class LinkConnector {
|
||||
return
|
||||
}
|
||||
|
||||
this.#dropOnInput(node, input)
|
||||
this._dropOnInput(node, input)
|
||||
}
|
||||
}
|
||||
|
||||
#dropOnInput(node: LGraphNode, input: INodeInputSlot): void {
|
||||
private _dropOnInput(node: LGraphNode, input: INodeInputSlot): void {
|
||||
for (const link of this.renderLinks) {
|
||||
if (!link.canConnectToInput(node, input)) continue
|
||||
|
||||
@@ -934,7 +934,7 @@ export class LinkConnector {
|
||||
}
|
||||
}
|
||||
|
||||
#dropOnOutput(node: LGraphNode, output: INodeOutputSlot): void {
|
||||
private _dropOnOutput(node: LGraphNode, output: INodeOutputSlot): void {
|
||||
for (const link of this.renderLinks) {
|
||||
if (!link.canConnectToOutput(node, output)) {
|
||||
if (
|
||||
@@ -1014,7 +1014,7 @@ export class LinkConnector {
|
||||
}
|
||||
|
||||
/** Sets connecting_links, used by some extensions still. */
|
||||
#setLegacyLinks(fromSlotIsInput: boolean): void {
|
||||
private _setLegacyLinks(fromSlotIsInput: boolean): void {
|
||||
const links = this.renderLinks.map((link) => {
|
||||
const input = fromSlotIsInput ? (link.fromSlot as INodeInputSlot) : null
|
||||
const output = fromSlotIsInput ? null : (link.fromSlot as INodeOutputSlot)
|
||||
@@ -1033,7 +1033,7 @@ export class LinkConnector {
|
||||
afterRerouteId
|
||||
} satisfies ConnectingLink
|
||||
})
|
||||
this.#setConnectingLinks(links)
|
||||
this._setConnectingLinks(links)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,10 +10,10 @@ import type { ReadOnlyRect, Size } from '@/lib/litegraph/src/interfaces'
|
||||
* - Width and height are then updated, clamped to min/max values
|
||||
*/
|
||||
export class ConstrainedSize {
|
||||
#width: number = 0
|
||||
#height: number = 0
|
||||
#desiredWidth: number = 0
|
||||
#desiredHeight: number = 0
|
||||
private _width: number = 0
|
||||
private _height: number = 0
|
||||
private _desiredWidth: number = 0
|
||||
private _desiredHeight: number = 0
|
||||
|
||||
minWidth: number = 0
|
||||
minHeight: number = 0
|
||||
@@ -21,29 +21,29 @@ export class ConstrainedSize {
|
||||
maxHeight: number = Infinity
|
||||
|
||||
get width() {
|
||||
return this.#width
|
||||
return this._width
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.#height
|
||||
return this._height
|
||||
}
|
||||
|
||||
get desiredWidth() {
|
||||
return this.#desiredWidth
|
||||
return this._desiredWidth
|
||||
}
|
||||
|
||||
set desiredWidth(value: number) {
|
||||
this.#desiredWidth = value
|
||||
this.#width = clamp(value, this.minWidth, this.maxWidth)
|
||||
this._desiredWidth = value
|
||||
this._width = clamp(value, this.minWidth, this.maxWidth)
|
||||
}
|
||||
|
||||
get desiredHeight() {
|
||||
return this.#desiredHeight
|
||||
return this._desiredHeight
|
||||
}
|
||||
|
||||
set desiredHeight(value: number) {
|
||||
this.#desiredHeight = value
|
||||
this.#height = clamp(value, this.minHeight, this.maxHeight)
|
||||
this._desiredHeight = value
|
||||
this._height = clamp(value, this.minHeight, this.maxHeight)
|
||||
}
|
||||
|
||||
constructor(width: number, height: number) {
|
||||
@@ -70,6 +70,6 @@ export class ConstrainedSize {
|
||||
}
|
||||
|
||||
toSize(): Size {
|
||||
return [this.#width, this.#height]
|
||||
return [this._width, this._height]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ import { isInRectangle } from '@/lib/litegraph/src/measure'
|
||||
* - {@link size}: The size of the rectangle.
|
||||
*/
|
||||
export class Rectangle extends Float64Array {
|
||||
#pos: Float64Array<ArrayBuffer> | undefined
|
||||
#size: Float64Array<ArrayBuffer> | undefined
|
||||
private _pos: Float64Array<ArrayBuffer> | undefined
|
||||
private _size: Float64Array<ArrayBuffer> | undefined
|
||||
|
||||
constructor(
|
||||
x: number = 0,
|
||||
@@ -78,8 +78,8 @@ export class Rectangle extends Float64Array {
|
||||
* Updating the values of the returned object will update this rectangle.
|
||||
*/
|
||||
get pos(): Point {
|
||||
this.#pos ??= this.subarray(0, 2)
|
||||
return this.#pos! as unknown as Point
|
||||
this._pos ??= this.subarray(0, 2)
|
||||
return this._pos! as unknown as Point
|
||||
}
|
||||
|
||||
set pos(value: Readonly<Point>) {
|
||||
@@ -93,8 +93,8 @@ export class Rectangle extends Float64Array {
|
||||
* Updating the values of the returned object will update this rectangle.
|
||||
*/
|
||||
get size(): Size {
|
||||
this.#size ??= this.subarray(2, 4)
|
||||
return this.#size! as unknown as Size
|
||||
this._size ??= this.subarray(2, 4)
|
||||
return this._size! as unknown as Size
|
||||
}
|
||||
|
||||
set size(value: Readonly<Size>) {
|
||||
|
||||
@@ -23,15 +23,15 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
||||
return !!this.widget
|
||||
}
|
||||
|
||||
#widget: WeakRef<IBaseWidget> | undefined
|
||||
private _widgetRef: WeakRef<IBaseWidget> | undefined
|
||||
|
||||
/** Internal use only; API is not finalised and may change at any time. */
|
||||
get _widget(): IBaseWidget | undefined {
|
||||
return this.#widget?.deref()
|
||||
return this._widgetRef?.deref()
|
||||
}
|
||||
|
||||
set _widget(widget: IBaseWidget | undefined) {
|
||||
this.#widget = widget ? new WeakRef(widget) : undefined
|
||||
this._widgetRef = widget ? new WeakRef(widget) : undefined
|
||||
}
|
||||
|
||||
get collapsedPos(): Readonly<Point> {
|
||||
@@ -79,4 +79,12 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
||||
|
||||
ctx.textAlign = textAlign
|
||||
}
|
||||
|
||||
override toJSON(): INodeInputSlot {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
link: this.link,
|
||||
widget: this.widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import type { SubgraphOutput } from '@/lib/litegraph/src/subgraph/SubgraphOutput
|
||||
import { isSubgraphOutput } from '@/lib/litegraph/src/subgraph/subgraphUtils'
|
||||
|
||||
export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
#node: LGraphNode
|
||||
|
||||
links: LinkId[] | null
|
||||
_data?: unknown
|
||||
slot_index?: number
|
||||
@@ -27,7 +25,7 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
|
||||
get collapsedPos(): Readonly<Point> {
|
||||
return [
|
||||
this.#node._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH,
|
||||
this._node._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH,
|
||||
LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
||||
]
|
||||
}
|
||||
@@ -40,7 +38,6 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
this.links = slot.links
|
||||
this._data = slot._data
|
||||
this.slot_index = slot.slot_index
|
||||
this.#node = node
|
||||
}
|
||||
|
||||
override isValidTarget(
|
||||
@@ -78,4 +75,12 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
ctx.textAlign = textAlign
|
||||
ctx.strokeStyle = strokeStyle
|
||||
}
|
||||
|
||||
override toJSON(): INodeOutputSlot {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
links: this.links,
|
||||
slot_index: this.slot_index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
pos?: Point
|
||||
|
||||
/** The offset from the parent node to the centre point of this slot. */
|
||||
get #centreOffset(): Readonly<Point> {
|
||||
private get _centreOffset(): Readonly<Point> {
|
||||
const nodePos = this.node.pos
|
||||
const { boundingRect } = this
|
||||
|
||||
@@ -55,9 +55,9 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
/** The center point of this slot when the node is collapsed. */
|
||||
abstract get collapsedPos(): Readonly<Point>
|
||||
|
||||
#node: LGraphNode
|
||||
protected _node: LGraphNode
|
||||
get node(): LGraphNode {
|
||||
return this.#node
|
||||
return this._node
|
||||
}
|
||||
|
||||
get highlightColor(): CanvasColour {
|
||||
@@ -89,7 +89,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
super(name, type, rectangle)
|
||||
|
||||
Object.assign(this, rest)
|
||||
this.#node = node
|
||||
this._node = node
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +126,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
? this.highlightColor
|
||||
: LiteGraph.NODE_TEXT_COLOR
|
||||
|
||||
const pos = this.#centreOffset
|
||||
const pos = this._centreOffset
|
||||
const slot_type = this.type
|
||||
const slot_shape = (
|
||||
slot_type === SlotType.Array ? SlotShape.Grid : this.shape
|
||||
@@ -260,6 +260,25 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
ctx.lineWidth = originalLineWidth
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom JSON serialization to prevent circular reference errors.
|
||||
* Returns only serializable slot properties without the node back-reference.
|
||||
*/
|
||||
toJSON(): INodeSlot {
|
||||
return {
|
||||
name: this.name,
|
||||
type: this.type,
|
||||
label: this.label,
|
||||
color_on: this.color_on,
|
||||
color_off: this.color_off,
|
||||
shape: this.shape,
|
||||
dir: this.dir,
|
||||
localized_name: this.localized_name,
|
||||
pos: this.pos,
|
||||
boundingRect: [...this.boundingRect] as [number, number, number, number]
|
||||
}
|
||||
}
|
||||
|
||||
drawCollapsed(ctx: CanvasRenderingContext2D) {
|
||||
const [x, y] = this.collapsedPos
|
||||
|
||||
|
||||
131
src/lib/litegraph/src/serialization.test.ts
Normal file
131
src/lib/litegraph/src/serialization.test.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { LGraph, LGraphNode, LiteGraph } from './litegraph'
|
||||
import type { NodeInputSlot } from './node/NodeInputSlot'
|
||||
import type { NodeOutputSlot } from './node/NodeOutputSlot'
|
||||
|
||||
class TestNode extends LGraphNode {
|
||||
static override title = 'TestNode'
|
||||
constructor() {
|
||||
super('TestNode')
|
||||
}
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType('test/TestNode', TestNode)
|
||||
|
||||
describe('Serialization - Circular Reference Prevention', () => {
|
||||
describe('LGraph.toJSON()', () => {
|
||||
it('should serialize without circular reference errors', () => {
|
||||
const graph = new LGraph()
|
||||
|
||||
expect(() => JSON.stringify(graph)).not.toThrow()
|
||||
})
|
||||
|
||||
it('should return serialize() output from toJSON()', () => {
|
||||
const graph = new LGraph()
|
||||
const serialized = graph.serialize()
|
||||
const jsonOutput = graph.toJSON()
|
||||
|
||||
expect(jsonOutput).toEqual(serialized)
|
||||
})
|
||||
|
||||
it('should not include list_of_graphcanvas in JSON output', () => {
|
||||
const graph = new LGraph()
|
||||
const json = JSON.stringify(graph)
|
||||
const parsed = JSON.parse(json)
|
||||
|
||||
expect(parsed.list_of_graphcanvas).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('NodeSlot.toJSON()', () => {
|
||||
it('NodeInputSlot should serialize without circular reference errors', () => {
|
||||
const graph = new LGraph()
|
||||
const node = LiteGraph.createNode('test/TestNode')!
|
||||
graph.add(node)
|
||||
node.addInput('test_input', 'TEST')
|
||||
|
||||
const inputSlot = node.inputs[0]
|
||||
|
||||
expect(() => JSON.stringify(inputSlot)).not.toThrow()
|
||||
})
|
||||
|
||||
it('NodeOutputSlot should serialize without circular reference errors', () => {
|
||||
const graph = new LGraph()
|
||||
const node = LiteGraph.createNode('test/TestNode')!
|
||||
graph.add(node)
|
||||
node.addOutput('test_output', 'TEST')
|
||||
|
||||
const outputSlot = node.outputs[0]
|
||||
|
||||
expect(() => JSON.stringify(outputSlot)).not.toThrow()
|
||||
})
|
||||
|
||||
it('NodeInputSlot.toJSON() should not include _node reference', () => {
|
||||
const graph = new LGraph()
|
||||
const node = LiteGraph.createNode('test/TestNode')!
|
||||
graph.add(node)
|
||||
node.addInput('test_input', 'TEST')
|
||||
|
||||
const inputSlot = node.inputs[0] as NodeInputSlot
|
||||
const json = inputSlot.toJSON()
|
||||
|
||||
expect('_node' in json).toBe(false)
|
||||
expect('node' in json).toBe(false)
|
||||
expect(json.name).toBe('test_input')
|
||||
expect(json.type).toBe('TEST')
|
||||
})
|
||||
|
||||
it('NodeOutputSlot.toJSON() should not include _node reference', () => {
|
||||
const graph = new LGraph()
|
||||
const node = LiteGraph.createNode('test/TestNode')!
|
||||
graph.add(node)
|
||||
node.addOutput('test_output', 'TEST')
|
||||
|
||||
const outputSlot = node.outputs[0] as NodeOutputSlot
|
||||
const json = outputSlot.toJSON()
|
||||
|
||||
expect('_node' in json).toBe(false)
|
||||
expect('node' in json).toBe(false)
|
||||
expect(json.name).toBe('test_output')
|
||||
expect(json.type).toBe('TEST')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Full graph with nodes - no circular references', () => {
|
||||
it('should serialize a graph with connected nodes', () => {
|
||||
const graph = new LGraph()
|
||||
|
||||
const node1 = LiteGraph.createNode('test/TestNode')!
|
||||
const node2 = LiteGraph.createNode('test/TestNode')!
|
||||
graph.add(node1)
|
||||
graph.add(node2)
|
||||
|
||||
node1.addOutput('out', 'TEST')
|
||||
node2.addInput('in', 'TEST')
|
||||
|
||||
node1.connect(0, node2, 0)
|
||||
|
||||
expect(() => JSON.stringify(graph)).not.toThrow()
|
||||
})
|
||||
|
||||
it('should serialize graph.serialize() output without errors', () => {
|
||||
const graph = new LGraph()
|
||||
|
||||
const node1 = LiteGraph.createNode('test/TestNode')!
|
||||
const node2 = LiteGraph.createNode('test/TestNode')!
|
||||
graph.add(node1)
|
||||
graph.add(node2)
|
||||
|
||||
node1.addOutput('out', 'TEST')
|
||||
node2.addInput('in', 'TEST')
|
||||
|
||||
node1.connect(0, node2, 0)
|
||||
|
||||
const serialized = graph.serialize()
|
||||
|
||||
expect(() => JSON.stringify(serialized)).not.toThrow()
|
||||
expect(() => JSON.parse(JSON.stringify(serialized))).not.toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -50,7 +50,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
inputs: { linkId: number | null; name: string; type: ISlotType }[]
|
||||
|
||||
/** Backing field for {@link id}. */
|
||||
#id: ExecutionId
|
||||
private _id: ExecutionId
|
||||
|
||||
/**
|
||||
* The path to the actual node through subgraph instances, represented as a list of all subgraph node IDs (instances),
|
||||
@@ -62,7 +62,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
* - `3` is the node ID of the actual node in the subgraph definition
|
||||
*/
|
||||
get id() {
|
||||
return this.#id
|
||||
return this._id
|
||||
}
|
||||
|
||||
get type() {
|
||||
@@ -106,7 +106,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
if (!node.graph) throw new NullGraphError()
|
||||
|
||||
// Set the internal ID of the DTO
|
||||
this.#id = [...this.subgraphNodePath, this.node.id].join(':')
|
||||
this._id = [...this.subgraphNodePath, this.node.id].join(':')
|
||||
this.graph = node.graph
|
||||
this.inputs = this.node.inputs.map((x) => ({
|
||||
linkId: x.link,
|
||||
@@ -265,7 +265,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
// Upstreamed: Bypass nodes are bypassed using the first input with matching type
|
||||
if (this.mode === LGraphEventMode.BYPASS) {
|
||||
// Bypass nodes by finding first input with matching type
|
||||
const matchingIndex = this.#getBypassSlotIndex(slot, type)
|
||||
const matchingIndex = this._getBypassSlotIndex(slot, type)
|
||||
|
||||
// No input types match - bypass not possible
|
||||
if (matchingIndex === -1) {
|
||||
@@ -281,7 +281,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
|
||||
const { node } = this
|
||||
if (node.isSubgraphNode())
|
||||
return this.#resolveSubgraphOutput(slot, type, visited)
|
||||
return this._resolveSubgraphOutput(slot, type, visited)
|
||||
|
||||
if (node.isVirtualNode) {
|
||||
const virtualLink = this.node.getInputLink(slot)
|
||||
@@ -321,7 +321,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
* @param type The type of the final target input (so type list matches are accurate)
|
||||
* @returns The index of the input slot on this node, otherwise `-1`.
|
||||
*/
|
||||
#getBypassSlotIndex(slot: number, type: ISlotType) {
|
||||
private _getBypassSlotIndex(slot: number, type: ISlotType) {
|
||||
const { inputs } = this
|
||||
const oppositeInput = inputs[slot]
|
||||
const outputType = this.node.outputs[slot].type
|
||||
@@ -358,7 +358,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
* @param visited A set of unique IDs to guard against infinite recursion. See {@link resolveInput}.
|
||||
* @returns A DTO for the node, and the origin ID / slot index of the output.
|
||||
*/
|
||||
#resolveSubgraphOutput(
|
||||
private _resolveSubgraphOutput(
|
||||
slot: number,
|
||||
type: ISlotType,
|
||||
visited: Set<string>
|
||||
|
||||
@@ -38,12 +38,12 @@ export abstract class SubgraphIONodeBase<
|
||||
static minWidth = 100
|
||||
static roundedRadius = 10
|
||||
|
||||
readonly #boundingRect: Rectangle = new Rectangle()
|
||||
private readonly _boundingRect: Rectangle = new Rectangle()
|
||||
|
||||
abstract readonly id: NodeId
|
||||
|
||||
get boundingRect(): Rectangle {
|
||||
return this.#boundingRect
|
||||
return this._boundingRect
|
||||
}
|
||||
|
||||
selected: boolean = false
|
||||
@@ -181,7 +181,7 @@ export abstract class SubgraphIONodeBase<
|
||||
): void {
|
||||
// Only allow renaming non-empty slots
|
||||
if (slot !== this.emptySlot) {
|
||||
this.#promptForSlotRename(slot, event)
|
||||
this._promptForSlotRename(slot, event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,14 +191,14 @@ export abstract class SubgraphIONodeBase<
|
||||
* @param event The event that triggered the context menu.
|
||||
*/
|
||||
protected showSlotContextMenu(slot: TSlot, event: CanvasPointerEvent): void {
|
||||
const options: (IContextMenuValue | null)[] = this.#getSlotMenuOptions(slot)
|
||||
const options: (IContextMenuValue | null)[] = this._getSlotMenuOptions(slot)
|
||||
if (!(options.length > 0)) return
|
||||
|
||||
new LiteGraph.ContextMenu(options, {
|
||||
event,
|
||||
title: slot.name || 'Subgraph Output',
|
||||
callback: (item: IContextMenuValue) => {
|
||||
this.#onSlotMenuAction(item, slot, event)
|
||||
this._onSlotMenuAction(item, slot, event)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -208,7 +208,7 @@ export abstract class SubgraphIONodeBase<
|
||||
* @param slot The slot to get the context menu options for.
|
||||
* @returns The context menu options.
|
||||
*/
|
||||
#getSlotMenuOptions(slot: TSlot): (IContextMenuValue | null)[] {
|
||||
private _getSlotMenuOptions(slot: TSlot): (IContextMenuValue | null)[] {
|
||||
const options: (IContextMenuValue | null)[] = []
|
||||
|
||||
// Disconnect option if slot has connections
|
||||
@@ -239,7 +239,7 @@ export abstract class SubgraphIONodeBase<
|
||||
* @param slot The slot
|
||||
* @param event The event that triggered the context menu.
|
||||
*/
|
||||
#onSlotMenuAction(
|
||||
private _onSlotMenuAction(
|
||||
selectedItem: IContextMenuValue,
|
||||
slot: TSlot,
|
||||
event: CanvasPointerEvent
|
||||
@@ -260,7 +260,7 @@ export abstract class SubgraphIONodeBase<
|
||||
// Rename the slot
|
||||
case 'rename':
|
||||
if (slot !== this.emptySlot) {
|
||||
this.#promptForSlotRename(slot, event)
|
||||
this._promptForSlotRename(slot, event)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -273,7 +273,7 @@ export abstract class SubgraphIONodeBase<
|
||||
* @param slot The slot to rename.
|
||||
* @param event The event that triggered the rename.
|
||||
*/
|
||||
#promptForSlotRename(slot: TSlot, event: CanvasPointerEvent): void {
|
||||
private _promptForSlotRename(slot: TSlot, event: CanvasPointerEvent): void {
|
||||
this.subgraph.canvasAction((c) =>
|
||||
c.prompt(
|
||||
'Slot name',
|
||||
@@ -362,7 +362,7 @@ export abstract class SubgraphIONodeBase<
|
||||
}
|
||||
|
||||
configure(data: ExportedSubgraphIONode): void {
|
||||
this.#boundingRect.set(data.bounding)
|
||||
this._boundingRect.set(data.bounding)
|
||||
this.pinned = data.pinned ?? false
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
override widgets: IBaseWidget[] = []
|
||||
|
||||
/** Manages lifecycle of all subgraph event listeners */
|
||||
#eventAbortController = new AbortController()
|
||||
private _eventAbortController = new AbortController()
|
||||
|
||||
constructor(
|
||||
/** The (sub)graph that contains this subgraph instance. */
|
||||
@@ -76,7 +76,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
|
||||
// Update this node when the subgraph input / output slots are changed
|
||||
const subgraphEvents = this.subgraph.events
|
||||
const { signal } = this.#eventAbortController
|
||||
const { signal } = this._eventAbortController
|
||||
|
||||
subgraphEvents.addEventListener(
|
||||
'input-added',
|
||||
@@ -89,12 +89,12 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const { inputNode, input } = subgraph.links[linkId].resolve(subgraph)
|
||||
const widget = inputNode?.widgets?.find?.((w) => w.name == name)
|
||||
if (widget)
|
||||
this.#setWidget(subgraphInput, existingInput, widget, input?.widget)
|
||||
this._setWidget(subgraphInput, existingInput, widget, input?.widget)
|
||||
return
|
||||
}
|
||||
const input = this.addInput(name, type)
|
||||
|
||||
this.#addSubgraphInputListeners(subgraphInput, input)
|
||||
this._addSubgraphInputListeners(subgraphInput, input)
|
||||
},
|
||||
{ signal }
|
||||
)
|
||||
@@ -179,7 +179,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
}
|
||||
}
|
||||
|
||||
#addSubgraphInputListeners(
|
||||
private _addSubgraphInputListeners(
|
||||
subgraphInput: SubgraphInput,
|
||||
input: INodeInputSlot & Partial<ISubgraphInput>
|
||||
) {
|
||||
@@ -201,7 +201,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
if (!widget) return
|
||||
|
||||
const widgetLocator = e.detail.input.widget
|
||||
this.#setWidget(subgraphInput, input, widget, widgetLocator)
|
||||
this._setWidget(subgraphInput, input, widget, widgetLocator)
|
||||
},
|
||||
{ signal }
|
||||
)
|
||||
@@ -288,7 +288,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
continue
|
||||
}
|
||||
|
||||
this.#addSubgraphInputListeners(subgraphInput, input)
|
||||
this._addSubgraphInputListeners(subgraphInput, input)
|
||||
|
||||
// Find the first widget that this slot is connected to
|
||||
for (const linkId of subgraphInput.linkIds) {
|
||||
@@ -318,13 +318,13 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const widget = inputNode.getWidgetFromSlot(targetInput)
|
||||
if (!widget) continue
|
||||
|
||||
this.#setWidget(subgraphInput, input, widget, targetInput.widget)
|
||||
this._setWidget(subgraphInput, input, widget, targetInput.widget)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#setWidget(
|
||||
private _setWidget(
|
||||
subgraphInput: Readonly<SubgraphInput>,
|
||||
input: INodeInputSlot,
|
||||
widget: Readonly<IBaseWidget>,
|
||||
@@ -553,7 +553,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
|
||||
override onRemoved(): void {
|
||||
// Clean up all subgraph event listeners
|
||||
this.#eventAbortController.abort()
|
||||
this._eventAbortController.abort()
|
||||
|
||||
// Clean up all promoted widgets
|
||||
for (const widget of this.widgets) {
|
||||
|
||||
@@ -57,10 +57,10 @@ export abstract class BaseWidget<
|
||||
maxWidth?: number
|
||||
}
|
||||
|
||||
#node: LGraphNode
|
||||
private _node: LGraphNode
|
||||
/** The node that this widget belongs to. */
|
||||
get node() {
|
||||
return this.#node
|
||||
return this._node
|
||||
}
|
||||
|
||||
linkedWidgets?: IBaseWidget[]
|
||||
@@ -97,20 +97,20 @@ export abstract class BaseWidget<
|
||||
canvas: LGraphCanvas
|
||||
): boolean
|
||||
|
||||
#value?: TWidget['value']
|
||||
private _value?: TWidget['value']
|
||||
get value(): TWidget['value'] {
|
||||
return this.#value
|
||||
return this._value
|
||||
}
|
||||
|
||||
set value(value: TWidget['value']) {
|
||||
this.#value = value
|
||||
this._value = value
|
||||
}
|
||||
|
||||
constructor(widget: TWidget & { node: LGraphNode })
|
||||
constructor(widget: TWidget, node: LGraphNode)
|
||||
constructor(widget: TWidget & { node: LGraphNode }, node?: LGraphNode) {
|
||||
// Private fields
|
||||
this.#node = node ?? widget.node
|
||||
this._node = node ?? widget.node
|
||||
|
||||
// The set and get functions for DOM widget values are hacked on to the options object;
|
||||
// attempting to set value before options will throw.
|
||||
|
||||
@@ -294,7 +294,7 @@ export class PromptExecutionError extends Error {
|
||||
}
|
||||
|
||||
export class ComfyApi extends EventTarget {
|
||||
#registered = new Set()
|
||||
private _registered = new Set()
|
||||
api_host: string
|
||||
api_base: string
|
||||
/**
|
||||
@@ -451,7 +451,7 @@ export class ComfyApi extends EventTarget {
|
||||
) {
|
||||
// Type assertion: strictFunctionTypes. So long as we emit events in a type-safe fashion, this is safe.
|
||||
super.addEventListener(type, callback as EventListener, options)
|
||||
this.#registered.add(type)
|
||||
this._registered.add(type)
|
||||
}
|
||||
|
||||
override removeEventListener<TEvent extends keyof ApiEvents>(
|
||||
@@ -492,7 +492,7 @@ export class ComfyApi extends EventTarget {
|
||||
/**
|
||||
* Poll status for colab and other things that don't support websockets.
|
||||
*/
|
||||
#pollQueue() {
|
||||
private _pollQueue() {
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const resp = await this.fetchApi('/prompt')
|
||||
@@ -568,7 +568,7 @@ export class ComfyApi extends EventTarget {
|
||||
this.socket.addEventListener('error', () => {
|
||||
if (this.socket) this.socket.close()
|
||||
if (!isReconnect && !opened) {
|
||||
this.#pollQueue()
|
||||
this._pollQueue()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -691,7 +691,7 @@ export class ComfyApi extends EventTarget {
|
||||
)
|
||||
break
|
||||
default:
|
||||
if (this.#registered.has(msg.type)) {
|
||||
if (this._registered.has(msg.type)) {
|
||||
// Fallback for custom types - calls super direct.
|
||||
super.dispatchEvent(
|
||||
new CustomEvent(msg.type, { detail: msg.data })
|
||||
@@ -956,7 +956,7 @@ export class ComfyApi extends EventTarget {
|
||||
* @param {*} type The endpoint to post to
|
||||
* @param {*} body Optional POST data
|
||||
*/
|
||||
async #postItem(type: string, body?: Record<string, unknown>) {
|
||||
private async _postItem(type: string, body?: Record<string, unknown>) {
|
||||
try {
|
||||
await this.fetchApi('/' + type, {
|
||||
method: 'POST',
|
||||
@@ -976,7 +976,7 @@ export class ComfyApi extends EventTarget {
|
||||
* @param {number} id The id of the item to delete
|
||||
*/
|
||||
async deleteItem(type: string, id: string) {
|
||||
await this.#postItem(type, { delete: [id] })
|
||||
await this._postItem(type, { delete: [id] })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -984,7 +984,7 @@ export class ComfyApi extends EventTarget {
|
||||
* @param {string} type The type of list to clear, queue or history
|
||||
*/
|
||||
async clearItems(type: string) {
|
||||
await this.#postItem(type, { clear: true })
|
||||
await this._postItem(type, { clear: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -993,7 +993,7 @@ export class ComfyApi extends EventTarget {
|
||||
* @param {string | null} [runningPromptId] Optional Running Prompt ID to interrupt
|
||||
*/
|
||||
async interrupt(runningPromptId: string | null) {
|
||||
await this.#postItem(
|
||||
await this._postItem(
|
||||
'interrupt',
|
||||
runningPromptId ? { prompt_id: runningPromptId } : undefined
|
||||
)
|
||||
|
||||
@@ -241,17 +241,17 @@ function dragElement(dragEl): () => void {
|
||||
}
|
||||
|
||||
class ComfyList {
|
||||
#type
|
||||
#text
|
||||
#reverse
|
||||
private _type
|
||||
private _text
|
||||
private _reverse
|
||||
element: HTMLDivElement
|
||||
button?: HTMLButtonElement
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
constructor(text, type?, reverse?) {
|
||||
this.#text = text
|
||||
this.#type = type || text.toLowerCase()
|
||||
this.#reverse = reverse || false
|
||||
this._text = text
|
||||
this._type = type || text.toLowerCase()
|
||||
this._reverse = reverse || false
|
||||
this.element = $el('div.comfy-list') as HTMLDivElement
|
||||
this.element.style.display = 'none'
|
||||
}
|
||||
@@ -261,7 +261,7 @@ class ComfyList {
|
||||
}
|
||||
|
||||
async load() {
|
||||
const items = await api.getItems(this.#type)
|
||||
const items = await api.getItems(this._type)
|
||||
this.element.replaceChildren(
|
||||
...Object.keys(items).flatMap((section) => [
|
||||
$el('h4', {
|
||||
@@ -269,12 +269,12 @@ class ComfyList {
|
||||
}),
|
||||
$el('div.comfy-list-items', [
|
||||
// @ts-expect-error fixme ts strict error
|
||||
...(this.#reverse ? items[section].reverse() : items[section]).map(
|
||||
...(this._reverse ? items[section].reverse() : items[section]).map(
|
||||
(item: LegacyQueueItem) => {
|
||||
// Allow items to specify a custom remove action (e.g. for interrupt current prompt)
|
||||
const removeAction = item.remove ?? {
|
||||
name: 'Delete',
|
||||
cb: () => api.deleteItem(this.#type, item.prompt[1])
|
||||
cb: () => api.deleteItem(this._type, item.prompt[1])
|
||||
}
|
||||
return $el('div', { textContent: item.prompt[0] + ': ' }, [
|
||||
$el('button', {
|
||||
@@ -311,9 +311,9 @@ class ComfyList {
|
||||
]),
|
||||
$el('div.comfy-list-actions', [
|
||||
$el('button', {
|
||||
textContent: 'Clear ' + this.#text,
|
||||
textContent: 'Clear ' + this._text,
|
||||
onclick: async () => {
|
||||
await api.clearItems(this.#type)
|
||||
await api.clearItems(this._type)
|
||||
await this.load()
|
||||
}
|
||||
}),
|
||||
@@ -339,7 +339,7 @@ class ComfyList {
|
||||
hide() {
|
||||
this.element.style.display = 'none'
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.button.textContent = 'View ' + this.#text
|
||||
this.button.textContent = 'View ' + this._text
|
||||
}
|
||||
|
||||
toggle() {
|
||||
|
||||
@@ -6,7 +6,7 @@ type DialogAction<T> = string | { value?: T; text: string }
|
||||
export class ComfyAsyncDialog<
|
||||
T = string | null
|
||||
> extends ComfyDialog<HTMLDialogElement> {
|
||||
#resolve: (value: T | null) => void = () => {}
|
||||
private _resolve: (value: T | null) => void = () => {}
|
||||
|
||||
constructor(actions?: Array<DialogAction<T>>) {
|
||||
super(
|
||||
@@ -30,7 +30,7 @@ export class ComfyAsyncDialog<
|
||||
super.show(html)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.#resolve = resolve
|
||||
this._resolve = resolve
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@ export class ComfyAsyncDialog<
|
||||
this.element.showModal()
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.#resolve = resolve
|
||||
this._resolve = resolve
|
||||
})
|
||||
}
|
||||
|
||||
override close(result: T | null = null) {
|
||||
this.#resolve(result)
|
||||
this._resolve(result)
|
||||
this.element.close()
|
||||
super.close()
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ type ComfyButtonProps = {
|
||||
}
|
||||
|
||||
export class ComfyButton implements ComfyComponent<HTMLElement> {
|
||||
#over = 0
|
||||
#popupOpen = false
|
||||
private _over = 0
|
||||
private _popupOpen = false
|
||||
isOver = false
|
||||
iconElement = $el('i.mdi')
|
||||
contentElement = $el('span')
|
||||
@@ -123,7 +123,7 @@ export class ComfyButton implements ComfyComponent<HTMLElement> {
|
||||
this.element.addEventListener('click', (e) => {
|
||||
if (this.popup) {
|
||||
// we are either a touch device or triggered by click not hover
|
||||
if (!this.#over) {
|
||||
if (!this._over) {
|
||||
this.popup.toggle()
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ export class ComfyButton implements ComfyComponent<HTMLElement> {
|
||||
internalClasses.push('disabled')
|
||||
}
|
||||
if (this.popup) {
|
||||
if (this.#popupOpen) {
|
||||
if (this._popupOpen) {
|
||||
internalClasses.push('popup-open')
|
||||
} else {
|
||||
internalClasses.push('popup-closed')
|
||||
@@ -172,16 +172,16 @@ export class ComfyButton implements ComfyComponent<HTMLElement> {
|
||||
if (mode === 'hover') {
|
||||
for (const el of [this.element, this.popup.element]) {
|
||||
el.addEventListener('mouseenter', () => {
|
||||
this.popup.open = !!++this.#over
|
||||
this.popup.open = !!++this._over
|
||||
})
|
||||
el.addEventListener('mouseleave', () => {
|
||||
this.popup.open = !!--this.#over
|
||||
this.popup.open = !!--this._over
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
popup.addEventListener('change', () => {
|
||||
this.#popupOpen = popup.open
|
||||
this._popupOpen = popup.open
|
||||
this.updateClasses()
|
||||
})
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ export class ComfyPopup extends EventTarget {
|
||||
this.open = prop(this, 'open', false, (v, o) => {
|
||||
if (v === o) return
|
||||
if (v) {
|
||||
this.#show()
|
||||
this._show()
|
||||
} else {
|
||||
this.#hide()
|
||||
this._hide()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -65,24 +65,24 @@ export class ComfyPopup extends EventTarget {
|
||||
this.open = !this.open
|
||||
}
|
||||
|
||||
#hide() {
|
||||
private _hide() {
|
||||
this.element.classList.remove('open')
|
||||
window.removeEventListener('resize', this.update)
|
||||
window.removeEventListener('click', this.#clickHandler, { capture: true })
|
||||
window.removeEventListener('keydown', this.#escHandler, { capture: true })
|
||||
window.removeEventListener('click', this._clickHandler, { capture: true })
|
||||
window.removeEventListener('keydown', this._escHandler, { capture: true })
|
||||
|
||||
this.dispatchEvent(new CustomEvent('close'))
|
||||
this.dispatchEvent(new CustomEvent('change'))
|
||||
}
|
||||
|
||||
#show() {
|
||||
private _show() {
|
||||
this.element.classList.add('open')
|
||||
this.update()
|
||||
|
||||
window.addEventListener('resize', this.update)
|
||||
window.addEventListener('click', this.#clickHandler, { capture: true })
|
||||
window.addEventListener('click', this._clickHandler, { capture: true })
|
||||
if (this.closeOnEscape) {
|
||||
window.addEventListener('keydown', this.#escHandler, { capture: true })
|
||||
window.addEventListener('keydown', this._escHandler, { capture: true })
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('open'))
|
||||
@@ -90,7 +90,7 @@ export class ComfyPopup extends EventTarget {
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
#escHandler = (e) => {
|
||||
private _escHandler = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
this.open = false
|
||||
e.preventDefault()
|
||||
@@ -99,7 +99,7 @@ export class ComfyPopup extends EventTarget {
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
#clickHandler = (e) => {
|
||||
private _clickHandler = (e) => {
|
||||
/** @type {any} */
|
||||
const target = e.target
|
||||
if (
|
||||
|
||||
@@ -5,11 +5,11 @@ export class ComfyDialog<
|
||||
> extends EventTarget {
|
||||
element: T
|
||||
textElement!: HTMLElement
|
||||
#buttons: HTMLButtonElement[] | null
|
||||
private _buttons: HTMLButtonElement[] | null
|
||||
|
||||
constructor(type = 'div', buttons: HTMLButtonElement[] | null = null) {
|
||||
super()
|
||||
this.#buttons = buttons
|
||||
this._buttons = buttons
|
||||
this.element = $el(type + '.comfy-modal', { parent: document.body }, [
|
||||
$el('div.comfy-modal-content', [
|
||||
$el('p', { $: (p) => (this.textElement = p) }),
|
||||
@@ -20,7 +20,7 @@ export class ComfyDialog<
|
||||
|
||||
createButtons() {
|
||||
return (
|
||||
this.#buttons ?? [
|
||||
this._buttons ?? [
|
||||
$el('button', {
|
||||
type: 'button',
|
||||
textContent: 'Close',
|
||||
|
||||
@@ -54,7 +54,7 @@ class ConversionContext {
|
||||
/** Reroutes that has at least a valid link pass through it */
|
||||
validReroutes: Set<Reroute>
|
||||
|
||||
#rerouteIdCounter = 0
|
||||
private _rerouteIdCounter = 0
|
||||
|
||||
constructor(public workflow: WorkflowJSON04) {
|
||||
this.nodeById = _.keyBy(workflow.nodes.map(_.cloneDeep), 'id')
|
||||
@@ -76,7 +76,7 @@ class ConversionContext {
|
||||
pos: getNodeCenter(node),
|
||||
linkIds: []
|
||||
}))
|
||||
this.#rerouteIdCounter = reroutes.length + 1
|
||||
this._rerouteIdCounter = reroutes.length + 1
|
||||
|
||||
this.rerouteByNodeId = _.keyBy(reroutes, 'nodeId')
|
||||
this.rerouteById = _.keyBy(reroutes, 'id')
|
||||
@@ -88,7 +88,7 @@ class ConversionContext {
|
||||
/**
|
||||
* Gets the chain of reroute nodes leading to the given node
|
||||
*/
|
||||
#getRerouteChain(node: RerouteNode): RerouteNode[] {
|
||||
private _getRerouteChain(node: RerouteNode): RerouteNode[] {
|
||||
const nodes: RerouteNode[] = []
|
||||
let currentNode: RerouteNode = node
|
||||
while (currentNode?.type === 'Reroute') {
|
||||
@@ -106,7 +106,7 @@ class ConversionContext {
|
||||
return nodes
|
||||
}
|
||||
|
||||
#connectRerouteChain(rerouteNodes: RerouteNode[]): Reroute[] {
|
||||
private _connectRerouteChain(rerouteNodes: RerouteNode[]): Reroute[] {
|
||||
const reroutes = rerouteNodes.map((node) => this.rerouteByNodeId[node.id])
|
||||
for (const reroute of reroutes) {
|
||||
this.validReroutes.add(reroute)
|
||||
@@ -121,7 +121,7 @@ class ConversionContext {
|
||||
return reroutes
|
||||
}
|
||||
|
||||
#createNewLink(
|
||||
private _createNewLink(
|
||||
startingLink: ComfyLinkObject,
|
||||
endingLink: ComfyLinkObject,
|
||||
rerouteNodes: RerouteNode[]
|
||||
@@ -136,7 +136,7 @@ class ConversionContext {
|
||||
parentId: reroute.id
|
||||
})
|
||||
|
||||
const reroutes = this.#connectRerouteChain(rerouteNodes)
|
||||
const reroutes = this._connectRerouteChain(rerouteNodes)
|
||||
for (const reroute of reroutes) {
|
||||
reroute.linkIds ??= []
|
||||
reroute.linkIds.push(endingLink.id)
|
||||
@@ -153,11 +153,11 @@ class ConversionContext {
|
||||
}
|
||||
}
|
||||
|
||||
#createNewInputFloatingLink(
|
||||
private _createNewInputFloatingLink(
|
||||
endingLink: ComfyLinkObject,
|
||||
rerouteNodes: RerouteNode[]
|
||||
): ComfyLinkObject {
|
||||
const reroutes = this.#connectRerouteChain(rerouteNodes)
|
||||
const reroutes = this._connectRerouteChain(rerouteNodes)
|
||||
for (const reroute of reroutes) {
|
||||
if (!reroute.linkIds?.length) {
|
||||
reroute.floating = {
|
||||
@@ -166,7 +166,7 @@ class ConversionContext {
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: this.#rerouteIdCounter++,
|
||||
id: this._rerouteIdCounter++,
|
||||
origin_id: -1,
|
||||
origin_slot: -1,
|
||||
target_id: endingLink.target_id,
|
||||
@@ -176,11 +176,11 @@ class ConversionContext {
|
||||
}
|
||||
}
|
||||
|
||||
#createNewOutputFloatingLink(
|
||||
private _createNewOutputFloatingLink(
|
||||
startingLink: ComfyLinkObject,
|
||||
rerouteNodes: RerouteNode[]
|
||||
): ComfyLinkObject {
|
||||
const reroutes = this.#connectRerouteChain(rerouteNodes)
|
||||
const reroutes = this._connectRerouteChain(rerouteNodes)
|
||||
for (const reroute of reroutes) {
|
||||
if (!reroute.linkIds?.length) {
|
||||
reroute.floating = {
|
||||
@@ -190,7 +190,7 @@ class ConversionContext {
|
||||
}
|
||||
|
||||
return {
|
||||
id: this.#rerouteIdCounter++,
|
||||
id: this._rerouteIdCounter++,
|
||||
origin_id: startingLink.origin_id,
|
||||
origin_slot: startingLink.origin_slot,
|
||||
target_id: -1,
|
||||
@@ -200,7 +200,7 @@ class ConversionContext {
|
||||
}
|
||||
}
|
||||
|
||||
#reconnectLinks(nodes: ComfyNode[], links: ComfyLinkObject[]): void {
|
||||
private _reconnectLinks(nodes: ComfyNode[], links: ComfyLinkObject[]): void {
|
||||
// Remove all existing links on sockets
|
||||
for (const node of nodes) {
|
||||
for (const input of node.inputs ?? []) {
|
||||
@@ -245,18 +245,18 @@ class ConversionContext {
|
||||
const endingRerouteNode = this.nodeById[
|
||||
endingLink.origin_id
|
||||
] as RerouteNode
|
||||
const rerouteNodes = this.#getRerouteChain(endingRerouteNode)
|
||||
const rerouteNodes = this._getRerouteChain(endingRerouteNode)
|
||||
const startingLink =
|
||||
this.linkById[
|
||||
rerouteNodes[rerouteNodes.length - 1]?.inputs?.[0]?.link ?? -1
|
||||
]
|
||||
if (startingLink) {
|
||||
// Valid link found, create a new link
|
||||
links.push(this.#createNewLink(startingLink, endingLink, rerouteNodes))
|
||||
links.push(this._createNewLink(startingLink, endingLink, rerouteNodes))
|
||||
} else {
|
||||
// Floating link found, create a new floating link
|
||||
floatingLinks.push(
|
||||
this.#createNewInputFloatingLink(endingLink, rerouteNodes)
|
||||
this._createNewInputFloatingLink(endingLink, rerouteNodes)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -270,14 +270,14 @@ class ConversionContext {
|
||||
})
|
||||
|
||||
for (const rerouteNode of floatingEndingRerouteNodes) {
|
||||
const rerouteNodes = this.#getRerouteChain(rerouteNode)
|
||||
const rerouteNodes = this._getRerouteChain(rerouteNode)
|
||||
const startingLink =
|
||||
this.linkById[
|
||||
rerouteNodes[rerouteNodes.length - 1]?.inputs?.[0]?.link ?? -1
|
||||
]
|
||||
if (startingLink) {
|
||||
floatingLinks.push(
|
||||
this.#createNewOutputFloatingLink(startingLink, rerouteNodes)
|
||||
this._createNewOutputFloatingLink(startingLink, rerouteNodes)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -285,7 +285,7 @@ class ConversionContext {
|
||||
const nodes = Object.values(this.nodeById).filter(
|
||||
(node) => node.type !== 'Reroute'
|
||||
)
|
||||
this.#reconnectLinks(nodes, links)
|
||||
this._reconnectLinks(nodes, links)
|
||||
|
||||
return {
|
||||
...this.workflow,
|
||||
|
||||
Reference in New Issue
Block a user