# Use Shared Canvases

Shared mode lets multiple LiveView clients work against the same accepted
canvas state.

The coordination machinery changes. The command model does not. Adding an item,
moving a graph node, committing a vector item, restoring layout, or removing a
selection still goes through the same canvas command path.

Use this guide to add shared mode to an app. Use `Operate Shared Canvases` for
production backend and recovery details.

## Start With Local Mode

Local mode is the default:

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

Each mounted canvas LiveView owns its own state unless your app deliberately
shares a canvas id and backend.

Start with local mode while building the item model and UI. Move to shared mode
when users should collaborate in the same room, board, workflow, or document.

## Enable Shared Mode

Shared mode needs:

- the LivePanels application child in your supervision tree;
- a PubSub module;
- `session: [mode: :shared]`;
- a stable `session` id;
- a shared backend.

For a one-node app:

```elixir
children = [
  {Phoenix.PubSub, name: MyApp.PubSub},
  LivePanels.Application
]
```

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

  use LivePanels.Canvas,
    pubsub: MyApp.PubSub,
    session: [
      mode: :shared,
      id: {:param, "room_id"},
      backend: LivePanels.Canvas.Shared.Local
    ]
end
```

Now clients that visit the same route parameter share the same canvas state.

## Choose A Canvas Id

The canvas id is the coordination key.

Good canvas ids are stable and product-owned:

```elixir
session: [id: {:param, "board_id"}]
```

or:

```elixir
session: [id: "support-war-room"]
```

Avoid per-tab ids when you want collaboration. Avoid ids derived from display
state that can change during a session.

If the canvas id identifies a product record, authorize access to that record in
your Phoenix route, mount logic, or canvas policy. Shared mode coordinates
canvas state; it does not replace app-level authorization.

## Participants

Each connected browser has a participant identity and per-participant runtime
projection.

Use read helpers when the application needs participant information:

```elixir
participants = LivePanels.Canvas.Read.participants(socket)
client_id = LivePanels.client_id(socket)
```

Selection, history, clipboard, viewport, and focus can be actor-scoped even
while the canvas state is shared. That lets two users inspect different parts of
the same room without fighting over every local UI detail.

## What Gets Shared

Shared mode coordinates accepted canvas state:

- item add, remove, placement, visibility, props, and z-order;
- graph connectors and graph selection commands;
- committed vector items and vector item edits;
- layout restore;
- persistent shared state snapshots or deltas;
- participant presence metadata.

It does not automatically make all application UI state shared. Your toolbar mode,
open inspector, filter controls, unsaved form fields, and route-level app state
remain application concerns unless you explicitly model them as canvas
state or app props.

## Persistence

Configure a durable layout store when shared canvas state should recover after
process or server restart:

```elixir
defmodule MyApp.CanvasLayouts do
  use LivePanels.Canvas.LayoutStore.Ecto,
    repo: MyApp.Repo,
    schema: MyApp.LivePanelsCanvasLayout
end

use LivePanels.Canvas,
  pubsub: MyApp.PubSub,
  session: [
    mode: :shared,
    id: {:param, "room_id"},
      backend:
        {LivePanels.Canvas.Shared.Local,
         layout_store: MyApp.CanvasLayouts}
  ]
```

For local development, the default ETS store is useful. For production shared
canvases, persist to a database-backed store and run the shared backend health
check against that store.

## Subscribe To Runtime Streams

Application code can subscribe to canvas runtime events:

```elixir
def mount(_params, _session, socket) do
  if connected?(socket), do: LivePanels.subscribe(socket, :events)
  {:ok, socket}
end
```

Use subscriptions for app integration, analytics, status display, or external
process coordination. Use commands for mutations.

## Graph And Drawing In Shared Mode

Graph and drawing do not need separate collaboration models.

Graph connectors and drawing primitives are canvas items after commit. Because
of that, shared mode can coordinate them through the same accepted command path
used by normal item movement.

In-progress drawing drafts are still not canonical state, but shared canvases
broadcast throttled draw intent so other participants can see live previews.
The final vector item is shared through the accepted `add_vector_item`
command.

## What LivePanels Owns

LivePanels owns shared coordinator startup, command routing, accepted state
fanout, participant tracking, recovery hooks, and projection to each client.

Your app owns room routing, user authorization, durable layout storage,
deployment topology, and product-specific collaboration behavior.
