Getting started
Rich text editor
@junco/editor is a TipTap editor with Junco's look: a formatting toolbar, a / command menu and markdown shortcuts. Give it a Y.Doc and it's collaborative.
Use it#
npm i @junco/editor @junco/react @junco/tailwind yjsimport { Editor } from "@junco/editor";
<Editor content="<p>Hello</p>" onUpdate={(editor) => save(editor.getJSON())} />Markdown shortcuts work as you type: # for headings, - or 1. for lists, > for quotes, ``` for code, **bold**, _italic_. Type / on a line for the command menu; arrow keys move, Enter picks, Escape closes.
Edit together#
Pass a doc and everyone with the same document edits it live; pass a provider with an Awareness and their carets and selections show with their names. A Towhee room is both. Undo only undoes your own changes.
import { createClient } from "@towhee/client";
import { Editor, editorText } from "@junco/editor";
import { CollabProvider, CommentsPanel, HistoryPanel, towheeAdapter } from "@junco/collab";
const client = createClient({ url: "wss://towhee.example.com", authEndpoint: "/api/towhee-token" });
const room = client.enter("doc:42");
// The room is the Y.Doc's home and holds everyone's carets (its awareness).
<Editor doc={room.doc} provider={room} />
// Comments and history for the same document; textOf turns on version compare.
const adapter = towheeAdapter({ room, inbox: client.inbox, textOf: (doc) => editorText(doc), restoreOptions: { types: { default: "xml" } } });
<CollabProvider adapter={adapter}>
<CommentsPanel />
<HistoryPanel />
</CollabProvider>See it running in the collaborative docs editor: comments, an inbox, version history with compare, and a share dialog around one shared document.
Slash commands#
The defaults are text, three headings, bulleted and numbered lists, quote, code block and divider. Add your own:
import { Editor, defaultSlashCommands, type SlashCommand } from "@junco/editor";
const callout: SlashCommand = {
id: "callout",
label: "Callout",
keywords: "note tip",
run: (editor) => editor.chain().focus().setBlockquote().insertContent("Note: ").run(),
};
<Editor slashCommands={[...defaultSlashCommands, callout]} />Props#
| Prop | What it does |
|---|---|
content | Starting HTML or TipTap JSON, when not collaborating |
doc | A Y.Doc to edit together; the document is then the content |
field | The XmlFragment in doc this editor owns (default "default") |
provider | Anything with an awareness (a Towhee room, y-websocket, Hocuspocus) for shared carets |
user | Your name and color on your caret. Towhee sets these from the token |
placeholder | Shown on an empty document |
editable | False for read-only (hides the toolbar and menu) |
toolbar | Show the formatting toolbar (default true) |
slashCommands | Commands for the / menu; [] turns it off |
extensions | More TipTap extensions |
onReady, onUpdate | Get the TipTap editor, to read or change content |
label | Accessible name of the editing area (default "Document") |
className, classNames | root, toolbar and content slots |
Also exported: Toolbar and SlashMenu for your own TipTap setup, defaultSlashCommands, and editorText(doc, field), which turns a document into plain text (for version compare or search).
Accessibility
The editing area is a labeled multi-line textbox, the toolbar is a toolbar with pressed states and shortcuts in its tooltips, and the / menu is a listbox the editor points at with aria-activedescendant. Other people's cursors show their name in a neutral tag with a colored border, so text contrast holds for any color.