# `LivePanels.Graph.Layout`
[🔗](https://github.com/livepanels/livepanels/blob/v0.1.0/lib/livepanels/graph/layout.ex#L1)

Headless graph layout assist contracts.

This module plans placements for graph-connected world items and returns data
that applications can submit through the normal canvas `place_item` command path. It
does not render controls, choose product workflow language, or persist layout
by itself.

# `adapter`

```elixir
@type adapter() :: %{
  direction: axis(),
  nodes: [map()],
  connectors: [map()],
  components: [component()],
  boundaries: [boundary()]
}
```

# `axis`

```elixir
@type axis() :: :horizontal | :vertical
```

# `boundary`

```elixir
@type boundary() :: map()
```

# `component`

```elixir
@type component() :: %{
  id: String.t(),
  item_ids: [String.t()],
  connector_ids: [String.t()]
}
```

# `layout_strategy`

```elixir
@type layout_strategy() :: :layered | :tree | :orthogonal | :components
```

# `placement`

```elixir
@type placement() :: %{
  space: :world,
  anchor: :top_left,
  x: integer(),
  y: integer(),
  w: number(),
  h: number()
}
```

# `adapter`

```elixir
@spec adapter(map(), keyword() | map()) :: adapter()
```

Returns a port-aware payload for external graph layout engines.

# `adapter_commands`

```elixir
@spec adapter_commands(map(), map() | [map()], keyword() | map()) :: [
  {:place_item, String.t(), placement()}
]
```

Converts external layout node coordinates into `place_item` command tuples.

Accepted node result entries are maps with `:id`/`"id"` plus `:x`/`"x"` and
`:y`/`"y"`. Width and height default to the current item frame when absent.

# `adapter_placements`

```elixir
@spec adapter_placements(map(), map() | [map()], keyword() | map()) :: [
  {String.t(), placement()}
]
```

Converts an external adapter result into placement tuples.

# `commands`

```elixir
@spec commands(map(), layout_strategy(), keyword() | map()) :: [
  {:place_item, String.t(), placement()}
]
```

Returns `place_item` command tuples for a layout strategy.

Strategies: `:layered`, `:tree`, `:orthogonal`, `:components`.

# `component_commands`

```elixir
@spec component_commands(map(), keyword() | map()) :: [
  {:place_item, String.t(), placement()}
]
```

Returns `place_item` commands for `component_placements/2`.

# `component_placements`

```elixir
@spec component_placements(map(), keyword() | map()) :: [{String.t(), placement()}]
```

Returns placements arranged by connected component.

Components are computed with undirected graph membership. Each component uses
`:strategy` (`:layered`, `:tree`, or `:orthogonal`) and is then offset as a
separate band.

# `layered_commands`

```elixir
@spec layered_commands(map(), keyword() | map()) :: [
  {:place_item, String.t(), placement()}
]
```

Returns `place_item` commands for `layered_placements/2`.

# `layered_placements`

```elixir
@spec layered_placements(map(), keyword() | map()) :: [{String.t(), placement()}]
```

Returns layered graph placements as `{item_id, placement}` tuples.

The helper derives layer depth from directed connectors. Items with no incoming
connector start at depth zero, and downstream items move one layer at a time. If a
graph contains a cycle, the bounded pass count keeps the helper deterministic
and returns the deepest placement discovered during the pass window.

Options:

* `:axis` - `:horizontal` (default) or `:vertical`.
* `:x` / `:y` - origin coordinates. Defaults to the current graph minimum.
* `:layer_gap` - distance between depth layers. Default `340`.
* `:item_gap` - distance between items in the same layer. Default `230`.

# `orthogonal_commands`

```elixir
@spec orthogonal_commands(map(), keyword() | map()) :: [
  {:place_item, String.t(), placement()}
]
```

Returns `place_item` commands for `orthogonal_placements/2`.

# `orthogonal_placements`

```elixir
@spec orthogonal_placements(map(), keyword() | map()) :: [{String.t(), placement()}]
```

Returns placements aligned for orthogonal graph routing.

This arrangement keeps directed ranks on one axis and orders items within a
rank by current spatial position, giving applications a headless track layout they
can pair with orthogonal connector paths.

# `port_aware_adapter`

```elixir
@spec port_aware_adapter(map(), keyword() | map()) :: adapter()
```

Returns a port-aware external layout adapter payload.

The adapter names nodes, ports, connectors, connected components, and graph
boundaries using runtime ids. Applications can pass this data to an external layout
engine and convert the resulting node coordinates with `adapter_commands/3`.

# `tree_commands`

```elixir
@spec tree_commands(map(), keyword() | map()) :: [
  {:place_item, String.t(), placement()}
]
```

Returns `place_item` commands for `tree_placements/2`.

# `tree_placements`

```elixir
@spec tree_placements(map(), keyword() | map()) :: [{String.t(), placement()}]
```

Returns tree-oriented placements for graph world items.

Roots are items without incoming connectors inside their component. When a cycle or
a parentless remainder exists, the remaining items are placed deterministically
after the discovered tree ranks.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
