Model Nodes, Ports, And Connectors

Copy Markdown View Source

Graph apps in LivePanels start with canvas items.

A node is an item. A port is an item capability. A connector is a canvas item that connects endpoints. That model is what lets graph apps reuse item placement, selection, z-order, history, clipboard, persistence, and shared mode.

Nodes Are Items

Declare graph nodes as items with port capabilities:

<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item
    id="source-1"
    type="source"
    at={{120, 80}}
    size={{520, 360}}
    ports={[
      %{name: "out", direction: :output, type: :data}
    ]}
  >
    <.source_node />
  </:item>

  <:item
    id="transform-1"
    type="transform"
    at={{720, 80}}
    size={{560, 400}}
    ports={[
      %{name: "in", direction: :input, type: :data, max_connections: 1},
      %{name: "out", direction: :output, type: :data}
    ]}
  >
    <.transform_node />
  </:item>
</.canvas_runtime>

The node can render LiveView content, static content, or app-only data. Graph behavior comes from the declared ports, not from a separate node system.

Ports Are Declared Capabilities

A port declaration names the endpoint and the rules LivePanels can validate:

%{
  name: "out",
  label: "Rows",
  direction: :output,
  type: :data,
  max_connections: :unlimited,
  anchor: %{edge: :right, offset: 0.5, inset: 0}
}

Common fields:

FieldPurpose
:nameStable endpoint name used by commands and connectors.
:labelOptional app-facing label.
:direction:input or :output.
:typeCompatibility group such as :data, :image, or :task.
:max_connectionsPositive integer or :unlimited; omitted means unlimited.
:anchorGeometry used to resolve the port position against the item frame.

Use stable port names. If a saved layout contains a connector to "out", the item type should still declare "out" when the layout restores.

Port Geometry

Port anchors are resolved from the item frame.

Edge anchors:

%{edge: :right, offset: 0.5, inset: 0}

Explicit coordinates:

%{x: {:px_from_end, 24}, y: 32}

For row-based nodes, compute offsets from row height:

defp column_ports do
  for slot <- 0..8, side <- [:input, :output] do
    %{
      name: port_name(slot, side),
      label: "Column #{slot}",
      direction: side,
      type: :field,
      anchor: %{
        edge: if(side == :input, do: :left, else: :right),
        offset: 56 + slot * 32 + 16,
        inset: 0
      }
    }
  end
end

The browser does not measure DOM handles and report them back as canonical state. The server can resolve connector endpoints from item placement and declared anchors.

Connectors Are Canvas Items

A connector is a %LivePanels.Item{type: :connector}. Use LivePanels.Item.Connector to build and inspect connector items.

Connection connectors point from one item port to another:

LivePanels.Item.Connector.connection(
  from: {"source-1", "out"},
  to: {"transform-1", "in"},
  label: "rows",
  role: :data
)

Relationship connectors connect item centers:

LivePanels.Item.Connector.relationship("idea-1", "idea-2",
  label: "related"
)

Use connection connectors when endpoint ports matter. Use relationship connectors when the product only needs item-to-item relationships.

Starting Connectors

Canvas applications can start with connector topology:

<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item
    :for={node <- @nodes}
    id={node.id}
    type="workflow_node"
    at={{node.x, node.y}}
    size={{node.w, node.h}}
    ports={node.ports}
  >
    <.workflow_node node={node} />
  </:item>

  <:item :let={context}>
    <.workflow_node item={context.item} />
  </:item>
</.canvas_runtime>

Create starting connectors with LivePanels.Graph.connect/2 after the endpoint items exist in canvas state, or restore them from layout persistence. If an endpoint cannot be resolved, the connector command is rejected instead of creating a partial graph.

Canvas Data Versus Domain Data

Keep this distinction clear:

  • LivePanels connector state says item A's port connects to item B's port.
  • Your app decides whether that means a workflow transition, foreign key, dataflow pipe, dependency, or relation.

If a connection should create a durable domain record, handle that in your application or app layer and submit the LivePanels graph command for the canvas effect.

Design Checklist

Before rendering the graph, decide:

  • what item types are graph nodes;
  • which ports are stable and restorable;
  • which port types are compatible;
  • whether port connection limits are runtime rules or product policy;
  • whether connectors are connection connectors or relationship connectors;
  • which connector presentation fields your application will use for labels, roles, markers, routes, and bends;
  • which domain records, if any, mirror canvas connectors.