# Entity Interactions (Current System)

This document maps the relationships and interaction patterns between all entity types in the litegraph layer as it exists today. It serves as a baseline for the ECS migration planned in [ADR 0008](../adr/0008-entity-component-system.md).

## Entities

| Entity   | Class         | ID Type         | Primary Location                                                             |
| -------- | ------------- | --------------- | ---------------------------------------------------------------------------- |
| Graph    | `LGraph`      | `UUID`          | `src/lib/litegraph/src/LGraph.ts`                                            |
| Node     | `LGraphNode`  | `NodeId`        | `src/lib/litegraph/src/LGraphNode.ts`                                        |
| Link     | `LLink`       | `LinkId`        | `src/lib/litegraph/src/LLink.ts`                                             |
| Subgraph | `Subgraph`    | `UUID`          | `src/lib/litegraph/src/LGraph.ts` (ECS: node component, not separate entity) |
| Widget   | `BaseWidget`  | name + nodeId   | `src/lib/litegraph/src/widgets/BaseWidget.ts`                                |
| Slot     | `SlotBase`    | index on parent | `src/lib/litegraph/src/node/SlotBase.ts`                                     |
| Reroute  | `Reroute`     | `RerouteId`     | `src/lib/litegraph/src/Reroute.ts`                                           |
| Group    | `LGraphGroup` | `number`        | `src/lib/litegraph/src/LGraphGroup.ts`                                       |

Under the ECS model, subgraphs are not a separate entity kind — they are nodes with `SubgraphStructure` and `SubgraphMeta` components. See [Subgraph Boundaries](subgraph-boundaries-and-promotion.md).

## 1. Overview

High-level ownership and reference relationships between all entities.

```mermaid
graph TD
    subgraph Legend
        direction LR
        L1[A] -->|owns| L2[B]
        L3[C] -.->|references| L4[D]
        L5[E] ==>|extends| L6[F]
    end

    Graph["LGraph
(UUID)"]
    Node["LGraphNode
(NodeId)"]
    SubgraphEntity["Subgraph
(UUID)"]
    SubgraphNode["SubgraphNode"]
    Link["LLink
(LinkId)"]
    Widget["BaseWidget
(name)"]
    Slot["SlotBase
(index)"]
    Reroute["Reroute
(RerouteId)"]
    Group["LGraphGroup
(number)"]
    Canvas["LGraphCanvas"]

    %% Ownership (solid)
    Graph -->|"_nodes[]"| Node
    Graph -->|"_links Map"| Link
    Graph -->|"reroutes Map"| Reroute
    Graph -->|"_groups[]"| Group
    Graph -->|"_subgraphs Map"| SubgraphEntity
    Node -->|"inputs[], outputs[]"| Slot
    Node -->|"widgets[]"| Widget

    %% Extends (thick)
    SubgraphEntity ==>|extends| Graph
    SubgraphNode ==>|extends| Node

    %% References (dashed)
    Link -.->|"origin_id, target_id"| Node
    Link -.->|"parentId"| Reroute
    Slot -.->|"link / links[]"| Link
    Reroute -.->|"linkIds"| Link
    Reroute -.->|"parentId"| Reroute
    Group -.->|"_children Set"| Node
    Group -.->|"_children Set"| Reroute
    SubgraphNode -.->|"subgraph"| SubgraphEntity
    Node -.->|"graph"| Graph
    Canvas -.->|"graph"| Graph
    Canvas -.->|"selectedItems"| Node
    Canvas -.->|"selectedItems"| Group
    Canvas -.->|"selectedItems"| Reroute
```

## 2. Connectivity

How Nodes, Slots, Links, and Reroutes form the graph topology.

```mermaid
graph LR
    subgraph OutputNode["Origin Node"]
        OSlot["Output Slot
links: LinkId[]"]
    end

    subgraph InputNode["Target Node"]
        ISlot["Input Slot
link: LinkId | null"]
    end

    OSlot -->|"LinkId ref"| Link["LLink
origin_id + origin_slot
target_id + target_slot
type: ISlotType"]
    Link -->|"LinkId ref"| ISlot

    Link -.->|"parentId"| R1["Reroute A"]
    R1 -.->|"parentId"| R2["Reroute B"]

    R1 -.-|"linkIds Set"| Link
    R2 -.-|"linkIds Set"| Link
```

### Subgraph Boundary Connections

```mermaid
graph TD
    subgraph ParentGraph["Parent Graph"]
        ExtNode["External Node"]
        SGNode["SubgraphNode
(in parent graph)"]
    end

    subgraph SubgraphDef["Subgraph"]
        SInput["SubgraphInput"]
        SInputNode["SubgraphInputNode
(virtual)"]
        InternalNode["Internal Node"]
        SOutputNode["SubgraphOutputNode
(virtual)"]
        SOutput["SubgraphOutput"]
    end

    ExtNode -->|"Link (parent graph)"| SGNode
    SGNode -.->|"maps to"| SInput
    SInput -->|"owns"| SInputNode
    SInputNode -->|"Link (subgraph)"| InternalNode
    InternalNode -->|"Link (subgraph)"| SOutputNode
    SOutputNode -->|"owned by"| SOutput
    SOutput -.->|"maps to"| SGNode
    SGNode -->|"Link (parent graph)"| ExtNode
```

### Floating Links (In-Progress Connections)

```mermaid
graph LR
    Slot["Source Slot"] -->|"drag starts"| FL["Floating LLink
origin_id=-1 or target_id=-1"]
    FL -->|"stored in"| FLMap["graph.floatingLinks Map"]
    FL -.->|"may pass through"| Reroute
    Reroute -.-|"floatingLinkIds Set"| FL
    FL -->|"on drop"| Permanent["Permanent LLink
(registered in graph._links)"]
```

## 3. Rendering

How LGraphCanvas draws each entity type.

```mermaid
graph TD
    Canvas["LGraphCanvas
render loop"]

    Canvas -->|"1. background"| DrawGroups["drawGroups()"]
    Canvas -->|"2. connections"| DrawConns["drawConnections()"]
    Canvas -->|"3. foreground"| DrawNodes["drawNode() per node"]
    Canvas -->|"4. in-progress"| DrawLC["LinkConnector.renderLinks"]

    DrawGroups --> Group["group.draw(canvas, ctx)"]

    DrawConns --> LinkSeg["LinkSegment interface"]
    LinkSeg --> Link["LLink path rendering"]
    LinkSeg --> RerouteRender["Reroute inline rendering
(draw, drawSlots)"]

    DrawNodes --> NodeDraw["node drawing pipeline"]
    NodeDraw -->|"drawSlots()"| SlotDraw["slot.draw() per slot"]
    NodeDraw -->|"drawWidgets()"| WidgetDraw["widget.drawWidget() per widget"]
    NodeDraw -->|"title, badges"| NodeChrome["title bar, buttons, badges"]

    DrawLC --> FloatingViz["Floating link visualization"]
```

### Rendering Order Detail

```mermaid
sequenceDiagram
    participant C as Canvas
    participant Gr as Groups
    participant L as Links/Reroutes
    participant N as Nodes
    participant S as Slots
    participant W as Widgets

    C->>Gr: drawGroups() — background layer
    Gr-->>C: group shapes + titles

    C->>L: drawConnections() — middle layer
    L-->>C: bezier paths + reroute dots

    loop each node (back to front)
        C->>N: drawNode()
        N->>N: drawNodeShape() (background, title)
        N->>S: drawSlots() (input/output circles)
        S-->>N: slot shapes + labels
        N->>W: drawWidgets() (if not collapsed)
        W-->>N: widget UI elements
        N-->>C: complete node
    end

    C->>C: overlay (tooltips, debug)
```

## 4. Lifecycle

Creation and destruction flows for each entity.

### Node Lifecycle

```mermaid
stateDiagram-v2
    [*] --> Created: new LGraphNode(title)
    Created --> Configured: node.configure(data)
    Configured --> InGraph: graph.add(node)

    state InGraph {
        [*] --> Active
        Active --> Active: connect/disconnect slots
        Active --> Active: add/remove widgets
        Active --> Active: move, resize, collapse
    }

    InGraph --> Removed: graph.remove(node)
    Removed --> [*]

    note right of Created
        Constructor sets defaults.
        No graph reference yet.
    end note

    note right of InGraph
        node.onAdded(graph) called.
        ID assigned from graph.state.
        Slots may trigger onConnectionsChange.
    end note

    note right of Removed
        All links disconnected.
        node.onRemoved() called.
        Removed from graph._nodes.
    end note
```

### Link Lifecycle

```mermaid
stateDiagram-v2
    [*] --> Created: node.connect() or connectSlots()
    Created --> Registered: graph._links.set(id, link)

    state Registered {
        [*] --> Active
        Active --> Active: data flows through
        Active --> Active: reroutes added/removed
    }

    Registered --> Disconnected: node.disconnectInput/Output()
    Disconnected --> Removed: link.disconnect(network)
    Removed --> [*]

    note right of Created
        new LLink(id, type, origin, slot, target, slot)
        Output slot.links[] updated.
        Input slot.link set.
    end note

    note right of Removed
        Removed from graph._links.
        Orphaned reroutes cleaned up.
        graph._version incremented.
    end note
```

### Widget Lifecycle

```mermaid
stateDiagram-v2
    [*] --> Created: node.addWidget(type, name, value, options)
    Created --> Concrete: toConcreteWidget()
    Concrete --> Bound: widget.setNodeId(nodeId)

    state Bound {
        [*] --> Active
        Active --> Active: setValue() → store + node callback
        Active --> Active: draw(), onClick(), onDrag()
    }

    Bound --> Removed: node.removeWidget(widget)
    Removed --> [*]

    note right of Bound
        Registered in WidgetValueStore.
        State keyed by graphId:nodeId:name.
        Value reads/writes via store.
    end note
```

### Subgraph Lifecycle

```mermaid
stateDiagram-v2
    [*] --> Created: graph.createSubgraph(data)

    state Created {
        [*] --> Defined
        Defined: registered in rootGraph._subgraphs
    }

    Created --> Instantiated: new SubgraphNode(subgraph)
    Instantiated --> InGraph: graph.add(subgraphNode)

    state InGraph {
        [*] --> Active
        Active --> Active: add/remove inputs/outputs
        Active --> Active: promote/demote widgets
        Active --> Active: edit internal nodes
    }

    InGraph --> Unpacked: graph.unpackSubgraph(node)
    Unpacked --> [*]

    InGraph --> NodeRemoved: graph.remove(subgraphNode)
    NodeRemoved --> MaybePurged: no other SubgraphNodes reference it?
    MaybePurged --> [*]

    note right of Instantiated
        SubgraphNode.subgraph = subgraph.
        Inputs/outputs synced from subgraph.
    end note

    note right of Unpacked
        Internal nodes cloned to parent.
        Links remapped. SubgraphNode removed.
        Subgraph def removed if unreferenced.
    end note
```

## 5. State Management

External stores and their relationships to entities.

```mermaid
graph TD
    subgraph Entities
        Node["LGraphNode"]
        Widget["BaseWidget"]
        Reroute["Reroute"]
        Link["LLink"]
        Graph["LGraph"]
        SGNode["SubgraphNode"]
    end

    subgraph Stores
        WVS["WidgetValueStore
(Pinia)"]
        PS["PromotionStore
(Pinia)"]
        LM["LayoutMutations
(composable)"]
    end

    subgraph GraphState["Graph Internal State"]
        Version["graph._version"]
        LGState["graph.state
(lastNodeId, lastLinkId,
lastRerouteId, lastGroupId)"]
    end

    %% WidgetValueStore
    Widget -->|"setNodeId() registers"| WVS
    Widget <-->|"value, label, disabled"| WVS
    WVS -.->|"keyed by graphId:nodeId:name"| Widget

    %% PromotionStore
    SGNode -->|"tracks promoted widgets"| PS
    Widget -.->|"isPromotedByAny() query"| PS

    %% LayoutMutations
    Node -->|"pos/size setter"| LM
    Reroute -->|"move()"| LM
    Link -->|"connectSlots()/disconnect()"| LM
    Graph -->|"add()/remove()"| LM

    %% Graph state
    Node -->|"connect/disconnect"| Version
    Widget -->|"setValue()"| Version
    Node -->|"collapse/toggleAdvanced"| Version
    Graph -->|"add/remove entities"| LGState
```

### Change Notification Flow

```mermaid
sequenceDiagram
    participant E as Entity (Node/Widget/Link)
    participant G as LGraph
    participant C as LGraphCanvas
    participant R as Render Loop

    E->>G: graph._version++
    E->>G: graph.beforeChange() (undo checkpoint)

    Note over E,G: ... mutation happens ...

    E->>G: graph.afterChange() (undo checkpoint)
    E->>G: graph.change()
    G->>C: canvasAction → canvas.setDirty(true, true)
    C->>R: dirty flags checked on next frame
    R->>C: full redraw
```

### Widget State Delegation

```mermaid
sequenceDiagram
    participant N as Node
    participant W as Widget
    participant S as WidgetValueStore
    participant G as Graph

    N->>W: addWidget(type, name, value)
    W->>W: toConcreteWidget()
    N->>W: setNodeId(nodeId)
    W->>S: registerWidget(graphId, state)
    S-->>W: state reference stored in widget._state

    Note over W,S: All value access now goes through store

    W->>S: widget.value = newVal (setter)
    S-->>S: store.state.value = newVal
    W->>N: node.onWidgetChanged?.(name, val)
    W->>G: graph._version++
```
