# `Hakushi.Issho`
[🔗](https://github.com/hattoriai/hakushi/blob/v0.6.0/lib/hakushi/issho.ex#L112)

A shared canvas on [Issho](https://hex.pm/packages/issho): the canvas's
graph is a document of an Issho room, the canvas's LiveView is one of
the room's sessions, and Issho is the room's people and their cursors.

    <.canvas id="schema" graph={@graph} issho={[instance: MyApp.Issho, document: "schema:42"]} user={@current_user} />

`issho` takes the keys of every component (`Issho.Join`): `:instance`
and `:document` (required; the room is the part before the first
`"/"`), `:can` (`:read` for a viewer), `:people` (`false` hides the
canvas's avatars, on a page that lists the room's people), `:follow`
(`false` for a canvas that takes no part in following),
`:on_behalf_of` and `:claims`.

The first canvas to open the room's document gives it its graph, its
rules and its `on_change`; the others get the document as it is. Each
change goes to the document's authority, which checks the rules,
applies it, asks `on_change` (once, whoever made it), and only then
sends it to every canvas of the room. The document lasts as long as a
canvas has it open: your app's tables, which `on_change` writes, are
the truth, and the next canvas starts from the graph you give it.

`change/4` makes your app's own change, from anywhere (a job, another
LiveView), on a canvas someone has open. A place on the canvas is
`Hakushi.Issho.Place`.

## Events

Each change of a shared canvas is an event of its room (`Issho.Event`),
once the room has it: an agent waits for what people do on the canvas
(`wait_for_events` with `"canvas.*"`), a component of the page hears
it (`Issho.Live.subscribe/3`'s `:events`), and your app acts on it
(`Issho.Events.subscribe/2`). The event's `document` is the canvas's,
its `by` who made the change, and its name is the change's op:

  * `"canvas.added"`: nodes added (dropped, pasted, an undone delete),
    `%{nodes: [%{id: id, type: type}], edges: [%{id: id, source: id, target: id}]}`.
  * `"canvas.connected"`: edges drawn, `%{edges: [%{id: id, source: id, target: id}]}`.
  * `"canvas.reconnected"`: an edge's end moved to another node, the
    same, with its new ends.
  * `"canvas.moved"`: nodes moved or resized, `%{nodes: [%{id: id, x: x, y: y}]}`
    (with `width` and `height` for a resize, `parent` for a node put in
    a group or out), the nodes inside a moved node too.
  * `"canvas.updated"`: nodes or edges changed (a node's data, an
    edge's label), `%{nodes: [id], edges: [id]}`.
  * `"canvas.deleted"`: nodes and edges deleted, `%{nodes: [id], edges: [id]}`,
    with the nodes inside the nodes deleted and the edges that went
    with them.

A change of the paper makes none: Issho's `"document.changed"` says it,
as it says every change.

# `change`

```elixir
@spec change(atom(), String.t(), Hakushi.Change.t() | [Hakushi.Change.t()], keyword()) ::
  :ok | {:error, term()}
```

Makes your app's own change on a shared canvas: the way of a person's
(the rules, `on_change`), to everyone in the room, and not a person's to
undo. `changes` is a `Hakushi.Change` or a list of them, applied
together. Gives `:ok`, `{:error, reason}`, or `{:error, :not_running}`
when no canvas has the document open (then change your own copy, in
your database).

Options: `:author`, who makes it: a key (`"server"`, the default) or
your actor (`current_user`); `on_change` gets it as `:by`, an
`Issho.Identity`, as everyone's.

# `document`

```elixir
@spec document(Hakushi.Graph.t(), keyword()) :: {:ok, map()}
```

A shared canvas's document, for your policy's `document/2`
(`Issho.Policy`): what the room's canvas is, whoever opens it first (its
page, an embed of it in a document, an agent with no browser). Give it
the graph your app keeps, and the canvas's `rules` and `on_change` (as
`canvas/1` takes them): a canvas that opens the room gets the document
as it is, and its own `graph`, `rules` and `on_change` are not used.

    def document(_identity, "board:" <> id = room) do
      board = MyApp.Boards.get!(id)
      Hakushi.Issho.document(board.graph, on_change: &MyApp.Boards.on_change(room, &1, &2))
    end

Options: `:rules` (a list, as the canvas's), `:on_change` and `:canvas`
(the id `on_change` gets as `:canvas`, `"agent"` unless given).

# `graph`

```elixir
@spec graph(atom(), String.t()) :: Hakushi.Graph.t() | nil
```

The graph of a shared canvas as its room has it, or `nil` when no canvas has it open.

# `place`

The place module of this binding, or `nil`.

# `tools`

```elixir
@spec tools() :: [module()]
```

Hakushi's tools for agents (`Issho.Tool`): an agent reads a shared
canvas, points (the people see its pointer, with its name), selects,
adds nodes, drags them (they see the node go), connects them, changes
and deletes them, as a person does, through the canvas's rules and its
`on_change`. Give them to your app's model (`Issho.Tools`), or serve
them to MCP clients (`Issho.MCP`), with Issho's room tools:

    tools = Issho.Tools.room() ++ Hakushi.Issho.tools()

A tool names its canvas by its document (`"schema:42"`, the canvas's
`issho` `:document`);
an agent works on a canvas someone has open, or on one your policy's
`document/2` describes with `document/2`.

A person may pause or stop an agent at work (`Issho.Work`): then its
tools read the canvas and change nothing on it, not even where its
pointer is, and say why, as Issho's room tools do.

Its pointer travels to where it works, and drags, as a hand does: only
where it goes is sent, and each browser draws the way. For changes that
land at once: `config :hakushi, Hakushi.Issho, travel: false`.

On a canvas with ink (`Hakushi.Kits.Ink`), `draw_on_canvas` draws
strokes, or a ring around a node: the people see its pen draw each
stroke as they see a person's, and each stroke lands as a node when
the pen lifts. How it draws is the app's to set, and the agent's for
a drawing (its `live` argument):

    config :hakushi, Hakushi.Issho,
      ink: [
        live: true,    # false: the strokes land at once, unseen as drawn
        speed: 900,    # how fast the pen goes, in pixels of the canvas a second
        max: 8_000     # the most a drawing takes, in milliseconds
      ]

It draws only for someone who sees it: when no one in the room shows
the canvas, or each of them prefers reduced motion, or with `travel:
false` (Hakushi's or Issho's), the strokes land at once.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
