# Add Graph Connections

Graph support lets canvas items expose ports and connect those ports with
validated connectors.

Use it when a connector changes the canvas itself: workflow transitions,
dataflow pipes, dependency lines, relationships, references, or diagram
connections.

LivePanels owns the runtime facts: declared ports, connector state, endpoint
validation, connector selection, routing projection, history, persistence, and
shared coordination. Your application owns node markup, port visuals, connector
styling, labels, toolbars, and product meaning.

<div data-livepanels-doc-graph="livepanels-graph-runtime"></div>

## Use The Graph Toolkit

Import the graph toolkit on graph-capable canvases:

```elixir
defmodule MyAppWeb.FlowLive do
  use MyAppWeb, :live_view

  use LivePanels.Canvas,
    pubsub: MyApp.PubSub

  use LivePanels.Toolkit.Graph

  def render(assigns) do
    ~H"""
    <.canvas_runtime {canvas_runtime_attrs(assigns)}>
      <:item
        id="transform-1"
        type="transform"
        at={{120, 80}}
        size={{560, 400}}
        ports={[
          %{name: "input", direction: :input, type: :data, max_connections: 1},
          %{name: "output", direction: :output, type: :data}
        ]}
      >
        <.transform_node />
      </:item>
    </.canvas_runtime>
    """
  end
end
```

Ports belong to item attrs. They are not just buttons in the DOM.
That lets the server validate endpoint existence, direction, type, and
connection limits.

## Declare Port Geometry

Ports need world-space endpoints. Declare their anchor relative to the item
frame:

```elixir
%{
  name: "output",
  direction: :output,
  type: :data,
  anchor: %{edge: :right, offset: 0.5, inset: 0}
}
```

Or use explicit offsets from the item origin:

```elixir
%{
  name: "preview",
  direction: :output,
  type: :image,
  anchor: %{x: {:px_from_end, 24}, y: 32}
}
```

The runtime resolves anchors from item placement. The browser does not become
the canonical source of port coordinates.

## Render Ports

Render app-owned port controls in the `:item` slot:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item :let={context}>
    <article class="node-card">
      <header {drag_handle_attrs()}>{context.item.label}</header>

      <button
        :for={port <- context.ports}
        class={"node-port node-port--#{port.direction}"}
        {graph_port_anchor_attrs(port)}
        {if port.direction == :output,
          do: begin_connector_attrs(context.item, port),
          else: complete_connector_attrs(context.item, port)}
      >
        {port.label}
      </button>

      {context.content}
    </article>
  </:item>
</.canvas_runtime>
```

`graph_port_anchor_attrs/2` gives the browser the port metadata. The begin and
complete attrs send graph commands when a user connects ports.

You can render ports as buttons, dots, handles, rows, or icons. The runtime
only needs the attrs.

## Connect From Server Code

Use `LivePanels.Graph.connect/2` when server code creates a connector:

```elixir
socket =
  LivePanels.Graph.connect!(socket,
    from: {"extract-1", "output"},
    to: {"transform-1", "input"}
  )
```

Remove or reconnect connectors through the same owner module:

```elixir
socket = LivePanels.Graph.disconnect!(socket, connector_id)

socket =
  LivePanels.Graph.reconnect!(socket, connector_id,
    endpoint: :to,
    target: {"load-1", "input"}
  )
```

These calls validate graph rules before changing canvas state.

LivePanels permits cycles by default because not every graph is a DAG. If your
application needs DAG semantics, reject the connector command in policy with
`LivePanels.Graph.Query.creates_cycle?/3`.

## Render Connectors

`LivePanels.Toolkit.Graph` registers the graph extension. With `canvas_runtime/1`,
connector items render automatically in the world SVG item projection. Do not
add a host connector layer:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item :let={context}>
    <.node context={context} />
  </:item>
</.canvas_runtime>
```

Style connectors with the runtime classes and data attrs:

```css
.graph-canvas .livepanels-connector-path {
  stroke: currentColor;
  stroke-width: 3px;
}

.graph-canvas
  [data-livepanels-connector-selection="selected"]
  .livepanels-connector-path {
  stroke-width: 4px;
}
```

Store route, label, role, marker, and emphasis on connector items:

```elixir
socket =
  LivePanels.Graph.edit!(socket, connector_id,
    route: :orthogonal,
    label: "rows",
    emphasis: :active
  )
```

## Select And Fit Graphs

Use graph attrs for graph selection controls:

```heex
<button {graph_select_all_attrs()}>Select graph</button>
<button {graph_fit_attrs()}>Fit graph</button>
<button {graph_fit_selection_attrs()}>Fit selection</button>
```

Use server helpers when the application drives graph navigation:

```elixir
socket = LivePanels.Graph.select_all!(socket)
socket = LivePanels.Graph.fit(socket, :graph)
socket = LivePanels.Graph.fit(socket, :selection)
```

Graph selection includes graph connectors as well as items. Ordinary item selection
helpers continue to work for item-only selections.

## Layout Graph Nodes

Use graph layout when node placement should follow graph structure:

```elixir
socket = LivePanels.Graph.layout!(socket, :layered)
```

Graph layout applies ordinary canvas placement commands. That means history,
persistence, policy, and shared coordination still see item placement changes
the normal way.

## Persistence, History, Clipboard, And Shared Mode

Connector items live in canvas state. Because of that:

- persistent layouts can include graph connectors;
- undo and redo can reverse graph mutations;
- clipboard can copy selected items and their internal connector items together;
- shared canvases can broadcast accepted graph changes;
- moving an item can refresh connected connector projection.

The application should not keep a separate canonical graph store for runtime
connectors. If your product has domain relationships, store those in your app
and mirror the runtime connector commands when the canvas should show or edit
them.

## What LivePanels Owns

LivePanels owns port declarations, connector validation, connector state, selection,
projection helpers, graph commands, history/persistence participation, and
shared coordination.

Your application owns node design, port design, SVG connector styling, labels,
menus, semantic meaning, and any domain records behind the graph.
