# Add Items

Items are the units of a LivePanels canvas.

An item has an id, type, label, placement, visibility, z-order, props, and
capabilities. It may render LiveView content, expose graph ports, represent a
vector item, or exist only as runtime state.

The application decides what an item looks like. LivePanels owns the runtime facts that
make the item movable, selectable, persistent, shareable, and commandable.

## Declare Items In Markup

Use `<:item>` inside `canvas_runtime/1` when the host source code should own the
item instance and its content:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item id="note-1" at={{120, 80}} size={{560, 360}} resize>
    <section class="note-card">
      <header {drag_handle_attrs()}>Note</header>
      <p>Any HEEx can live here.</p>
      <.resize_handles />
    </section>
  </:item>
</.canvas_runtime>
```

The `id` is the runtime item id. `at` declares `{x, y}`. `size` declares
`{w, h}`. The slot entry creates the canvas item through the normal command,
state, persistence, and shared-canvas path; the slot body is the rendered item
content.

The slot may receive a render context:

```heex
<:item :let={context} id="note-1" at={{120, 80}} size={{560, 360}} resize>
  <section class={"note-card #{if context.selected?, do: "is-selected"}"}>
    <header {drag_handle_attrs()}>{context.item.id}</header>
    <p>{context.item.w} x {context.item.h}</p>
    <.resize_handles :if={context.view.resizable?} />
  </section>
</:item>
```

Declared items are source-owned. To remove one permanently, stop rendering that
slot. Runtime-created items use commands plus an id-less item renderer slot.

## Render Runtime Items

When an item is created from a browser control or server command, the host still
owns the item body in HEEx. Add one id-less `:item` slot to render runtime-owned
items:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item :let={context}>
    <section class="note-card">
      <header {drag_handle_attrs()}>{context.item.label}</header>
      <p>{LivePanels.Item.prop(context.item, :body, "")}</p>
      <.resize_handles :if={context.view.resizable?} />
    </section>
  </:item>
</.canvas_runtime>
```

The item command payload supplies type, placement, props, and capabilities.
Common options include:

- `:label`;
- `:props`;
- `:resize`;
- `:interaction`, such as `interaction: %{removable: false}`;
- `:drag_drop`;
- graph `:ports`;
- `:render`, such as frame overflow or stream update behavior.

Use `mix livepanels.check` when a declaration is not behaving as expected. The
task validates canvas declarations before you debug browser wiring.

## Add Items From App Controls

Use `add_item_attrs/2` for an app-rendered browser control that creates a
runtime item from an explicit type and option payload:

```heex
<button {add_item_attrs(:note)}>New note</button>
```

You can pass placement and item props as attrs:

```heex
<button
  {add_item_attrs(:note,
    x: 160,
    y: 120,
    w: 380,
    h: 240,
    props: %{body: "Created from the toolbar"}
  )}
>
  New note
</button>
```

The button is your markup. The attr helper supplies the command payload.

## Add Items From Server Code

Use `LivePanels.Canvas.Item.add/3` when an application event or server process should
create an item:

```elixir
def handle_event("create-note", _params, socket) do
  socket =
    LivePanels.Canvas.Item.add!(socket, :note,
      id: "note-#{System.unique_integer([:positive])}",
      x: 120,
      y: 80,
      w: 360,
      h: 220,
      props: %{body: "Created on the server"}
    )

  {:noreply, socket}
end
```

The command still goes through the same validation, policy, state update,
history, persistence, and projection path as a browser action.

## Place Items

Placement describes where an item lives:

```elixir
%{
  space: :world,
  anchor: :top_left,
  x: 120,
  y: 80,
  w: 360,
  h: 220
}
```

World-space items move with the camera. They are the normal choice for panels,
cards, graph nodes, and vector items.

Viewport-space items stay pinned to the screen while the world pans and zooms.
Use them for HUDs, overlays, or controls that should belong to the canvas but
not to the world.

Viewport items can also declare responsive frames. Configure named viewport
profiles on the canvas, then put `viewport_frames` on the viewport item:

```heex
<:item
  id="canvas-controls"
  type="canvas_controls"
  space={:viewport}
  anchor={:bottom_right}
  lock
  viewport_frames={%{
    default: %{
      space: :viewport,
      anchor: :bottom_right,
      x: 16,
      y: 16,
      w: {:fill, inset: 16, min: 280, max: 480},
      h: 72
    },
    compact: %{space: :viewport, anchor: :top_left, x: 0, y: 0, w: :fill, h: 96}
  }}
>
  <nav class="canvas-controls">...</nav>
</:item>
```

The frame keys are `:space`, `:anchor`, `:x`, `:y`, `:w`, `:h`, and
`:space` must resolve to `:viewport` when present. Width and height may be
positive numbers, `:fill`, or `{:fill, inset: n, min: n, max: n}`. The
`default` frame is used when no configured profile matches.

Viewport frames are render projection only. LivePanels recomputes the frame for
each connected socket from that socket's viewport snapshot, but
`Canvas.State.items`, layout persistence, and shared coordination keep canonical
item placement. World-space responsive layout remains application policy; use
item commands or host layout code when world items should move at a breakpoint.

Place an existing item with `LivePanels.Canvas.Item.place/3`:

```elixir
socket =
  LivePanels.Canvas.Item.place!(socket, "note-1", %{
    space: :world,
    anchor: :top_left,
    x: 240,
    y: 140,
    w: 420,
    h: 260
  })
```

Use `move_by/3`, `move_to/3`, `resize_by/3`, and `resize_to/3` for common
placement changes.

## Store Item Props

Use `LivePanels.Canvas.Item` for app-owned item props:

```elixir
socket =
  LivePanels.Canvas.Item.merge_props!(socket, "note-1", %{
    status: "review",
    accent: "blue"
  })
```

The canvas can persist item props that belong to layout/runtime recovery, but
your application remains responsible for domain records. For example, a canvas
item can store `record_id`, while your app owns the record itself.

An id-less `:item` slot receives a render context for runtime items:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item :let={context}>
    <section class={"note note--#{LivePanels.Item.prop(context.item, :accent, "plain")}"}>
      {context.content}
    </section>
  </:item>
</.canvas_runtime>
```

## Hide, Show, Or Remove Items

Visibility is not removal.

```elixir
socket = LivePanels.Canvas.Item.hide!(socket, "note-1")
socket = LivePanels.Canvas.Item.show!(socket, "note-1")
socket = LivePanels.Canvas.Item.toggle_visibility!(socket, "note-1")
```

A hidden item can remain in state, history, layout, graph relationships, and
shared coordination. Remove an item only when it should leave the canvas:

```elixir
socket = LivePanels.Canvas.Item.remove!(socket, "note-1")
```

From markup:

```heex
<button {remove_item_attrs(context)}>Close</button>
```

## Attach Child Items

Some UIs need an item to belong to another item, such as a badge, inspector, or
attached tool on a parent card.

```elixir
socket =
  socket
  |> LivePanels.Canvas.Item.add!(:comment, id: "comment-1")
  |> LivePanels.Canvas.Item.attach!("note-1", "comment-1")
```

Use attachment when the child should remain a canvas item but track a parent
relationship. Do not use it just to render normal markup inside a card; that is
item content.

## Capabilities Add Behavior

Capabilities describe what runtime behavior an item supports.

A note can be resizable and removable. A graph node can have ports. A drawing
vector item can expose vector state. A locked item can keep placement fixed.
`interaction: %{promote_on_focus: false}` lets an item become active without
automatic z-order promotion.

Application code should read the render view instead of duplicating capability
logic:

```heex
<.resize_handles :if={context.view.resizable?} />
```

For graph-capable items, render ports:

```heex
<button :for={port <- context.ports} {graph_port_anchor_attrs(port)}>
  {port.label}
</button>
```

The item attrs declare the capability. Your markup presents it.

## What LivePanels Owns

LivePanels owns item identity, placement, visibility, focus, selection,
z-order, capability checks, command handling, layout persistence, and projection.

Your application owns item shell markup, titles, buttons, menus, product-specific data,
and how users choose to create or organize items.
