Collaboration History
This extension enhances your collaborative editor experience by introducing a version history feature. With it, you have the flexibility to manually or automatically generate document versions. Not only can you restore previous iterations, but you can also derive new versions from older ones.
Installation
Pro Extension
This extension requires a valid subscription in an eligible plan and a running Tiptap Cloud instance. To install the extension you need access to our private registry, set this up first.
Once done, you can install the extension from our private registry:
npm install @tiptap-pro/extension-collaboration-history @hocuspocus/transformer
Note:
The @hocuspocus/transformer
package is required for transforming Y.js binary to Tiptap JSON content. The package also requires a Y.js installation which is required for collaboration. If you don't have it installed, run npm install yjs
in your project.
This should automatically happen if you are using NPM (as it automatically resolves peer dependencies).
Settings
Setting | Type | Default |
---|---|---|
provider | TiptapCollabProvider |
null |
onUpdate | function |
() => {} |
Storage
Key | Type | Description |
---|---|---|
versions | array<Version> |
The array of versions that are stored in the history. |
currentVersion | number |
The current version. |
latestVersion | number |
The latest version. |
autoVersioning | boolean |
autoVersioning |
Commands
Command | Description |
---|---|
saveVersion | Creates a new version with a given title |
toggleVersioning | Toggles auto versioning for this document |
revertToVersion | Revert to a specific version, can create a new revert version with optional title |
Examples
Basic Setup
const provider = new TiptapCollabProvider({
// ...
})
const editor = new Editor({
// ...
extensions: [
// ...
CollabHistory.configure({
provider,
})
],
})
Get Version Update Data and save it into a variable
let currentVersion = 0
let latestVersion = 0
let autoversioningEnabled = false
let versions = []
const provider = new TiptapCollabProvider({
// ...
})
const editor = new Editor({
// ...
extensions: [
// ...
CollabHistory.configure({
provider,
onUpdate(payload) {
currentVersion = payload.currentVersion
latestVersion = payload.latestVersion
versions = payload.versions
autoversioningEnabled = payload.autoVersioning
}
})
],
})
Access version data directly from storage
const provider = new TiptapCollabProvider({
// ...
})
const editor = new Editor({
// ...
extensions: [
// ...
CollabHistory.configure({
provider,
})
],
})
const latestVersion = editor.storage.collabHistory.latestVersion
const currentVersion = editor.storage.collabHistory.currentVersion
const versions = editor.storage.collabHistory.versions
const autoversioningEnabled = editor.storage.collabHistory.autoVersioning
Create a new version manually
editor.commands.saveVersion('My new custom version')
Toggle autoversioning on document
editor.commands.toggleVersioning()
Revert to a specific version
editor.commands.revertToVersion(4)
Revert to a specific version with a custom name
editor.commands.revertToVersion(4, 'Revert to version')
Revert to a specific version with a custom name and a custom name for the previous version
editor.commands.revertToVersion(4, 'Revert to version', 'Unversioned changes before revert')
Implementing version previews for your editor
The examples discussed above will directly modify the document and do not provide local-only previews of a version. Therefore, it is necessary to create your own frontend solution for this requirement. You can leverage the stateless messaging system of the TiptapCloudProvider
to request a specific version from the server.
Start by attaching a listener to the provider:
// Import the getPreviewContentFromVersionPayload helper function (refer to details below)
import { watchContent } from '@tiptap-pro/extension-collaboration-history'
// Configure the provider
const provider = new TiptapCollabProvider({ ... })
// use the watchContent util function to watch for content changes on the provider
const unbindWatchContent = watchContent(provider, content => {
// set your editors content
editor.commands.setContent(content)
})
If you want to unbind the watcher, you can call the returned unbindWatchContent
function like this:
const unbindWatchContent = watchPreviewContent(provider, content => {
// set your editors content
editor.commands.setContent(content)
})
// unwatch
unbindWatchContent()
Following this setup, you can trigger version.preview
requests like so:
// Define a function that sends a version.preview request to the provider
const requestVersion = version => {
provider.sendStateless(JSON.stringify({
action: 'version.preview',
// Include your version number here
version,
}))
}
// Trigger the request
requestVersion(1)
// This function can then be linked to button clicks or other UI elements to trigger the request
How version reverting works
Upon reverting a version, a new version is generated at the top of the version history. This new version will house the content of the reverted version and will stand as the latest version from which all users will proceed. If there were any unversioned changes before the revert action, an additional version will be created prior to the new one. This additional version will preserve the unaltered changes, thereby ensuring no data is lost.
Utility Functions
getPreviewContentFromVersionPayload
This function will turn the payload received from the Tiptap collab provider into Tiptap JSON content.
Argument | Description |
---|---|
payload | The Hocuspocus payload for the version preview event |
field | The field you want to parse. Default: default |
const myContent = getPreviewContentFromVersionPayload(payload, 'default')
watchPreviewContent
This function will setup a watcher on your Provider that watches the necessary events to react to version content changes. It also returns a new function that can be used to unwatch those events.
Argument | Description |
---|---|
provider | The Tiptap collab provider |
callback | The callback function that is called, the argument is the Tiptap JSON content |
field | The watched field - defaults to default |
const unwatchContent = watchPreviewContent(provider, editor.commands.setContent, 'default')
// unwatch the version preview content
unwatchContent()
Possible provider payloads
Here is a list of payloads that can be sent or received from the provider:
Outgoing
document.revert
Request a document revert to a given version with optional title settings.
provider.sendStateless(JSON.stringify({
action: 'document.revert',
version: 1,
currentVersionName: 'Before reverting to version 1',
newVersionName: 'Revert to version 1',
}))
version.create
Creates a new version with an optional title.
this.options.provider.sendStateless(JSON.stringify({ action: 'version.create', name: 'My custom version' }))
Incoming
saved
This stateless message can be used to retrieve the last saved timestamp.
provider.on('stateless', (data) => {
const payload = JSON.parse(data.payload)
if (payload.action === 'saved') {
const lastSaved = new Date()
}
})
version.created
This stateless message includes information about newely created versions.
provider.on('stateless', (data) => {
const payload = JSON.parse(data.payload)
if (payload.action === 'version.created') {
const latestVersion = payload.version
const currentVersion = payload.version
}
})
document.reverted
This stateless message includes information about a document revert.
provider.on('stateless', (data) => {
const payload = JSON.parse(data.payload)
if (payload.action === 'document.reverted') {
const currentVersion = payload.version
}
})