Kotoba's binding for Issho: a shared
editor's document is an Issho document, its engine Kotoba's semantic
operations (Kotoba.Collab.Document), and a person's place their
selection in it.
Name it as a document's engine through document/1, in your policy;
change a document from your own code with change/4; give agents
tools/0. A place is a selection (Kotoba.Issho.Place). A document's
changes are events of its room (events/2), and a Kotoba editor writes
Issho's comments (read_comment/1).
Summary
Functions
Changes a shared document from your own code (a job, an import),
through the same authority, checks and store as the editors' changes:
operations (Kotoba.Collab.Document), or a transaction map (with its
"id" and "base_revision") to retry one idempotently.
The field of a comment box (read_comment/1), from what the :input
slot of Issho.Components.comments/1 gives: the comment's words as a
document, a paragraph for each line, each "@" and name of someone
comment_prompts/2 offers a mention again, so that an edit starts
from the comment as it is.
The @ prompt of a comment box (read_comment/1): the room's people,
those here first, and mentionable ([%{key: "user:42", name: "Ada"}],
as Issho.Components.comments/1 has it), filtered as the person
types. A mention's id is the person's key. The same list for the
editor's prompts and for Kotoba.Live.handle_prompt/3, in the
LiveView that is in the room.
What a shared editor's document is, for your policy's document/2
(Issho.Policy), the same wherever it opens first.
The events of a change of a shared document (the spec's :events,
see Issho.Documents.open/4), which document/1 gives: once the
change is saved, they are events of the document's room, with the
document and the change's author, for agents (wait_for_events) and
for LiveViews (Issho.Live.subscribe/3's :events).
The place module of this binding, or nil.
A Kotoba editor as the comment box of Issho's comments
(Issho.Components.comments/1): its read, for the comment's words
and whom they mention from the box's form params. With
comment_prompts/2 and comment_field/3
Shows Issho's streams (Issho.Streams) as suggestions in this
LiveView's shared editors: an answer, a draft, an agent's proposal,
written by a process of your app or an agent, for this person (or the
room), at a place of an editor's document. Each shows in its editor as
a suggestion (see the Suggestions guide), after the
block of its place, as it comes; a page that mounts while it is
written picks it up where it got to. Accept puts it in the document,
as the person's change; Accept, Reject and Stop close the stream
("accepted", "rejected", "stopped"), and a page that goes away
does not. A stream closed elsewhere (accepted in another tab) goes.
Kotoba's tools for agents (Issho.Tool): an agent reads a shared
document, selects text (the people see its selection), types (they see
it written), replaces, deletes, formats, links, adds and moves
blocks, copies and pastes, as a person does in the editor, and its
changes merge with theirs; or it suggests text that the person takes
or leaves (suggest_text, see suggestions/4). Give them to your
app's model (Issho.Tools), or serve them to MCP clients
(Issho.MCP), with Issho's room tools
Functions
Changes a shared document from your own code (a job, an import),
through the same authority, checks and store as the editors' changes:
operations (Kotoba.Collab.Document), or a transaction map (with its
"id" and "base_revision") to retry one idempotently.
Options: :author, who made it, as the document's hook and history
know them: an actor (Issho.Actor, your user or an agent) or a key
("server" unless given, a service); :id, the transaction's.
@spec comment_field(map(), String.t(), [map()]) :: Phoenix.HTML.FormField.t()
The field of a comment box (read_comment/1), from what the :input
slot of Issho.Components.comments/1 gives: the comment's words as a
document, a paragraph for each line, each "@" and name of someone
comment_prompts/2 offers a mention again, so that an edit starts
from the comment as it is.
The @ prompt of a comment box (read_comment/1): the room's people,
those here first, and mentionable ([%{key: "user:42", name: "Ada"}],
as Issho.Components.comments/1 has it), filtered as the person
types. A mention's id is the person's key. The same list for the
editor's prompts and for Kotoba.Live.handle_prompt/3, in the
LiveView that is in the room.
What a shared editor's document is, for your policy's document/2
(Issho.Policy), the same wherever it opens first.
def document(_identity, "page:" <> _ = id),
do: Kotoba.Issho.document(store: {MyApp.CollabStore, MyApp.Repo})Options: :store (required), your Kotoba.Collab.Store and its
context; :policy, the document's checks (:features, :nodes,
:validate), as every accepted transaction must pass them; :events,
false for a document whose changes make no events of the room (see
events/2).
The events of a change of a shared document (the spec's :events,
see Issho.Documents.open/4), which document/1 gives: once the
change is saved, they are events of the document's room, with the
document and the change's author, for agents (wait_for_events) and
for LiveViews (Issho.Live.subscribe/3's :events).
"text.block_added":%{block: id, type: type, n: n}, a block that was not there: a new one, or one that came back (an undo)."text.block_removed":%{block: id, type: type}, a block that went."text.changed":%{block: id, type: type, n: n}, a block whose text changed, and that was there before and after. A change of format only is not one.
A block is a paragraph, a heading, a quote, a list item or a code
block. id is its node's id, which stays the same as the text
changes; n is its number, as read_text counts them (from 1), after
the change; type is what it is, as read_text says it
("paragraph", "h2", "bullet item"). The text is not in the
event, as the room's people may not all read each of its documents.
Issho.Live.subscribe("notebook:7", :self, events: ["text.*"])
def handle_info({Issho.Live, "event", _room, %Issho.Event{name: "text.changed"} = event}, socket) do
...
end
# The room's other messages, which :self gets too.
def handle_info({Issho.Live, _event, _key, _payload}, socket), do: {:noreply, socket}An app with events of its own gives both:
{:ok, spec} = Kotoba.Issho.document(store: {MyApp.CollabStore, MyApp.Repo})
{:ok, %{spec | events: &(Kotoba.Issho.events(&1, &2) ++ MyApp.Notebooks.events(&1, &2))}}
The place module of this binding, or nil.
A Kotoba editor as the comment box of Issho's comments
(Issho.Components.comments/1): its read, for the comment's words
and whom they mention from the box's form params. With
comment_prompts/2 and comment_field/3:
<Issho.Components.comments id="comments" room={@room} mentionable={@mentionable} read={&Kotoba.Issho.read_comment/1}>
<:input :let={input}>
<.kotoba
field={Kotoba.Issho.comment_field(input, @room, @mentionable)}
id={input.id}
label="Comment"
placeholder={input.placeholder}
features={[:mentions]}
prompts={Kotoba.Issho.comment_prompts(@room, @mentionable)}
/>
</:input>
</Issho.Components.comments>
def handle_event("kotoba:prompt", params, socket) do
prompts = Kotoba.Issho.comment_prompts(socket.assigns.room, socket.assigns.mentionable)
{:noreply, Kotoba.Live.handle_prompt(socket, params, prompts)}
endA comment keeps its words as text: a mention of the @ prompt is
"@" and the person's name in it, as Issho's own comment box writes
it, and its key is in the comment's mentions, so the person hears of
it ("comment.created", Issho.Inbox). Other mentions are their
label. Give the box only what text keeps, [:mentions]: bold or a
link's address would be lost.
@spec suggestions(Phoenix.LiveView.Socket.t(), term(), map() | keyword(), keyword()) :: Phoenix.LiveView.Socket.t()
Shows Issho's streams (Issho.Streams) as suggestions in this
LiveView's shared editors: an answer, a draft, an agent's proposal,
written by a process of your app or an agent, for this person (or the
room), at a place of an editor's document. Each shows in its editor as
a suggestion (see the Suggestions guide), after the
block of its place, as it comes; a page that mounts while it is
written picks it up where it got to. Accept puts it in the document,
as the person's change; Accept, Reject and Stop close the stream
("accepted", "rejected", "stopped"), and a page that goes away
does not. A stream closed elsewhere (accepted in another tab) goes.
on_mount Issho.Live
def mount(_params, _session, socket) do
issho = [instance: MyApp.Issho, document: "notebook:7/intro"]
socket = assign(socket, issho: issho, user: socket.assigns.current_user)
{:ok, Kotoba.Issho.suggestions(socket, socket.assigns.user, %{"intro_editor" => issho})}
end
# <.kotoba field={@form[:intro]} id="intro_editor" issho={@issho} user={@user} assist={true} />editors maps each editor's id to its issho options (the
:instance and the :document). The editors need assist (true
for no menu), for the person's answers to reach the LiveView. The
LiveView joins each document's room (Issho.Live.join/4, with
on_mount Issho.Live), and handles the streams' messages and the
editors' kotoba:suggestion events itself (attach_hook/4).
A stream's place is %{"doc" => document, "at" => at}, at a node
id (%{"node" => id}, a block's or a text's) or a Kotoba selection
(Kotoba.Issho.Place): the suggestion goes after its block. An
editor shows one at a time; the others wait their turn. A stream's
label is its meta's "label", else its kind.
Options:
:kinds- the kinds of stream that are suggestions (all by default).:display-:panel(the default) or:inline, asKotoba.Live.stream_start/4.:pass-truegives yourhandle_info/2the messages of the room's other streams; by default they stop here.
With Kotoba.Issho.tools/0, an agent writes one with suggest_text.
Without Issho, Kotoba.Live.stream_resume/5 and the Suggestions
guide's recipe do the same with a process of your app.
@spec tools() :: [module()]
Kotoba's tools for agents (Issho.Tool): an agent reads a shared
document, selects text (the people see its selection), types (they see
it written), replaces, deletes, formats, links, adds and moves
blocks, copies and pastes, as a person does in the editor, and its
changes merge with theirs; or it suggests text that the person takes
or leaves (suggest_text, see suggestions/4). 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() ++ Kotoba.Issho.tools()A tool names its document by id ("notebook:7/intro"); the room is the
part before the "/". Text is quoted, not counted: select_text with
the words, then link_text with no text links the selection.
An agent moves as a person does, so that the people follow it: its caret goes to where it works, it selects as a drag does and holds a moment before it changes the selection, and it types at a person's pace. Only where it goes is sent; each browser draws the way there. For changes that appear at once:
config :kotoba, Kotoba.Issho, pace: 0, travel: falsepace: the milliseconds it takes to type a character (25; a long text
is typed in at most 8 seconds). travel: whether it goes to where it
works and drags (true).