■ pointy/docs

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

  1. Create a developer account on the sync host and register a key (JWKS endpoint or static key).
  2. npm i @pointyink/hugerte hugerte
  3. Mint a short-lived JWT per end-user from your backend (see Authentication).
  4. Call attachRogue(editor, { wsUrl, docId, getToken }) in HugeRTE's setup.
  5. Two users on the same docId converge.

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

OptionTypeDescription
wsUrlstringWebSocket URL of the sync host.
docIdstringDoc 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() => voidFired once the doc is loaded and rendered.
onProgress(p) => voidInitial-sync progress 0..1, for a load bar on large docs.
readOnlybooleanRender + 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) => voidEach 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'