Collaboration & Review plugins
Six plugins that turn the editor into a real-world document workflow: slash commands, @mentions, Track Changes, threaded comments, revision history, and Yjs-backed presence. All ship in the standard license — no premium SKU required.
Slash commands
Type / on an empty line (or after whitespace) to open an inline command picker. 15 built-in blocks (H1–H3, bullet / numbered lists, quote, code, divider, table, image, link, emoji, today’s date) plus every registered AI Toolkit action.
Config
{
slashCommandEnabled: true, // default when this plugin is loaded
slashCommandIncludeAi: true // pull aiToolkitActions into the menu
}API
editor.slashCommands.register({ id, label, keywords, run });
editor.slashCommands.remove(id);
editor.slashCommands.list();
editor.slashCommands.open(); / .close();@Mentions
Trigger-character framework for @, #, [[wiki]], or any custom prefix. Async data sources (debounced, with a loading state). Atomic non-editable pills — backspace removes the whole chip.
Register a trigger
editor.mentions.register({
trigger: "@",
insertClass: "rte-mention-person",
search: function (query, done) {
fetch("/api/users?q=" + encodeURIComponent(query))
.then(r => r.json())
.then(done); // Array<{ id, name, color? }>
},
renderItem: function (user) { return user.name; },
renderInsert: function (user) { return "@" + user.name; }
});Track Changes (human suggesting mode)
Word-style suggesting. Insertions appear underlined in the current user’s color; deletions get a strikethrough (not removed until accepted). Sequential deletes merge into one span; backspacing inside your own just-typed insert shrinks it.
Config
{
trackChangesEnabled: true,
currentUser: { id: "maya", name: "Maya Patel", color: "#9333ea" },
reviewCanDecide: (entry, action, user) => user.role === "editor"
}Per-author accept / reject from the Review drawer. Use reviewCanDecide to enforce your application's reviewer policy; denied decisions leave the suggestion pending and raise review_decision_denied. Accepted and rejected suggestions store decidedBy for an audit trail. Works alongside AI suggestions — both end up in the same editor.reviewLedger store.
Revision history
Snapshot browser with a sandboxed preview iframe and an LCS-based line diff. Manual or debounced auto-snapshots. localStorage by default, or POST to a server endpoint.
Config
{
revisionHistoryEnabled: true,
revisionHistoryMaxEntries: 50,
revisionHistoryAutoSnapshotMs: 10000,
revisionHistoryUrl: "/api/revisions", // optional
revisionHistoryRequestHeaders: () => ({
"X-CSRF-Token": readCsrfToken()
}),
onRevisionHistorySyncError: ({ reason, status }) => {
reportSyncProblem(reason, status);
}
}Use a same-origin endpoint with your existing session and CSRF protection. Header values may be supplied by a function so a short-lived anti-forgery token can be read at request time. Do not place long-lived API keys in editor configuration.
API
editor.revisionHistory.snapshot(label?, metadata?);
editor.revisionHistory.promptAndSnapshot(defaultLabel?); // native prompt + snapshot
editor.revisionHistory.rename(id, label);
editor.revisionHistory.list(); // all snapshots
editor.revisionHistory.listNamed(); // user-labeled only
editor.revisionHistory.diff(idA, idB); // { lines, oldText, newText }
editor.revisionHistory.restore(id);
editor.revisionHistory.delete(id);
editor.revisionHistory.openBrowser();Yjs presence & concurrent typing
Live cursors, presence avatars, shared review ledger. Yjs is a peer dependency — the plugin never ships it. You load Yjs + a provider (WebSocket / WebRTC / Hocuspocus) and pass them in.
Attach
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";
const ydoc = new Y.Doc();
const provider = new WebsocketProvider("wss://collab.example.com", docId, ydoc);
editor.collab.attach({
doc: ydoc,
provider: provider,
user: { id: "alice", name: "Alice", color: "#2563eb" },
textSync: "crdt", // per-node Yjs CRDT; load crdt-engine.min.js first
requireCrdt: true // do not silently fall back to legacy snapshots
});textSync: "crdt". The per-node binding mirrors the editable DOM into a Y.XmlFragment, preserving concurrent text and formatting changes without the legacy whole-body reset. Call editor.collab.getStatus() after attach to record the active mode and any fallback reason.
Shared review ledger
AI suggestions, human tracked-change suggestions, and comments all funnel through a single store: editor.reviewLedger. That’s how the Review drawer can show them interleaved and accept / reject works the same way regardless of source.
API
editor.reviewLedger.add(entry); // { id, changeType, author, text, replies[], ... }
editor.reviewLedger.update(id, patch);
editor.reviewLedger.remove(id);
editor.reviewLedger.get(id);
editor.reviewLedger.list();
editor.reviewLedger.refreshPanel();
Threaded comments
Comments anchor to selection ranges and render as a highlight + sidebar thread. Composer, replies, resolve, delete.
Config
API