Find The API You Need

Copy Markdown View Source

Most LivePanels application code uses a focused set of public modules.

Start with LivePanels.Canvas, render with the toolkit helpers, then reach for graph, drawing, persistence, shared, or manual integration APIs only when that canvas needs them.

flowchart TD
  App["Application LiveView"]
  Canvas["LivePanels.Canvas\nmounts the runtime"]
  Toolkit["LivePanels.Toolkit.*\ncomponents and DOM attrs"]
  Items["LivePanels.Canvas.Item\nitem lifecycle and placement"]
  Data["LivePanels.Canvas.Item\nitem props"]
  Selection["LivePanels.Canvas.Selection\nselection and bulk actions"]
  Layout["LivePanels.Canvas.Layout\nplan item layouts"]
  History["LivePanels.Canvas.History\nundo and redo"]
  Clipboard["LivePanels.Canvas.Clipboard\ncopy and paste"]
  Read["LivePanels.Canvas.Read\nread current projection"]
  Graph["LivePanels.Graph\nports, connectors, graph layout"]
  Drawing["LivePanels.Drawing\nvector items and elements"]
  Shared["LivePanels.Canvas.Shared.*\nshared canvas coordination"]
  Manual["LivePanels.Toolkit.Integration.*\nescape hatches"]

  App --> Canvas
  App --> Toolkit
  App --> Items
  App --> Data
  App --> Selection
  App --> Layout
  App --> History
  App --> Clipboard
  App --> Read
  App --> Graph
  App --> Drawing
  Canvas --> Shared
  App --> Manual

Start Here

Use LivePanels.Canvas in the LiveView that owns the canvas route:

defmodule MyAppWeb.BoardLive do
  use MyAppWeb, :live_view

  use LivePanels.Canvas,
    pubsub: MyApp.PubSub

  use LivePanels.Toolkit.Canvas

  def render(assigns) do
    ~H"""
    <.canvas_runtime {canvas_runtime_attrs(assigns)}>
      <:item id="note-1" type="note" at={{120, 80}} size={{520, 320}}>
        <article class="note-card">Write ordinary HEEx here.</article>
      </:item>
    </.canvas_runtime>
    """
  end
end

use LivePanels.Canvas attaches the runtime. Add use LivePanels.Toolkit.Canvas, Graph, or Drawing when the LiveView renders toolkit helper components or attrs.

Toolkit Imports

Toolkit modules are use presets. They do not change the state model.

ToolkitUse when
LivePanels.Toolkit.CanvasYou need runtime helpers, item frame helpers, item controls, selection, layout, history, clipboard, and viewport JS.
LivePanels.Toolkit.GraphYou need canvas controls plus graph ports, connectors, selection, and fit helpers.
LivePanels.Toolkit.DrawingYou need canvas controls plus drawing modes, vector item controls, and point editing helpers.

Most apps should use a toolkit preset instead of hand-writing protocol attrs.

Command Owners

Commands stay on the module that owns the vocabulary:

WorkModule
Add, remove, present, place, move, resize, focus, attach, detachLivePanels.Canvas.Item
Replace, merge, delete, or clear item propsLivePanels.Canvas.Item
Select, clear, align, distribute, z-order selected itemsLivePanels.Canvas.Selection
Layout commands and reserved layout spaceLivePanels.Canvas.Arrange
Layout planning contracts and adaptersLivePanels.Canvas.Layout
Undo and redoLivePanels.Canvas.History
Copy and pasteLivePanels.Canvas.Clipboard
Store and navigate viewport anchorsLivePanels.Canvas.Viewport
Connect, disconnect, reconnect, fit, and lay out graph connectorsLivePanels.Graph
Create and edit vector items or drawing-owned elementsLivePanels.Drawing

Every command module accepts either a canvas LiveView socket or a LivePanels.Canvas.Transaction context:

socket =
  LivePanels.Canvas.transaction!(socket, fn tx ->
    tx
    |> LivePanels.Canvas.Item.move_by!("panel-a", {24, 0})
    |> LivePanels.Canvas.Item.merge_props!("panel-a", %{accent: "red"})
    |> LivePanels.Graph.connect!(from: {"panel-a", "out"}, to: {"panel-b", "in"})
  end)

Use transactions when several changes should become one command history step.

Browser Controls

Application-rendered controls should use toolkit attrs:

<button {add_item_attrs(:note)}>New note</button>
<button {remove_item_attrs(context)}>Close</button>
<button {layout_attrs(:grid)}>Layout</button>
<button {undo_attrs()}>Undo</button>

Those attrs emit the LivePanels protocol payloads expected by the runtime. They also keep your visible markup free to change.

Read Side

Use LivePanels.Canvas.Read when application code needs current runtime information:

items = LivePanels.Canvas.Read.items(socket)
selection = LivePanels.Canvas.Read.selection(socket)
last = LivePanels.Canvas.Read.last_command(socket)
trace = LivePanels.Canvas.Read.command_trace(socket)

Use root helpers for runtime identity and subscriptions:

canvas_id = LivePanels.canvas_id(socket)
client_id = LivePanels.client_id(socket)
mode = LivePanels.mode(socket)

:ok = LivePanels.subscribe(socket, :events)

Escape Hatches

Reach for these only when the normal path is not enough:

NeedAPI
Mount the canvas runtime without the use macroLivePanels.Canvas.mount_runtime/4
Add runtime items after mountLivePanels.Canvas.Item.add/3
Render runtime-created item bodiesid-less <:item :let={context}> slot
Authorize app-specific commandsLivePanels.Canvas.Policy
Persist layouts outside the default storeLivePanels.Canvas.LayoutStore
Refresh rendered item streams without mutating canvas stateLivePanels.Canvas.Render
Run shared canvases in a clusterLivePanels.Canvas.Shared.Backend and LivePanels.Canvas.Shared.Adapter
Build manual protocol attrs or render loopsLivePanels.Toolkit.Integration.ProtocolAttrs, LivePanels.Toolkit.Integration.Render

If an app can use a toolkit helper or owner module, prefer that over an escape hatch.