Compare commits

...

1 Commits

Author SHA1 Message Date
Connor Byrne
7f25359efa refactor(litegraph): delete 26 unused LGraphNode event hooks
Delete declarations and matching dispatch sites for: onOutputRemoved,
onInputRemoved, onBounding, onInputAdded, onOutputAdded, onExecute,
onBeforeConnectInput, onShowCustomPanelInfo, onAddPropertyToPanel,
onKeyUp, onDrawCollapsed, onDropItem, onDropData, onOutputClick,
onOutputDblClick, onGetPropertyInfo, onNodeOutputAdd, onNodeInputAdd,
onMenuNodeInputs, onMenuNodeOutputs, onMouseUp, onNodeTitleDblClick,
onDrawTitle, onDrawTitleText, onDrawTitleBar, onPropertyChange.

AUDIT-LG.9 confirmed zero internal AND zero external callers for each.
Per-hook verification rerun before deletion. The 27th hook in the
original audit (onTitleButtonClick) is excluded — verification found
it is a real overridden method on SubgraphNode covered by tests.

Transitive dead code folded in:
- LGraphNode.doExecute() body collapsed (only onAfterExecuteNode
  bookkeeping remains).
- LGraph._nodes_executable field + populating loop in
  updateExecutionOrder() removed (only consumer was the deleted
  onExecute dispatch in runStep).
- LGraph.computeExecutionOrder() lost its dead only_onExecute filter
  parameter.
- LGraph.runStep() lost its dead limit param and the unused per-node
  inner loops.
- onPropertyChange typo guard in LiteGraphGlobal kept (still useful
  for user nodes) with cast since the typed property is gone.

Closes part of #12224.
2026-05-13 16:32:25 -07:00
4 changed files with 17 additions and 259 deletions

View File

@@ -247,7 +247,6 @@ export class LGraph
_nodes: (LGraphNode | SubgraphNode)[] = []
_nodes_by_id: Record<NodeId, LGraphNode> = {}
_nodes_in_order: LGraphNode[] = []
_nodes_executable: LGraphNode[] | null = null
_groups: LGraphGroup[] = []
iteration: number = 0
globaltime: number = 0
@@ -410,8 +409,6 @@ export class LGraph
this._nodes_by_id = {}
// nodes sorted in execution order
this._nodes_in_order = []
// nodes that contain onExecute sorted in execution order
this._nodes_executable = null
this._links.clear()
this.reroutes.clear()
@@ -568,31 +565,16 @@ export class LGraph
* Run N steps (cycles) of the graph
* @param num number of steps to run, default is 1
* @param do_not_catch_errors [optional] if you want to try/catch errors
* @param limit max number of nodes to execute (used to execute from start to a node)
*/
runStep(num: number, do_not_catch_errors: boolean, limit?: number): void {
runStep(num: number, do_not_catch_errors: boolean): void {
num = num || 1
const start = LiteGraph.getTime()
this.globaltime = 0.001 * (start - this.starttime)
const nodes = this._nodes_executable || this._nodes
if (!nodes) return
limit = limit || nodes.length
if (do_not_catch_errors) {
// iterations
for (let i = 0; i < num; i++) {
for (let j = 0; j < limit; ++j) {
const node = nodes[j]
// FIXME: Looks like copy/paste broken logic - checks for "on", executes "do"
if (node.mode == LGraphEventMode.ALWAYS && node.onExecute) {
// wrap node.onExecute();
node.doExecute?.()
}
}
this.fixedtime += this.fixedtime_lapse
this.onExecuteStep?.()
}
@@ -602,13 +584,6 @@ export class LGraph
try {
// iterations
for (let i = 0; i < num; i++) {
for (let j = 0; j < limit; ++j) {
const node = nodes[j]
if (node.mode == LGraphEventMode.ALWAYS) {
node.onExecute?.()
}
}
this.fixedtime += this.fixedtime_lapse
this.onExecuteStep?.()
}
@@ -643,20 +618,11 @@ export class LGraph
* nodes with only inputs.
*/
updateExecutionOrder(): void {
this._nodes_in_order = this.computeExecutionOrder(false)
this._nodes_executable = []
for (const node of this._nodes_in_order) {
if (node.onExecute) {
this._nodes_executable.push(node)
}
}
this._nodes_in_order = this.computeExecutionOrder()
}
// This is more internal, it computes the executable nodes in order and returns it
computeExecutionOrder(
only_onExecute: boolean,
set_level?: boolean
): LGraphNode[] {
computeExecutionOrder(set_level?: boolean): LGraphNode[] {
const L: LGraphNode[] = []
const S: LGraphNode[] = []
const M: Dictionary<LGraphNode> = {}
@@ -666,10 +632,6 @@ export class LGraph
// search for the nodes without inputs (starting nodes)
for (const node of this._nodes) {
if (only_onExecute && !node.onExecute) {
continue
}
// add to pending nodes
M[node.id] = node
@@ -791,7 +753,7 @@ export class LGraph
arrange(margin?: number, layout?: string): void {
margin = margin || 100
const nodes = this.computeExecutionOrder(false, true)
const nodes = this.computeExecutionOrder(true)
const columns: LGraphNode[][] = []
for (const node of nodes) {
const col = node._level || 1

View File

@@ -1281,7 +1281,7 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
const canvas = LGraphCanvas.active_canvas
let entries: (IContextMenuValue<INodeSlotContextItem> | null)[] = []
const entries: (IContextMenuValue<INodeSlotContextItem> | null)[] = []
if (
LiteGraph.do_add_triggers_slots &&
@@ -1293,10 +1293,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
className: 'event'
})
}
// add callback for modifying the menu elements onMenuNodeOutputs
const retEntries = node.onMenuNodeOutputs?.(entries)
if (retEntries) entries = retEntries
if (!entries.length) return
new LiteGraph.ContextMenu<INodeSlotContextItem>(entries, {
@@ -1344,8 +1340,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
graph.beforeChange()
node.addOutput(v.value[0], v.value[1], v.value[2])
// a callback to the node when adding a slot
node.onNodeOutputAdd?.(v.value)
canvas.setDirty(true, true)
graph.afterChange()
}
@@ -2810,10 +2804,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
}
}
// TODO: Move callbacks to the start of this closure (onInputClick is already correct).
pointer.onDoubleClick = () => node.onOutputDblClick?.(i, e)
pointer.onClick = () => node.onOutputClick?.(i, e)
return
}
}
@@ -2872,12 +2862,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
// Node background
pointer.onDoubleClick = () => {
// Double-click
// Check if it's a double click on the title bar
// Note: pos[1] is the y-coordinate of the node's body
// If clicking on node header (title), pos[1] is negative
if (pos[1] < 0 && !inCollapse) {
node.onNodeTitleDblClick?.(e, pos, this)
} else if (node instanceof SubgraphNode) {
// Note: pos[1] is the y-coordinate of the node's body.
// If clicking on node header (title), pos[1] is negative — skip the
// subgraph-open behaviour in that case.
if (!(pos[1] < 0 && !inCollapse) && node instanceof SubgraphNode) {
this.openSubgraph(node.subgraph, node)
}
@@ -3841,25 +3829,8 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
this.isDragging = false
const x = e.canvasX
const y = e.canvasY
if (!this.linkConnector.isConnecting) {
this.dirty_canvas = true
this.node_over?.onMouseUp?.(
e,
[x - this.node_over.pos[0], y - this.node_over.pos[1]],
this
)
this.node_capturing_input?.onMouseUp?.(
e,
[
x - this.node_capturing_input.pos[0],
y - this.node_capturing_input.pos[1]
],
this
)
}
} else if (e.button === 1) {
// middle button
@@ -3994,10 +3965,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
(this._previously_dragging_canvas ?? false) && this.pointer.isDown
this._previously_dragging_canvas = null
}
for (const node of Object.values(this.selected_nodes)) {
node.onKeyUp?.(e)
}
}
// TODO: Do we need to remeasure and recalculate everything on every key down/up?
@@ -5627,10 +5594,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
ctx.shadowColor = 'transparent'
}
// custom draw collapsed method (draw after shadows because they are affected)
if (node.flags.collapsed && node.onDrawCollapsed?.(ctx, this) == true)
return
// clip if required (mask)
const shape = node._shape || RenderShape.BOX
const size = temp_vec2
@@ -5886,9 +5849,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
default_title_color: this.node_title_color,
low_quality
})
// custom title render
node.onDrawTitle?.(ctx)
}
// Draw stroke styles
@@ -8363,16 +8323,11 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
const value = node.properties[pName]
const info = node.getPropertyInfo(pName)
// in case the user wants control over the side panel widget
if (node.onAddPropertyToPanel?.(pName, panel)) continue
panel.addWidget(info.widget || info.type, pName, value, info, fUpdate)
}
panel.addSeparator()
node.onShowCustomPanelInfo?.(panel)
// clear
panel.footer.innerHTML = ''
panel

View File

@@ -43,10 +43,8 @@ import type {
INodeInputSlot,
INodeOutputSlot,
INodeSlot,
INodeSlotContextItem,
IPinnable,
ISlotType,
Panel,
Point,
Positionable,
ReadOnlyRect,
@@ -194,23 +192,17 @@ supported callbacks:
+ onDrawBackground: render the background area inside the node (only in edit mode)
+ onMouseDown
+ onMouseMove
+ onMouseUp
+ onMouseEnter
+ onMouseLeave
+ onExecute: execute the node
+ onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour)
+ onGetInputs: returns an array of possible inputs
+ onGetOutputs: returns an array of possible outputs
+ onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h])
+ onDblClick: double clicked in the node
+ onNodeTitleDblClick: double clicked in the node title
+ onInputDblClick: input slot double clicked (can be used to automatically create a node connected)
+ onOutputDblClick: output slot double clicked (can be used to automatically create a node connected)
+ onConfigure: called after the node has been configured
+ onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data)
+ onSelected
+ onDeselected
+ onDropItem : DOM item dropped over the node
+ onDropFile : file dropped over the node
+ onConnectInput : if returns false the incoming connection will be canceled
+ onConnectionsChange : a connection changed (new one or removed) (NodeSlotType.INPUT or NodeSlotType.OUTPUT, slot, true if connected, link_info, input_info )
@@ -416,18 +408,11 @@ export class LGraphNode
badges: (LGraphBadge | (() => LGraphBadge))[] = []
title_buttons: LGraphButton[] = []
badgePosition: BadgePosition = BadgePosition.TopLeft
onOutputRemoved?(this: LGraphNode, slot: number): void
onInputRemoved?(this: LGraphNode, slot: number, input: INodeInputSlot): void
/**
* The width of the node when collapsed.
* Updated by {@link LGraphCanvas.drawNode}
*/
_collapsed_width?: number
/**
* Called once at the start of every frame. Caller may change the values in {@link out}, which will be reflected in {@link boundingRect}.
* WARNING: Making changes to boundingRect via onBounding is poorly supported, and will likely result in strange behaviour.
*/
onBounding?(this: LGraphNode, out: Rect): void
console?: string[]
_level?: number
_shape?: RenderShape
@@ -466,7 +451,7 @@ export class LGraphNode
/** @inheritdoc {@link boundingRect} */
private _boundingRect: Rectangle = new Rectangle()
/**
* Cached node position & area as `x, y, width, height`. Includes changes made by {@link onBounding}, if present.
* Cached node position & area as `x, y, width, height`.
*
* Determines the node hitbox and other rendering effects. Calculated once at the start of every frame.
*/
@@ -622,15 +607,8 @@ export class LGraphNode
link_info: LLink | null | undefined,
inputOrOutput: INodeInputSlot | INodeOutputSlot | SubgraphIO
): void
onInputAdded?(this: LGraphNode, input: INodeInputSlot): void
onOutputAdded?(this: LGraphNode, output: INodeOutputSlot): void
onConfigure?(this: LGraphNode, serialisedNode: ISerialisedNode): void
onSerialize?(this: LGraphNode, serialised: ISerialisedNode): void
onExecute?(
this: LGraphNode,
param?: unknown,
options?: { action_call?: string }
): void
onAction?(
this: LGraphNode,
action: string,
@@ -639,21 +617,6 @@ export class LGraphNode
): void
onDrawBackground?(this: LGraphNode, ctx: CanvasRenderingContext2D): void
onNodeCreated?(this: LGraphNode): void
/**
* Callback invoked by {@link connect} to override the target slot index.
* Its return value overrides the target index selection.
* @param target_slot The current input slot index
* @param requested_slot The originally requested slot index - could be negative, or if using (deprecated) name search, a string
* @returns {number | null} If a number is returned, the connection will be made to that input index.
* If an invalid index or non-number (false, null, NaN etc) is returned, the connection will be cancelled.
*/
onBeforeConnectInput?(
this: LGraphNode,
target_slot: number,
requested_slot?: number | string
): number | false | null
onShowCustomPanelInfo?(this: LGraphNode, panel: Panel): void
onAddPropertyToPanel?(this: LGraphNode, pName: string, panel: Panel): boolean
onWidgetChanged?(
this: LGraphNode,
name: string,
@@ -662,7 +625,6 @@ export class LGraphNode
w: IBaseWidget
): void
onDeselected?(this: LGraphNode): void
onKeyUp?(this: LGraphNode, e: KeyboardEvent): void
onKeyDown?(this: LGraphNode, e: KeyboardEvent): void
onSelected?(this: LGraphNode): void
getExtraMenuOptions?(
@@ -672,11 +634,6 @@ export class LGraphNode
): (IContextMenuValue<unknown> | null)[]
getMenuOptions?(this: LGraphNode, canvas: LGraphCanvas): IContextMenuValue[]
onAdded?(this: LGraphNode, graph: LGraph): void
onDrawCollapsed?(
this: LGraphNode,
ctx: CanvasRenderingContext2D,
cavnas: LGraphCanvas
): boolean
onDrawForeground?(
this: LGraphNode,
ctx: CanvasRenderingContext2D,
@@ -697,39 +654,9 @@ export class LGraphNode
): IContextMenuValue[]
// FIXME: Re-typing
onDropItem?(this: LGraphNode, event: Event): boolean
onDropData?(
this: LGraphNode,
data: string | ArrayBuffer,
filename: string,
file: File
): void
onDropFile?(this: LGraphNode, file: File): void
onInputClick?(this: LGraphNode, index: number, e: CanvasPointerEvent): void
onInputDblClick?(this: LGraphNode, index: number, e: CanvasPointerEvent): void
onOutputClick?(this: LGraphNode, index: number, e: CanvasPointerEvent): void
onOutputDblClick?(
this: LGraphNode,
index: number,
e: CanvasPointerEvent
): void
onGetPropertyInfo?(this: LGraphNode, property: string): INodePropertyInfo
onNodeOutputAdd?(this: LGraphNode, value: unknown): void
onNodeInputAdd?(this: LGraphNode, value: unknown): void
onMenuNodeInputs?(
this: LGraphNode,
entries: (IContextMenuValue<INodeSlotContextItem> | null)[]
): (IContextMenuValue<INodeSlotContextItem> | null)[]
onMenuNodeOutputs?(
this: LGraphNode,
entries: (IContextMenuValue<INodeSlotContextItem> | null)[]
): (IContextMenuValue<INodeSlotContextItem> | null)[]
onMouseUp?(
this: LGraphNode,
e: CanvasPointerEvent,
pos: Point,
canvas: LGraphCanvas
): void
onMouseEnter?(this: LGraphNode, e: CanvasPointerEvent): void
/** Blocks drag if return value is truthy. @param pos Offset from {@link LGraphNode.pos}. */
onMouseDown?(
@@ -745,23 +672,6 @@ export class LGraphNode
pos: Point,
canvas: LGraphCanvas
): void
/** @param pos Offset from {@link LGraphNode.pos}. */
onNodeTitleDblClick?(
this: LGraphNode,
e: CanvasPointerEvent,
pos: Point,
canvas: LGraphCanvas
): void
onDrawTitle?(this: LGraphNode, ctx: CanvasRenderingContext2D): void
onDrawTitleText?(
this: LGraphNode,
ctx: CanvasRenderingContext2D,
title_height: number,
size: Size,
scale: number,
title_text_font: string,
selected?: boolean
): void
onDrawTitleBox?(
this: LGraphNode,
ctx: CanvasRenderingContext2D,
@@ -769,14 +679,6 @@ export class LGraphNode
size: Size,
scale: number
): void
onDrawTitleBar?(
this: LGraphNode,
ctx: CanvasRenderingContext2D,
title_height: number,
size: Size,
scale: number,
fgcolor: string
): void
onRemoved?(this: LGraphNode): void
onMouseMove?(
this: LGraphNode,
@@ -784,7 +686,6 @@ export class LGraphNode
pos: Point,
arg2: LGraphCanvas
): void
onPropertyChange?(this: LGraphNode): void
updateOutputData?(this: LGraphNode, origin_slot: number): void
private _getErrorStrokeStyle(
@@ -878,7 +779,6 @@ export class LGraphNode
? this.graph._links.get(input.link)
: null
this.onConnectionsChange?.(NodeSlotType.INPUT, i, true, link, input)
this.onInputAdded?.(input)
}
this.outputs ??= []
@@ -892,7 +792,6 @@ export class LGraphNode
const link = this.graph ? this.graph._links.get(linkId) : null
this.onConnectionsChange?.(NodeSlotType.OUTPUT, i, true, link, output)
}
this.onOutputAdded?.(output)
}
// SubgraphNode callback.
@@ -1153,8 +1052,6 @@ export class LGraphNode
if (node.updateOutputData) {
node.updateOutputData(link.origin_slot)
} else {
node.onExecute?.()
}
return link.data
@@ -1411,25 +1308,6 @@ export class LGraphNode
*/
doExecute(param?: unknown, options?: { action_call?: string }): void {
options = options || {}
if (this.onExecute) {
// enable this to give the event an ID
options.action_call ||= `${this.id}_exec_${Math.floor(Math.random() * 9999)}`
if (!this.graph) throw new NullGraphError()
// @ts-expect-error Technically it works when id is a string. Array gets props.
this.graph.nodes_executing[this.id] = true
this.onExecute(param, options)
// @ts-expect-error deprecated
this.graph.nodes_executing[this.id] = false
// save execution/action ref
this.exec_version = this.graph.iteration
if (options?.action_call) {
this.action_call = options.action_call
// @ts-expect-error deprecated
this.graph.nodes_executedAction[this.id] = options.action_call
}
}
// the nFrames it will be used (-- each step), means "how old" is the event
this.execute_triggered = 2
this.onAfterExecuteNode?.(param, options)
@@ -1547,7 +1425,6 @@ export class LGraphNode
// generate unique trigger ID if not present
if (!options.action_call)
options.action_call = `${this.id}_trigg_${Math.floor(Math.random() * 9999)}`
// -- wrapping node.onExecute(param); --
node.doExecute?.(param, options)
} else if (node.onAction) {
// generate unique action ID if not present
@@ -1646,7 +1523,6 @@ export class LGraphNode
this.outputs ||= []
this.outputs.push(output)
this.onOutputAdded?.(output)
if (LiteGraph.auto_load_slot_types)
LiteGraph.registerNodeAndSlotType(this, type, true)
@@ -1680,7 +1556,6 @@ export class LGraphNode
}
}
this.onOutputRemoved?.(slot)
this.setDirtyCanvas(true, true)
}
@@ -1705,7 +1580,6 @@ export class LGraphNode
this.inputs.push(input)
this.expandToFitContent()
this.onInputAdded?.(input)
LiteGraph.registerNodeAndSlotType(this, type)
this.setDirtyCanvas(true, true)
@@ -1721,7 +1595,7 @@ export class LGraphNode
this.disconnectInput(slot, true)
}
const { inputs } = this
const slot_info = inputs.splice(slot, 1)
inputs.splice(slot, 1)
for (let i = slot; i < inputs.length; ++i) {
const input = inputs[i]
@@ -1733,7 +1607,6 @@ export class LGraphNode
if (link) link.target_slot--
}
}
this.onInputRemoved?.(slot, slot_info[0])
this.setDirtyCanvas(true, true)
}
@@ -1917,11 +1790,6 @@ export class LGraphNode
if (this.constructor.widgets_info?.[property])
info = this.constructor.widgets_info[property]
// litescene mode using the constructor
if (!info && this.onGetPropertyInfo) {
info = this.onGetPropertyInfo(property)
}
info ||= {}
info.type ||= typeof this.properties[property]
if (info.widget == 'combo') info.type = 'enum'
@@ -2076,7 +1944,7 @@ export class LGraphNode
*
* Populates {@link out} with the results in graph space.
* Populates {@link _collapsed_width} with the collapsed width if the node is collapsed.
* Adjusts for title and collapsed status, but does not call {@link onBounding}.
* Adjusts for title and collapsed status.
* @param out `x, y, width, height` are written to this array.
* @param ctx The canvas context to use for measuring text.
*/
@@ -2135,7 +2003,6 @@ export class LGraphNode
updateArea(ctx?: CanvasRenderingContext2D): void {
const bounds = this._boundingRect
this.measure(bounds, ctx)
this.onBounding?.(bounds)
const renderArea = this._renderArea
renderArea.set(bounds)
@@ -2827,16 +2694,6 @@ export class LGraphNode
targetIndex = 0
}
// Allow target node to change slot
if (target_node.onBeforeConnectInput) {
// This way node can choose another slot (or make a new one?)
const requestedIndex = target_node.onBeforeConnectInput(
targetIndex,
target_slot
)
targetIndex = typeof requestedIndex === 'number' ? requestedIndex : null
}
if (
targetIndex === null ||
!target_node.inputs ||
@@ -3506,7 +3363,7 @@ export class LGraphNode
}
/**
* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus
* Allows to get onMouseMove events even if the mouse is out of focus
* @deprecated Use {@link LGraphCanvas.pointer} instead.
*/
captureInput(v: boolean): void {
@@ -3632,7 +3489,6 @@ export class LGraphNode
drawTitleBarBackground(
ctx: CanvasRenderingContext2D,
{
scale,
title_height = LiteGraph.NODE_TITLE_HEIGHT,
low_quality = false
}: DrawTitleOptions
@@ -3641,11 +3497,6 @@ export class LGraphNode
const shape = this.renderingShape
const size = this.renderingSize
if (this.onDrawTitleBar) {
this.onDrawTitleBar(ctx, title_height, size, scale, fgcolor)
return
}
if (this.title_mode === TitleMode.TRANSPARENT_TITLE) {
return
}
@@ -3757,7 +3608,6 @@ export class LGraphNode
drawTitleText(
ctx: CanvasRenderingContext2D,
{
scale,
default_title_color,
low_quality = false,
title_height = LiteGraph.NODE_TITLE_HEIGHT
@@ -3766,18 +3616,6 @@ export class LGraphNode
const size = this.renderingSize
const selected = this.selected
if (this.onDrawTitleText) {
this.onDrawTitleText(
ctx,
title_height,
size,
scale,
this.titleFontStyle,
selected
)
return
}
// Don't render title text if low quality
if (low_quality) {
return

View File

@@ -435,7 +435,10 @@ export class LiteGraphGlobal {
if (prev) this.onNodeTypeReplaced?.(type, base_class, prev)
// warnings
if (base_class.prototype.onPropertyChange)
if (
(base_class.prototype as unknown as Record<string, unknown>)
.onPropertyChange
)
console.warn(
`LiteGraph node class ${type} has onPropertyChange method, it must be called onPropertyChanged with d at the end`
)