# Add Drawing Tools

Drawing support lets users create committed canvas vector items: freehand strokes,
polylines, arrows, lines, rectangles, ellipses, diamonds, triangles, and text.

This guide is the short integration path. For a full drawing-app track, read
[`Drawing Apps With LivePanels`](drawing/01-drawing-apps-with-livepanels.md)
after this guide.

The browser owns in-progress drafts so the pointer feels immediate. When the
gesture is finished, the browser sends one drawing command. The server validates
the payload, creates a canvas item, and projects it like other canvas state.

That gives vector items normal canvas behavior: move, resize, select, remove,
persist, undo, redo, copy, paste, and share.

## Use The Drawing Toolkit

Import the drawing toolkit on drawing-capable canvases:

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

  use LivePanels.Canvas,
    pubsub: MyApp.PubSub,
    domains: [:drawing]

  use LivePanels.Toolkit.Drawing
end
```

The drawing preset includes canvas helpers plus drawing controls, style JS helpers,
vector item rendering helpers, erase attrs, and point-edit attrs.

## Add Drawing Mode Controls

Use `set_cursor_mode_js/3` to switch the browser cursor mode:

```heex
<button
  phx-click={
    set_cursor_mode_js(@livepanels.canvas_id, :draw,
      tool: :freehand,
      style: %{
        color: "#1f2937",
        width: 3,
        fill_color: "none",
        stroke_style: "solid",
        opacity: 100,
        roundness: 0
      }
    )
  }
>
  Freehand
</button>

<button
  phx-click={
    set_cursor_mode_js(@livepanels.canvas_id, :draw,
      tool: :rectangle,
      style: %{
        color: "#1f2937",
        width: 2,
        fill_color: "#dbeafe",
        stroke_style: "solid",
        opacity: 100,
        roundness: 8
      }
    )
  }
>
  Rectangle
</button>

<button phx-click={set_cursor_mode_js(@livepanels.canvas_id, :erase)}>
  Erase
</button>
```

If the application mirrors cursor mode in LiveView assigns, pass the mode back into
the runtime component:

```heex
<.canvas_runtime {canvas_runtime_attrs(assigns, cursor_mode: @cursor_mode)} />
```

The browser still owns the active draft. The application mirror is for visible toolbar
state and LiveView patch consistency.

## Update Drawing Style

Use `set_draw_style_js/3` to change browser-owned draft style without changing
mode:

```heex
<button
  phx-click={
    set_draw_style_js(@livepanels.canvas_id, %{
      color: "#dc2626",
      width: 4,
      fill_color: "none",
      stroke_style: "dashed",
      opacity: 100,
      roundness: 0
    })
  }
>
  Red dashed
</button>
```

For committed vector items, use `update_style_attrs/3`:

```heex
<button
  :if={@selected_vector_item}
  {update_style_attrs(@selected_vector_item, %{
    color: "#2563eb",
    width: 3,
    fill_color: "none",
    stroke_style: "solid",
    opacity: 100,
    roundness: 0
  })}
>
  Blue
</button>
```

Draft style and committed vector item style are separate because a draft is not canvas
state until it is committed.

## Create Vector Items From Server Code

Use `LivePanels.Drawing.vector_item/4` for server-authored vector items, imports, tests, and
derived annotations:

```elixir
socket =
  LivePanels.Drawing.vector_item!(
    socket,
    :line,
    %{
      space: :world,
      anchor: :top_left,
      x: 120,
      y: 80,
      w: 240,
      h: 160
    },
    points: [%{x: 0, y: 0}, %{x: 240, y: 160}],
    style: %{
      color: "#444444",
      width: 3,
      fill_color: "none",
      stroke_style: "solid",
      opacity: 100,
      roundness: 0
    }
  )
```

Text vector items use the same item placement model:

```elixir
socket =
  LivePanels.Drawing.vector_item!(socket, :text, placement,
    text: "Review this",
    style: %{
      color: "#111827",
      width: 1,
      fill_color: "none",
      stroke_style: "solid",
      opacity: 100,
      roundness: 0
    }
  )
```

The command creates an item with vector state. It does not append SVG
directly to the DOM.

Committed vector items set `interaction: %{promote_on_focus: false}`. Selecting
or focusing a vector item keeps the canvas stack order intact, so drawing layers
can coexist with graph nodes and LiveView-backed items. Use normal z-order
selection commands when a user chooses to move vector items forward or back.

## Render Committed Vector Items

The standard `canvas_runtime/1` renders committed vector items in the world
vector layer. The `:item` slot can stay focused on HTML item frames:

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

For manual render loops, use the manual render helpers:

```heex
<LivePanels.Toolkit.Integration.Render.svg_vector_item
  id={"vector-item-#{context.item.id}"}
  item={context.item}
  selected={context.selected?}
  active={context.focused?}
/>
```

Applications can also render vector items manually from `LivePanels.Drawing.vector_state/1`
when they need a custom SVG contract.

## Edit Points And Remove Vector Items

Point-backed vector items such as freehand, polyline, arrow, and line store
points in vector state. Render point handles when selected:

```heex
<circle
  :for={{point, index} <- Enum.with_index(points)}
  cx={point.x}
  cy={point.y}
  r="5"
  {vector_point_handle_attrs(item, index)}
/>
```

Use erase target attrs on the SVG geometry you want erase mode to recognize:

```heex
<path
  d={path}
  {erase_vector_item_target_attrs(item)}
/>
```

Or render a normal removal control:

```heex
<button {erase_attrs(item)}>Remove vector item</button>
```

The server verifies that the target is a committed vector item before removing
it.

## Flatten Dense Vector Item Sets

For dense drawing workflows, flatten committed vector items into one canvas item:

```elixir
socket = LivePanels.Drawing.flatten!(socket, selected_vector_item_ids)
```

The flattened item renders as one unit. Its vector state keeps each source vector item as
a serialized source item record in the flattened item's local coordinate space,
so the runtime can act on the flattened item as one object until it is restored:

```elixir
socket = LivePanels.Drawing.unflatten!(socket, flattened_vector_item)
```

Applications can also run threshold-based flattening after commits or on their own
schedule:

```elixir
socket =
  LivePanels.Drawing.flatten_over_limit!(socket, 250,
    scope: :viewport
  )
```

Supported scopes are `:all`, `:owner`, `:viewport`, and `:selection`.

## Add Content With Normal Items

Drawing tools create vector items. For screenshots, remote references, nested
panels, dashboards, or app-specific content, declare a normal item type and use
the standard item command path:

```heex
<button {add_item_attrs(:reference)}>
  Reference
</button>
```

That item can still be selected, moved, resized, connected with ports, or given
vector capability state when the application wants it to participate in drawing workflows.
The drawing toolkit does not create a separate content-item path.

## Persistence, History, Clipboard, And Shared Mode

Committed vector items are canvas items. Because of that:

- moving and resizing vector items uses normal item commands;
- selection can include vector items;
- history can undo vector item creation and edits;
- layouts can restore vector items;
- clipboard can copy and paste vector items;
- shared canvases can coordinate committed vector item changes.

Browser drafts are intentionally not canonical state. In shared canvases the
runtime can broadcast throttled draw intent for live peer previews, while the
accepted `vector_item` command remains the source of durable canvas state. Pending
draw commits are kept client-side until the command ack resolves, with a
runtime option for app-provided draft persistence.

## What LivePanels Owns

LivePanels owns drawing command validation, vector item state, committed vector item
creation, draft-to-command protocol, drawing attrs, neutral vector item rendering
helpers, persistence/history participation, and shared coordination.

Your application owns the toolbar, palette, selected-vector-item inspector, SVG styling,
product labels, and any domain data attached to drawings.
