# What LivePanels Is

LivePanels is a Phoenix LiveView canvas runtime.

It lets your app put LiveView-backed content on a two-dimensional canvas, then
move, resize, focus, select, persist, connect, draw on, and share that canvas
without making the browser the source of truth.

Use LivePanels when you want application content to behave like workspace items:

- notes, cards, dashboards, documents, terminals, inspectors, or media panels;
- graph nodes with typed ports and connectors;
- vector items, labels, arrows, frames, and annotations;
- shared boards where multiple participants see the same accepted canvas state;
- layouts that can be saved, restored, copied, pasted, undone, and redone.

LivePanels does not give you a product shell. It does not choose your navigation,
toolbar, visual style, node design, drawing palette, workflow language, or app
state. Your Phoenix app owns those.

The split is simple:

| Layer | Owns |
|---|---|
| Your application LiveView | routes, markup, item shells, controls, product language, CSS |
| LivePanels | canvas state, item placement, browser protocol, commands, projection |
| Item content LiveViews | the application UI that lives inside an item frame |

## The Core Mental Model

A LivePanels application does three things.

First, it declares a canvas and authors items in the canvas markup:

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

  use LivePanels.Canvas,
    pubsub: MyApp.PubSub
end
```

Second, it renders the canvas runtime:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns, class: "board-canvas")}>
  <:item :let={context} id="note-1" at={{80, 80}} size={{560, 360}} resize>
    <section class="note-card">
      <header {drag_handle_attrs(class: "note-card__title")}>
        Note
      </header>

      <div class="note-card__body">
        Put your LiveView or HEEx content here.
      </div>

      <.resize_handles :if={context.view.resizable?} />
    </section>
  </:item>
</.canvas_runtime>
```

Third, it sends changes through LivePanels helpers instead of mutating canvas
assigns directly:

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

```elixir
def handle_event("archive-note", %{"id" => id}, socket) do
  socket =
    socket
    |> LivePanels.Canvas.Item.merge_props!(id, %{archived: true})
    |> LivePanels.Canvas.Item.hide!(id)

  {:noreply, socket}
end
```

That is enough structure for LivePanels to keep browser gestures, server
commands, persistence, shared canvases, graph connectors, and vector items on the
same path.

## What The Browser Does

The browser makes pointer work feel immediate.

During a drag, resize, graph connection, point edit, or drawing gesture, the
browser can preview the interaction locally. When the gesture becomes a real
change, the browser sends a LivePanels command payload to the LiveView process.

That keeps the UI responsive without asking the browser to own accepted canvas
state.

## What The Server Does

The server owns accepted canvas state.

When a command arrives, LivePanels can validate the item, check capabilities,
run application policy, update state, persist layout when configured, and project the
new render data back to the browser.

This is why the same item can be moved by a drag handle, a toolbar button, a
server event, a graph layout helper, or a shared participant without creating
five different mutation paths.

## What Your App Does

The application owns the visible app.

You decide what an item looks like:

```heex
<section class={"card card-#{LivePanels.Item.prop(context.item, :tone, "neutral")}"}>
  <header {drag_handle_attrs()}>
    {context.item.label}
  </header>

  {context.content}
</section>
```

You decide where controls live:

```heex
<nav class="toolbar">
  <button {add_item_attrs(:note)}>Note</button>
  <button {layout_attrs(:grid)}>Layout</button>
  <button {undo_attrs()}>Undo</button>
</nav>
```

You decide what a graph connector means in your product. LivePanels only needs to
know that there is a connection between two declared endpoints. You decide what
a vector item means. LivePanels only needs to know that it is a committed
canvas item with vector state.

## The Rule To Remember

Use public LivePanels helpers for behavior. Use your own markup and CSS for
presentation.

That means:

- use `LivePanels.Canvas` to mount a canvas;
- use `canvas_runtime/1` and `canvas_runtime_attrs/2` to render it;
- use toolkit attrs such as `drag_handle_attrs/1`, `resize_handles/1`,
  `add_item_attrs/2`, graph attrs, and drawing attrs for browser commands;
- use owner modules such as `LivePanels.Canvas.Item`, `LivePanels.Graph`, and
  `LivePanels.Drawing` for server commands;
- avoid reaching into private runtime assigns, browser controllers, or
  compositor modules.

The rest of the guides show those pieces one at a time.
