HugeRTE plugin
@pointyink/hugerte is a drop-in binding that makes a
HugeRTE editor multi-user —
real-time, offline, and conflict-free — with server-side doc storage. HugeRTE is
the MIT-licensed fork of TinyMCE, so there is no GPL/pay licensing question: bring
your own HugeRTE, and Pointy handles the sync.
Quickstart
- Create a developer account on the sync host and register a key (JWKS endpoint or static key).
npm i @pointyink/hugerte hugerte- Mint a short-lived JWT per end-user from your backend (see Authentication).
- Call
attachRogue(editor, { wsUrl, docId, getToken })in HugeRTE'ssetup. - Two users on the same
docIdconverge.
Install
npm i @pointyink/hugerte hugerte
hugerte is a peer dependency — load the editor from npm or self-host
its assets. The plugin ships no editor code.
No build step (script tag)
You can skip npm and load the plugin as a prebuilt, self-contained ESM bundle (the engine + WASM are inlined) straight from the sync host:
<!-- load HugeRTE however you host it (npm, your own server) -->
<script src="hugerte.min.js"></script>
<script type="module">
import { attachRogue } from 'https://sync.pointy.ink/static/hugerte-plugin.js'
window.hugerte.init({
target: '#editor',
base_url: '/path/to/hugerte-assets',
custom_undo_redo_levels: 0,
setup: async (editor) => {
await attachRogue(editor, { wsUrl, docId, getToken })
},
})
</script>
The bundle is served from the sync host at /static/hugerte-plugin.js.
Same attachRogue call, no bundler, no npm install.
Usage
import hugerte from 'hugerte'
import { attachRogue } from '@pointyink/hugerte'
hugerte.init({
target: '#editor',
custom_undo_redo_levels: 0, // the CRDT owns undo — disable HugeRTE's
plugins: 'lists link table',
toolbar: 'bold italic | bullist numlist',
setup: async (editor) => {
await attachRogue(editor, {
wsUrl: 'wss://sync.pointy.ink/ws',
docId: '9f3a...-uuid',
getToken: () => fetch('/api/token').then(r => r.json()).then(t => t.token),
onStatus: (s) => console.log(s),
})
},
})
attachRogue waits for editor init, builds the engine against the
editor's DOM, and — when a wsUrl, docId, and
getToken are provided — stands up the sync client for you. Reconnect
backoff, the outgoing op queue, ack handling, and replay are all handled
internally. It also takes over undo/redo (removes HugeRTE's shortcuts, overrides
the Undo/Redo commands) and registers
rogueundo/rogueredo toolbar buttons you can drop into
your toolbar string.
Options
| Option | Type | Description |
|---|---|---|
wsUrl | string | WebSocket URL of the sync host. |
docId | string | Doc UUID to sync. A token is scoped to exactly one doc. |
getToken | () => Promise<string> | Returns a short-lived JWT minted by your backend (see Authentication). |
onStatus | (status) => void | 'connecting' | 'connected' | 'disconnected'. |
onReady | () => void | Fired once the doc is loaded and rendered. |
onProgress | (p) => void | Initial-sync progress 0..1, for a load bar on large docs. |
readOnly | boolean | Render + sync as a viewer; never allocates a replica or writes. |
comments | { render, onClick } | Enables comment annotations; the endpoint + JWT are derived internally. |
suggestions | { render, onClick } | Enables suggested edits (propose-and-accept changes). |
onCommit | (payload) => void | Each emitted local op payload, for your own instrumentation. |
Without a wsUrl the binding runs offline-only; you can still pass
your own onCommit to intercept and transport ops yourself.
Return value
const { node, comments, suggestions, deleteDoc } = await attachRogue(editor, opts)
// node.mergeOp(payload) — feed a remote op into the engine
// node.undo() — undo one local step
// node.redo() — re-apply the last undone step
// node.destroy() — detach listeners and free the WASM instance
// comments.create(msg) — comment on the current selection (when enabled)
// suggestions.create(t) — propose a replacement for the current selection
Auth
getToken returns a short-lived JWT minted by your backend
and signed with your private key. See the authentication
guide for registering keys (JWKS or static) and minting tokens. Pointy
verifies the signature against the key registered for your account; it never
stores your users or your signing key.
Offline & reconnect
Edits are applied locally first, queued in IndexedDB, and sent when the socket is
up. On reconnect the client re-syncs, replays anything unacked, and converges.
onStatus lets you surface connection state in your UI.
Undo & redo
The CRDT is the single undo source of truth. Set
custom_undo_redo_levels: 0 in your hugerte.init to disable
HugeRTE's own undo stack. Undo/redo are per-author: Cmd/Ctrl+Z undoes
your edits, Cmd/Ctrl+Shift+Z and Ctrl+Y redo them. Wire the registered
rogueundo/rogueredo buttons into your toolbar string to
expose them.
toolbar: 'rogueundo rogueredo | bold italic | bullist numlist'