Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/lib/litegraph/src/LGraphCanvas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { toString } from 'es-toolkit/compat'

import { PREFIX, SEPARATOR } from '@/constants/groupNodeConstants'
import { MovingInputLink } from '@/lib/litegraph/src/canvas/MovingInputLink'
import { LitegraphLinkAdapter } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
import type { LinkRenderContext } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
import { getSlotPosition } from '@/renderer/core/canvas/litegraph/slotCalculations'
Expand Down Expand Up @@ -4769,6 +4770,8 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
}
)
}
if (renderLink instanceof MovingInputLink)
renderLink.drawConnectionCircle(ctx, this.graph_mouse)

ctx.fillStyle = colour
ctx.beginPath()
Expand Down
7 changes: 7 additions & 0 deletions src/lib/litegraph/src/canvas/LinkConnector.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { remove } from 'es-toolkit'

import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import { LLink } from '@/lib/litegraph/src/LLink'
import type { Reroute } from '@/lib/litegraph/src/Reroute'
Expand Down Expand Up @@ -860,6 +862,11 @@ export class LinkConnector {
}

dropOnNothing(event: CanvasPointerEvent): void {
remove(
this.renderLinks,
(link) => link instanceof MovingInputLink && link.disconnectOnDrop
).forEach((link) => (link as MovingLinkBase).disconnect())
if (this.renderLinks.length === 0) return
// For external event only.
const mayContinue = this.events.dispatch('dropped-on-canvas', event)
if (mayContinue === false) return
Expand Down
20 changes: 20 additions & 0 deletions src/lib/litegraph/src/canvas/MovingInputLink.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import type { LLink } from '@/lib/litegraph/src/LLink'
import type { Reroute } from '@/lib/litegraph/src/Reroute'
import type { CustomEventTarget } from '@/lib/litegraph/src/infrastructure/CustomEventTarget'
Expand All @@ -24,6 +25,7 @@ export class MovingInputLink extends MovingLinkBase {
readonly fromPos: Point
readonly fromDirection: LinkDirection
readonly fromSlotIndex: number
disconnectOnDrop: boolean

constructor(
network: LinkNetwork,
Expand All @@ -38,6 +40,7 @@ export class MovingInputLink extends MovingLinkBase {
this.fromPos = fromReroute?.pos ?? this.outputPos
this.fromDirection = LinkDirection.NONE
this.fromSlotIndex = this.outputIndex
this.disconnectOnDrop = true
}

canConnectToInput(
Expand Down Expand Up @@ -130,4 +133,21 @@ export class MovingInputLink extends MovingLinkBase {
disconnect(): boolean {
return this.inputNode.disconnectInput(this.inputIndex, true)
}

drawConnectionCircle(ctx: CanvasRenderingContext2D, to: Point) {
if (!this.disconnectOnDrop) return
const radius = 35
ctx.save()
ctx.strokeStyle = LiteGraph.WIDGET_OUTLINE_COLOR
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(this.inputPos[0] + radius, this.inputPos[1])
ctx.arc(...this.inputPos, radius, 0, Math.PI * 2)
ctx.stroke()
ctx.restore()

const [fromX, fromY] = this.inputPos
const distSquared = (fromX - to[0]) ** 2 + (fromY - to[1]) ** 2
this.disconnectOnDrop = distSquared < radius ** 2
}
}
12 changes: 11 additions & 1 deletion src/renderer/core/canvas/links/linkConnectorAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { SlotLayout } from '@/renderer/core/layout/types'
import type { LGraph } from '@/lib/litegraph/src/LGraph'
import type { NodeId } from '@/lib/litegraph/src/LGraphNode'
import type { RerouteId } from '@/lib/litegraph/src/Reroute'
Expand Down Expand Up @@ -72,11 +73,20 @@ export class LinkConnectorAdapter {
beginFromInput(
nodeId: NodeId,
inputIndex: number,
opts?: { moveExisting?: boolean; fromRerouteId?: RerouteId }
opts?: {
moveExisting?: boolean
fromRerouteId?: RerouteId
layout: SlotLayout
}
Comment on lines 77 to 81
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Type inconsistency: layout should be optional.

The type declares layout: SlotLayout (required when opts is present), but line 85 checks opts?.layout, treating it as optional. This mismatch weakens type safety.

Apply this diff to correct the type:

   opts?: {
     moveExisting?: boolean
     fromRerouteId?: RerouteId
-    layout: SlotLayout
+    layout?: SlotLayout
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
opts?: {
moveExisting?: boolean
fromRerouteId?: RerouteId
layout: SlotLayout
}
opts?: {
moveExisting?: boolean
fromRerouteId?: RerouteId
layout?: SlotLayout
}
🤖 Prompt for AI Agents
In src/renderer/core/canvas/links/linkConnectorAdapter.ts around lines 76 to 80,
the opts type currently declares layout as required (layout: SlotLayout) but the
code treats opts?.layout as optional; change the opts type so layout is optional
(layout?: SlotLayout) to match usage and restore type safety. Update the type
definition only (making layout optional) and run TypeScript checks to ensure no
other call-sites relied on layout being mandatory; if any call-sites assumed
layout exists, add safe guards (e.g., optional chaining or defaults) where
necessary.

Comment on lines 77 to 81
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Required before Optional

Suggested change
opts?: {
moveExisting?: boolean
fromRerouteId?: RerouteId
layout: SlotLayout
}
opts?: {
layout: SlotLayout
moveExisting?: boolean
fromRerouteId?: RerouteId
}

): void {
const node = this.network.getNodeById(nodeId)
const input = node?.inputs?.[inputIndex]
if (!node || !input) return
if (opts?.layout)
input.pos = [
opts.layout.position.x - node.pos[0],
opts.layout.position.y - node.pos[1]
]

const fromReroute = this.network.getReroute(opts?.fromRerouteId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,8 @@ export function useSlotLinkInteraction({
})
} else {
activeAdapter.beginFromInput(localNodeId, index, {
moveExisting: shouldMoveExistingInput
moveExisting: shouldMoveExistingInput,
layout
})
}

Expand Down
Loading