comfy.graph is the editing surface for the graph currently shown to the user.
It provides graph-safe node, selection, viewport, group, link, and mutation
operations without exposing LGraph or LGraphCanvas.
Visible graph, root graph, and subgraphs
The active view is not always the document root:
A subgraph entry represents a definition, not each placed instance. If the same
definition is placed three times, it appears once and its internal nodes appear
once.
Node IDs must be resolved inside their owning graph. Do not collect all document
nodes into a map keyed only by ID:
node.graphId when persisting pack-owned in-memory state about a node.
Looking up nodes and links
LinkInfo is inert data with source and target
node IDs, slot IDs, types, and endpoint indexes at snapshot time. Indexes are
volatile; do not store them across slot mutations.
Use node.inputs and node.outputs to edit connectivity. graph.links() is for
inspection, not mutation.
graph.nodeAt({ x, y }) returns the topmost node at a graph-space point using
the rendered layout and z-order. It can find nothing before the first render.
Creating and removing nodes
add() constructs through the registered definition. It throws when the type
does not exist. duplicate() carries serializable widgets and properties but
does not copy links. It returns undefined when the source is gone or cannot be
constructed.
NodeHandle.remove() removes its own node. graph.remove(id) is the equivalent
when only an ID is available.
Replacing a node
Node type is identity and is read-only. Rebuild withreplace():
Compound edits and undo
Usebatch() for one synchronous user operation:
await inside it.
Selection and viewport
pointerPosition() is in graph coordinates and can return undefined when no
canvas is available. centerOn() does not change zoom.
For a panel anchored to a node, read node.getScreenRect() and update it when
the viewport changes:
Groups
GroupHandle provides:
getTitle()/setTitle();getColor()/setColor();nodes()for the nodes geometrically contained by the group;getBounds()in graph space;centerOn()for the visible view.
nodes() again after layout changes. A subgraph scope’s groups() reads groups
inside that definition.
Structural version token
graph.version changes when graph-visible state changes, including node or slot
structure, connections, node flags, and widget values committed through the
host protocol.
Treat it as an opaque token:
redraw().
graph.cacheSize is diagnostics for live handle-cache slots, not application
state.
Node and graph observation
There is no per-frame tick. Observe the semantic operation:Node property changes
visible. scope: 'document' includes the root and every
subgraph definition. Each event names its graph because node ID alone is not a
document-wide key.
The tracked fields are title, mode, foreground color, background color, shape,
and advanced-widget visibility. Position uses the movement stream instead
because it changes continuously during a drag.
Movement and drag completion
onNodeDragEnd is available under Nodes 2.0; the legacy renderer does
not publish a drag lifecycle.
Editor interaction state
comfy.isInteracting() reports whether the editor is already handling a link,
node, or widget gesture. A pack starting its own pointer gesture should stand
down while it is true.
Resolved supply inspection
graph.resolvedSupplies() runs the same pure supplier and priority arbitration
used by prompt construction and returns the winning graph-local edges without
mutating the graph. It is intended for editor commands that materialize virtual
broadcasts as real links. Exact-priority ties are absent, matching execution.
See Execution and resolution for supplier semantics.