# Render Item Content With LiveViews

Item content is what appears inside a canvas item frame.

The item owns runtime behavior: identity, placement, visibility, resize rules,
selection, ports, persistence, and shared coordination. The content owns the UI
inside the frame.

For many apps, item content is another LiveView.

## Define A Content LiveView

Use `LivePanels.Item.Content.LiveView` for a LiveView that will run inside an item:

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

  use LivePanels.Item.Content.LiveView,
    pubsub: MyApp.PubSub

  @impl true
  def mount_item(socket, props) do
    {:ok,
     socket
     |> assign(:body, LivePanels.Item.prop(props, :body, ""))
     |> assign(:saved?, true)}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <form phx-submit="save">
      <textarea name="body" phx-change="edit">{@body}</textarea>
      <button type="submit">Save</button>
    </form>
    """
  end

  @impl true
  def handle_event("edit", %{"body" => body}, socket) do
    {:noreply, assign(socket, body: body, saved?: false)}
  end
end
```

`mount_item/2` receives the item props from the canvas declaration or add
command. Use it to initialize content assigns.

## Use The LiveView Content Adapter

Source-owned items declare the content LiveView directly on the item:

```heex
<:item
  id="note-1"
  type="note"
  at={{120, 80}}
  size={{520, 320}}
  live_view={MyAppWeb.NoteLive}
  props={%{body: "Start writing..."}}
>
</:item>
```

Runtime-created items use the same content adapter in the command payload. The
application renders their frame and places the supervised content with
`context.content`:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns)}>
  <:item :let={context}>
    <article class="note-frame">
      <header {drag_handle_attrs()}>{context.item.label}</header>
      <section>{context.content}</section>
      <.resize_handles :if={context.view.resizable?} />
    </article>
  </:item>
</.canvas_runtime>
```

The content LiveView should not render the outer draggable frame. Keep the frame
in the canvas item renderer so every item uses the same canvas behavior.

## Send Commands From Content

Item content can ask the canvas to do item-scoped work through
`LivePanels.Item.Content.LiveView.Commands`.

For example, a note can remove itself:

```elixir
def handle_event("remove", _params, socket) do
  {:noreply, LivePanels.Item.Content.LiveView.Commands.remove(socket)}
end
```

Or it can update its application item props:

```elixir
def handle_event("save", %{"body" => body}, socket) do
  socket =
    socket
    |> LivePanels.Item.Content.LiveView.Commands.merge_props(%{body: body})
    |> assign(body: body, saved?: true)

  {:noreply, socket}
end
```

Use item-content helpers when the content wants to affect its own canvas item.
Use canvas application commands when the application is coordinating multiple items or app
state.

## Message Between App And Content

Item content is a LiveView process. That matters for lifecycle and messaging.

The runtime tracks the child process while the item exists. When an item is
removed, the runtime can notify and terminate the content. When a content
process exits, the item can remain in the canvas while the content is vector itemed
unavailable.

Applications can subscribe to item or canvas runtime topics when they need external
coordination:

```elixir
:ok = LivePanels.subscribe(socket, {:item, "note-1"})
:ok = LivePanels.subscribe(socket, :events)
```

Prefer explicit commands for mutations. Use messages for domain events, status
updates, or integration with the rest of your Phoenix app.

## Use Non-LiveView Content When It Fits

Not every item needs a child LiveView.

LivePanels item declarations can also represent static rendered content, remote
embeds, nested canvases, or custom renderer modules. Use a LiveView when the
inside of the item needs LiveView lifecycle, events, assigns, subscriptions, or
stateful UI.

Use simpler content when the item is mostly presentation or when the application
already owns the state.

## Keep The Boundary Clear

Good item content:

- renders the UI inside the item;
- handles its own form events and local state;
- uses item-content helpers for its own item commands;
- leaves placement, drag, resize, focus, selection, and z-order to the canvas.

Good application item frames:

- render the card/window/chrome around content;
- attach `drag_handle_attrs/1`, resize handles, remove controls, and graph or
  drawing attrs;
- read `context.view` instead of reimplementing capability checks;
- keep app-specific style and product language outside LivePanels internals.

That boundary is what lets the same item content work in local canvases, shared
canvases, graph nodes, nested canvases, and persisted layouts.
