A shared canvas on 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} live={{MyApp.Issho, "schema:42"}} user={@current_user} />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/3 makes your app's own change, from anywhere (a job, another
LiveView), on a canvas someone has open.
Summary
Functions
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 (by is nil). Gives :ok, {:error, reason}, or
{:error, :not_running} when no canvas has the room open (then change
your own copy, in your database).
A shared canvas's document, for your policy's document/2
(Issho.Policy): what the room's canvas is when an agent opens it
before anyone has it open in a browser. Give it the graph your app
keeps, and the canvas's rules and on_change (as canvas/1 takes
them), so that the agent's changes go the way of everyone's; a canvas
that opens the room later gets the document as it is.
The graph of a shared canvas as its room has it, or nil when no canvas has it open.
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
Functions
@spec change(atom(), String.t(), Hakushi.Change.t()) :: :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 (by is nil). Gives :ok, {:error, reason}, or
{:error, :not_running} when no canvas has the room open (then change
your own copy, in your database).
@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 when an agent opens it
before anyone has it open in a browser. Give it the graph your app
keeps, and the canvas's rules and on_change (as canvas/1 takes
them), so that the agent's changes go the way of everyone's; a canvas
that opens the room later gets the document as it is.
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))
endOptions: :rules (a list, as the canvas's), :on_change and :canvas
(the id on_change gets as :canvas, "agent" unless given).
@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.
@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 room (live={{MyApp.Issho, "schema:42"}});
an agent works on a canvas someone has open, or on one your policy's
document/2 describes with document/2.