A rich text editor for delivery notes, scope and long comments: a toolbar over a TipTap (ProseMirror)
surface, the way the canvas draws it (.editor, .editor-toolbar, .editor-btn, .editor-content).
The text lives in the browser (DevactionUI.Editor hook) and goes out as HTML and as Markdown in two hidden
inputs, <name>[html] and <name>[markdown], which fire input on every change so a form with phx-change
hears it. The initial content is value, as HTML. The root is ignored by LiveView patches, so the text is
never lost to a re-render; to replace it, give the editor a new id.
The server must treat what comes back as untrusted: sanitize the HTML before it is stored or shown.
Typing / at the start of a line opens a menu of blocks (slash, each %{key, label} with a key of
paragraph, h2, h3, bulletList, orderedList, taskList, blockquote, codeBlock or divider),
and @ opens the members (mentions, each %{id, label}); a chosen member stays in the text as
<span data-type="mention" data-id="...">, which is what the server reads to know who was mentioned.
compact is the comment form: a short surface, usually without a toolbar, and with send_on_enter the
command (or control) and Enter submit the form the editor is in.
Buttons come from the :tool slot, each with the command it runs (bold, italic, strike, code,
bulletList, orderedList, taskList, blockquote, indent, outdent, alignLeft, alignCenter,
alignRight, undo, redo, link, image (which sends a file to upload_url) or ai (which sends the Markdown as the event
improve to the component the editor is in, and takes back the text it answers with in editor:replace)), its label and its icon as the body. A tool with separator is
preceded by a divider. The library holds no icons and no language: the caller gives both.
Summary
Functions
Attributes
id(:string) (required)name(:string) (required) - prefix of the two inputs:name[html]andname[markdown].value(:string) - the initial content, as HTML. Defaults to"".label(:string) (required) - the accessible name of the text area.placeholder(:string) - Defaults to"".styles(:list) -[{label, value}]with valuesparagraph,h2,h3; empty hides the select. Defaults to[].style_label(:string) (required)words_label(:string) (required)slash(:list) -[%{key, label}]for the/menu; empty turns it off. Defaults to[].mentions(:list) -[%{id, label}]for the@menu; empty turns it off. Defaults to[].upload_url(:string) - where theimagetool sends a file (the image is the body of the request; it answers{url}); nil turns the tool off. Defaults tonil.upload_max(:integer) - the largest image, in bytes. Defaults to5242880.upload_errors(:map) - what to say when an upload fails:unsupported,too_large,failed. Defaults to%{}.ai_labels(:map) - texts of theaitool:working,doneandfailed; the tool sendsimprovewith the Markdown to the component the editor is in. Defaults to%{}.compact(:boolean) - the short comment form. Defaults tofalse.send_on_enter(:boolean) - ⌘↵ or Ctrl↵ submits the form. Defaults tofalse.link_label(:string) (required)class(:any) - Defaults tonil.- Global attributes are accepted.
phx-target: where theaitool sendsimprove. Supports all globals plus:["phx-target"].
Slots
tool- Accepts attributes:name(:string) (required)label(:string) (required)separator(:boolean)