# Render Connector Items

LivePanels renders connectors as connector items in the runtime world SVG
projection. Applications do not build a graph connector SVG layer, project paths,
or duplicate hit targets.

The connector item runtime owns:

- routed path geometry;
- marker definitions and marker URLs;
- connector labels stored on connector item presentation;
- item selection and hit target attrs;
- bend handles for selected connectors;
- stream updates for connector item changes.

The graph extension owns endpoint resolution, connection rules, graph selection,
graph layout, and graph command protocol.

Applications own item UI, ports, product controls, and CSS.

## Use The Runtime SVG Projection

Render graph canvases with the ordinary runtime component. Add ports inside item
markup; do not add a connector layer in `:world_layer`:

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

      <button
        :for={port <- context.ports}
        {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>
```

Connector items are streamed into:

```html
<g data-livepanels-svg-projection="items">...</g>
```

Each connector item renders as:

- `g.livepanels-connector-projection-item`;
- `path.livepanels-connector-path`;
- optional `text.livepanels-connector-label`;
- selected bend handles;
- `path.livepanels-connector-hit-path`.

## Style Connectors

The renderer emits structural classes and data attrs, but no product theme:

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

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

.app-canvas
  [data-livepanels-connector-emphasis="active"]
  .livepanels-connector-path {
  color: var(--app-accent);
}
```

Useful public attrs include:

- `data-livepanels-connector-id`;
- `data-livepanels-connector-from-id`;
- `data-livepanels-connector-from-port`;
- `data-livepanels-connector-to-id`;
- `data-livepanels-connector-to-port`;
- `data-livepanels-connector-route`;
- `data-livepanels-connector-role`;
- `data-livepanels-connector-emphasis`;
- `data-livepanels-connector-selection`;
- `data-livepanels-connector-hit-target`.

`data-livepanels-connector-selection` is `selected`, `related`, or `unrelated`
when the actor has a graph selection.

## Store Presentation On Connector Items

Connector presentation belongs on the connector item:

```elixir
connector =
  LivePanels.Item.Connector.connection(
    id: "extract-load",
    from: LivePanels.Item.Connector.endpoint("extract", "out"),
    to: LivePanels.Item.Connector.endpoint("load", "in"),
    route: :orthogonal,
    direction: :horizontal,
    label: "rows",
    role: :data,
    marker_end: :arrow,
    emphasis: :normal
  )
```

Starting connectors are created after their endpoint items exist:

```elixir
use LivePanels.Canvas,
  pubsub: MyApp.PubSub,
  initial: [connectors: [connector]],
  domains: [:graph]

use LivePanels.Toolkit.Graph
```

Programmatic commands accept the same presentation fields:

```elixir
socket =
  LivePanels.Graph.connect!(socket,
    from: {"extract", "out"},
    to: {"load", "in"},
    route: :orthogonal,
    label: "rows",
    emphasis: :active
  )
```

Update existing connector presentation through the graph command path:

```elixir
socket =
  LivePanels.Graph.edit!(socket, "extract-load",
    route: :straight,
    emphasis: :active
  )
```

Use `:all` to update every connector:

```elixir
socket =
  LivePanels.Graph.edit!(socket, :all,
    route: :orthogonal,
    direction: :horizontal
  )
```

## Product Labels

If labels come from product data, write the resolved value onto connector item
presentation with `LivePanels.Graph.connect!/2` or
`LivePanels.Graph.edit!/3`. This keeps rendered labels in
the same state, persistence, history, and shared-canvas path as connector routes
and markers.

## Route Options

Supported routes are:

- `:bezier`;
- `:orthogonal`;
- `:straight`.

Route options are connector presentation:

```elixir
LivePanels.Graph.edit!(socket, connector_id,
  route: :bezier,
  direction: :horizontal,
  precision: 1,
  curvature: 0.35,
  min_curvature: 40
)
```

The renderer reads those fields directly from the connector item, so route
changes survive replacement, streaming, persistence, and shared canvas updates.

## Layer Ordering

Connector items live in the world SVG item projection with drawing vector items.
The runtime orders world HTML items, world SVG items, and viewport items through
the same item stream story.

Use CSS and item z-index for visual stacking. Use `:world_layer` only for
application-owned background projections that are not LivePanels items. Use
`:world_overlay` for camera-addressed host projections that must sit above
LivePanels items, such as pointer presence cursors.
