Live editing, wired into a LiveView in one call.
Everything a shared document needs — placing edits that crossed on the wire, agreeing which version everyone is on, putting a session back in step after its socket drops, showing where other people's carets are — is here rather than in the host. It is not obvious code and it is not code worth writing twice; every part of it exists because something went visibly wrong without it.
Using it
def mount(%{"id" => id}, _session, socket) do
{:ok,
Leaf.Collab.join(socket,
room: MyApp.Notes.room_name(id),
editor_id: "note-editor",
identity: %{name: socket.assigns.current_user.name}
)}
endand in the template:
<.leaf_editor
id="note-editor"
content={@leaf_collab.content}
collaboration={@leaf_collab.collaboration}
/>That is the whole integration. join/2 attaches a handle_info hook, so the
host writes no message handling of its own.
What the hook consumes
The hook handles — and stops — {:leaf_operation, …}, {:leaf_awareness, …},
{:leaf_ready, …}, {:leaf_resync, …}, {:leaf_debug_state, …} and
{:leaf_changed, …}. A collaborating LiveView therefore does not receive
{:leaf_changed, …}: read the document from @leaf_collab.content, which is
kept current on every edit, local or remote. Anything the hook does not
recognise passes through to the host's own handle_info clauses untouched.
One collaborative editor per LiveView: join/2 owns the @leaf_collab
assign and the hook name.
What the host still owns
Starting a room per document and supervising it — Leaf has no opinion about
how many nodes you run or how you name processes. And saying where documents
live, which is Leaf.Collab.Store.
Turning it off
Not calling join/2 costs nothing. The editor does no collaboration work
unless it is asked to: no coordinates measured, no fingerprints taken, no
selection listener attached. Somebody using Leaf for a comment box pays for
none of this.
Summary
Functions
Join the document this room holds.
Everyone in the document, for a host that wants to list them.
Put the document back to its starting text, for everyone.
Functions
Join the document this room holds.
Options:
:room— the room process, started and supervised by the host:editor_id— theidgiven toleaf_editor:identity—%{name:, color:}, both optional. A host with signed-in users passes theirs so everyone sees a name rather than an identifier.:awareness— show other people's carets and selections. Defaults true.:debug— diagnostics. Defaults false; seeLeaf.Collab.Log.
Everyone in the document, for a host that wants to list them.
Put the document back to its starting text, for everyone.
Handles the part a host would forget: telling the other sessions. A reset that only changed the room left every other tab holding the old text, and the next reconciliation adopted it straight back — the reset undone by the people it was for.