Versions

A version comparison of a document with charts and diagrams compares what the document stores, never the picture. When any stored attribute differs between two versions, the chart or the diagram has changed.
Pro ExtensionAlphaPilot program

What is compared

NodeIts state
A chartIts attributes: its data, spec, Renderer, Data Source and when that source was last read
A diagramIts source text, format, title and options

With Tiptap's Compare

@tiptap-pro/compare compares the JSON of two documents. A chart or a diagram whose attributes differ is reported as a changed block. Leave out the attributes that only serve showing or editing, so they do not show up as changes:

import { compareDocuments } from '@tiptap-pro/compare'

const changeset = compareDocuments({
  schema: editor.schema,
  docA: olderVersion,
  docB: newerVersion,
  diffOptions: {
    // Compare's defaults, plus a chart's uploaded preview image and a table's Quick Input view
    ignoreAttributes: ['id', 'data-thread-id', '_hash', 'previewUrl', 'diagramInput'],
  },
})
AttributeChanges whenIn a comparison
datavalues are edited, or a synchronization brings different dataa change
specthe type, title, colors, axes or references changea change
rendereranother Renderer is chosen; the data stays the samea change
sourcethe Data Source is linked, detached, or its address changesa change
syncedAtthe chart reads its Data Source, also when the data is the samea change; add 'syncedAt' to ignoreAttributes to leave it out
previewUrla preview image is uploaded after a changeleave out
diagramInput (on a table)the table is used as the Quick Input's table viewleave out
  • A synchronization that brings the same data still records when the data was last checked.
  • Whether that is a change worth showing is yours to decide with ignoreAttributes.

Saying what changed in a chart

A comparison shows a changed chart as a whole. describeChartChange from @tiptap-pro/extension-diagrams/core says what changed inside it, for a line such as "February: 4 → 5, type: bar → line" next to the change:

import { describeChartChange } from '@tiptap-pro/extension-diagrams/core'

const change = describeChartChange({ before: olderChart.attrs, after: newerChart.attrs })

if (change.changed) {
  const cells =
    change.data?.cells.map(
      (cell) => `${cell.row} ${cell.column}: ${cell.before} → ${cell.after}`,
    ) ?? []
  const fields = change.spec.map(
    (field) => `${field.path}: ${String(field.before)} → ${String(field.after)}`,
  )
  console.log([...cells, ...fields].join(', '))
}

It returns:

FieldWhat it holds
changedWhether anything below differs
data{ columnsAdded, columnsRemoved, rowsAdded, rowsRemoved, cells }, or null
spec{ path, before, after } for every spec field that differs, such as axes.y.min
renderer, source, syncedAt{ before, after }, or null when unchanged
  • Rows are named by their label when every row has a distinct one, otherwise by their position.
  • Cells are named by row and column header.
  • Attributes from any document are accepted, also from before syncedAt was an attribute of its own.
  • What it cannot read counts as an empty chart.