Support tables and minor fixes ()

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-11-09 00:21:24 +07:00 committed by GitHub
parent 6cc79a0fcd
commit 88a9037545
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 412 additions and 106 deletions

View File

@ -656,6 +656,16 @@ dependencies:
packages:
/@_ueberdosis/prosemirror-tables/1.1.3:
resolution: {integrity: sha512-su3pbFi1DT89g6Cuh72TE0MWWKHmWgHcQJ3ODRkm6XfIppWaGpU49t02ur3sgJc7hUhfQXjB93aSkDgOmIii2w==}
dependencies:
prosemirror-keymap: 1.2.0
prosemirror-model: 1.18.1
prosemirror-state: 1.4.1
prosemirror-transform: 1.7.0
prosemirror-view: 1.29.0
dev: false
/@ampproject/remapping/2.2.0:
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'}
@ -1861,6 +1871,42 @@ packages:
'@tiptap/core': 2.0.0-beta.199
dev: false
/@tiptap/extension-table-cell/2.0.0-beta.202_@tiptap+core@2.0.0-beta.199:
resolution: {integrity: sha512-Ypmcq7zaMSZ0VNKwDPINOsSzyuH+gSIw+FrXy6O1dzVHAo1gNFJ2pEG/ZhQ2RqpDTpGfJFD8tNDx8wjCCAVlxA==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.193
dependencies:
'@tiptap/core': 2.0.0-beta.199
dev: false
/@tiptap/extension-table-header/2.0.0-beta.202_@tiptap+core@2.0.0-beta.199:
resolution: {integrity: sha512-/l0lz3Hmc+hikj+RfSW7F6B/jYV2dROGQnK1/EYjgbvOK0158ml1mB6/Dhm+BhldV73MI7eU8+3YLB9uhsPR4w==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.193
dependencies:
'@tiptap/core': 2.0.0-beta.199
dev: false
/@tiptap/extension-table-row/2.0.0-beta.202_@tiptap+core@2.0.0-beta.199:
resolution: {integrity: sha512-IsHBT3lp//XSqcAWPIGWjPIKQ4okVaDJbwcElehlOo/rcRBeK0orT+c10T08PoOsozi4BeMYRo0nfA5tvrJMEw==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.193
dependencies:
'@tiptap/core': 2.0.0-beta.199
dev: false
/@tiptap/extension-table/2.0.0-beta.202_@tiptap+core@2.0.0-beta.199:
resolution: {integrity: sha512-WMfXtDfx45FgU81WnfxGOSJbVoaDpe8hjuBJSGbwJj+Qj4HGhbK7/RbTtDrM8oqseHRzHuGWgNX+EfOUQppjdA==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.193
dependencies:
'@_ueberdosis/prosemirror-tables': 1.1.3
'@tiptap/core': 2.0.0-beta.199
prosemirror-model: 1.18.1
prosemirror-state: 1.4.1
prosemirror-view: 1.29.0
dev: false
/@tiptap/extension-task-item/2.0.0-beta.199_@tiptap+core@2.0.0-beta.199:
resolution: {integrity: sha512-dvMgXr4B/V8dYvksLtbby3R2wM9zk3xdkOBuohTLQuRq73dK12Bh/h5xrl4cey8i/2tQBWgUfFiGVPsEUJjQCQ==}
peerDependencies:
@ -14540,7 +14586,7 @@ packages:
dev: false
file:projects/text-editor.tgz_13653a9d42656433759444fbd2afc848:
resolution: {integrity: sha512-fTQmayD5wtd7FqRklhl13kDS5Y9tyBXfC5FAoJP+xUHbNk61+FzgKnmHFOjbsJVvgFVkag98AJA9xb9nHfjFzw==, tarball: file:projects/text-editor.tgz}
resolution: {integrity: sha512-CUuJVSwdoBUNvSy7GIaxemhr6fLCaU1BrbzzEpecP4HuRfQviyQQ2ME6gj4PWhcYVweFmUa1g3gRrCECtWIU8g==, tarball: file:projects/text-editor.tgz}
id: file:projects/text-editor.tgz
name: '@rush-temp/text-editor'
version: 0.0.0
@ -14555,6 +14601,10 @@ packages:
'@tiptap/extension-link': 2.0.0-beta.199_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-mention': 2.0.0-beta.199_c8f353cb3abc70247a8f6c56ebb87d62
'@tiptap/extension-placeholder': 2.0.0-beta.199_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-table': 2.0.0-beta.202_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-table-cell': 2.0.0-beta.202_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-table-header': 2.0.0-beta.202_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-table-row': 2.0.0-beta.202_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-task-item': 2.0.0-beta.199_ec97b388f910dbe754a14ff2f0072b88
'@tiptap/extension-task-list': 2.0.0-beta.199_@tiptap+core@2.0.0-beta.199
'@tiptap/extension-typography': 2.0.0-beta.199_@tiptap+core@2.0.0-beta.199

View File

@ -23,6 +23,20 @@
"Food": "Food",
"FullDescription": "Full description",
"NoFullDescription": "There are no detailed description",
"EnableDiffMode": "Diff mode"
"EnableDiffMode": "Diff mode",
"AddColumnBefore": "Add before",
"AddColumnAfter": "Add after",
"DeleteColumn": "Delete",
"AddRowBefore": "Add before",
"AddRowAfter": "Add after",
"DeleteRow": "Delete",
"DeleteTable": "Delete",
"CategoryRow": "Rows",
"CategoryColumn": "Columns",
"Table": "Table",
"InsertTable": "Insert table",
"TableOptions": "Customize table"
}
}

View File

@ -23,6 +23,20 @@
"Food": "Еда",
"FullDescription": "Детальное описание",
"NoFullDescription": "Нет детального описания",
"EnableDiffMode": "Режим сравнения"
"EnableDiffMode": "Режим сравнения",
"AddColumnBefore": "Добавить до",
"AddColumnAfter": "Добавить после",
"DeleteColumn": "Удалить",
"AddRowBefore": "Добавить до",
"AddRowAfter": "Добавить после",
"DeleteRow": "Удалить",
"DeleteTable": "Удалить",
"CategoryRow": "Строки",
"CategoryColumn": "Колонки",
"Table": "Таблица",
"InsertTable": "Добавить таблицу",
"TableOptions": "Настроить таблицу"
}
}

View File

@ -62,6 +62,10 @@
"diff": "~5.1.0",
"@tiptap/extension-code-block": "~2.0.0-beta.200",
"@tiptap/extension-gapcursor": "~2.0.0-beta.200",
"@tiptap/extension-heading": "~2.0.0-beta.200"
"@tiptap/extension-heading": "~2.0.0-beta.200",
"@tiptap/extension-table": "~2.0.0-beta.202",
"@tiptap/extension-table-cell": "~2.0.0-beta.202",
"@tiptap/extension-table-header": "~2.0.0-beta.202",
"@tiptap/extension-table-row": "~2.0.0-beta.202"
}
}

View File

@ -16,13 +16,7 @@
-->
<script lang="ts">
import { Editor, Extension } from '@tiptap/core'
import Highlight from '@tiptap/extension-highlight'
import Link from '@tiptap/extension-link'
import Heading, { Level } from '@tiptap/extension-heading'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import StarterKit from '@tiptap/starter-kit'
import { Plugin, PluginKey } from 'prosemirror-state'
import { onDestroy, onMount } from 'svelte'
@ -30,18 +24,16 @@
import { IconSize } from '@hcengineering/ui'
import StyleButton from './StyleButton.svelte'
import TipTapCodeBlock from '@tiptap/extension-code-block'
import Gapcursor from '@tiptap/extension-gapcursor'
import { DecorationSet } from 'prosemirror-view'
import textEditorPlugin from '../plugin'
import { calculateDecorations } from './diff/decorations'
import { defaultExtensions } from './extensions'
import Objects from './icons/Objects.svelte'
export let content: Markup
export let buttonSize: IconSize = 'small'
export let comparedVersion: Markup | undefined = undefined
export let headingLevels: Level[] = [1, 2, 3, 4]
let element: HTMLElement
let editor: Editor
@ -89,34 +81,7 @@
element,
content,
editable: true,
extensions: [
StarterKit,
Highlight.configure({
multicolor: false
}),
TipTapCodeBlock.configure({
languageClassPrefix: 'language-',
exitOnArrowDown: true,
exitOnTripleEnter: true,
HTMLAttributes: {
class: 'code-block'
}
}),
Gapcursor,
Heading.configure({
levels: headingLevels
}),
Link.configure({ openOnClick: false }),
TaskList,
TaskItem.configure({
nested: true,
HTMLAttributes: {
class: 'flex flex-grow gap-1 checkbox_style'
}
}),
DecorationExtension
// ...extensions
],
extensions: [...defaultExtensions, DecorationExtension],
onTransaction: () => {
// force re-render so `editor.isActive` works as expected
editor = editor

View File

@ -18,16 +18,11 @@
import { getEmbeddedLabel, IntlString, translate } from '@hcengineering/platform'
import { Editor, Extension, HTMLContent } from '@tiptap/core'
import Highlight from '@tiptap/extension-highlight'
import Link from '@tiptap/extension-link'
import Placeholder from '@tiptap/extension-placeholder'
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import Heading, { Level } from '@tiptap/extension-heading'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import { Level } from '@tiptap/extension-heading'
import Placeholder from '@tiptap/extension-placeholder'
import StarterKit from '@tiptap/starter-kit'
import { Plugin, PluginKey, Transaction } from 'prosemirror-state'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
@ -38,8 +33,8 @@
import * as Y from 'yjs'
import StyleButton from './StyleButton.svelte'
import TipTapCodeBlock from '@tiptap/extension-code-block'
import Gapcursor from '@tiptap/extension-gapcursor'
import presentation from '@hcengineering/presentation'
import { DecorationSet } from 'prosemirror-view'
import textEditorPlugin from '../plugin'
import { FormatMode, FORMAT_MODES } from '../types'
@ -48,7 +43,9 @@
import CodeBlock from './icons/CodeBlock.svelte'
import { calculateDecorations } from './diff/decorations'
import { defaultExtensions, headingLevels } from './extensions'
import Header from './icons/Header.svelte'
import IconTable from './icons/IconTable.svelte'
import Italic from './icons/Italic.svelte'
import LinkEl from './icons/Link.svelte'
import ListBullet from './icons/ListBullet.svelte'
@ -71,8 +68,6 @@
export let suggestMode = false
export let comparedVersion: Markup | undefined = undefined
export let headingLevels: Level[] = [1, 2, 3, 4]
const ydoc = new Y.Doc()
const wsProvider = new WebsocketProvider(collaboratorURL, documentId, ydoc, {
params: {
@ -213,37 +208,9 @@
// content: 'Hello world<br/> This is simple text<br/>Some more text<br/>Yahoo <br/>Cool <br/><br/> Done',
editable: true,
extensions: [
StarterKit,
Highlight.configure({
multicolor: false
}),
TipTapCodeBlock.configure({
languageClassPrefix: 'language-',
exitOnArrowDown: true,
exitOnTripleEnter: true,
HTMLAttributes: {
class: 'code-block'
}
}),
Gapcursor,
Heading.configure({
levels: headingLevels
}),
// ChangeHighlight,
// ChangesetExtension.configure({
// isSuggestMode
// }),
Link.configure({ openOnClick: false }),
// ...(supportSubmit ? [Handle] : []), // order important
// Typography, // we need to disable 1/2 -> ½ rule (https://github.com/hcengineering/anticrm/issues/345)
...defaultExtensions,
Placeholder.configure({ placeholder: placeHolderStr }),
TaskList,
TaskItem.configure({
nested: true,
HTMLAttributes: {
class: 'flex flex-grow gap-1 checkbox_style'
}
}),
Collaboration.configure({
document: ydoc
}),
@ -269,7 +236,6 @@
focused = true
},
onUpdate: (op: { editor: Editor; transaction: Transaction }) => {
// _decoration = DecorationSet.empty
dispatch('content', editor.getHTML())
updateFormattingState()
},
@ -328,7 +294,7 @@
showPopup(
SelectPopup,
{
value: Array.from(headingLevels).map((it) => ({ id: it.toString(), label: it.toString() }))
value: Array.from(headingLevels).map((it) => ({ id: it.toString(), text: it.toString() }))
},
getEventPositionElement(event),
(val) => {
@ -342,6 +308,182 @@
}
}
function insertTable (event: MouseEvent) {
const tables = [
{
label: '2x2',
rows: 2,
cols: 2,
header: false
},
{
label: '3x3',
rows: 3,
cols: 3,
header: false
},
{
label: '2x1',
rows: 2,
cols: 1,
header: false
},
{
label: '5x5',
rows: 5,
cols: 5,
header: false
},
{
label: '1x2',
rows: 1,
cols: 2,
header: false
},
{
label: 'Headed 2x2',
rows: 2,
cols: 2,
header: true
},
{
label: 'Headed 3x3',
rows: 3,
cols: 3,
header: true
},
{
label: 'Headed 2x1',
rows: 2,
cols: 1,
header: true
},
{
label: 'Headed 5x5',
rows: 5,
cols: 5,
header: true
},
{
label: 'Headed 1x2',
rows: 1,
cols: 2,
header: true
}
]
showPopup(
SelectPopup,
{
value: [
{ id: '#delete', label: presentation.string.Remove },
...tables.map((it) => ({ id: it.label, text: it.label }))
]
},
getEventPositionElement(event),
(val) => {
if (val !== undefined) {
if (val === '#delete') {
editor.commands.deleteTable()
needFocus = true
updateFormattingState()
return
}
const tab = tables.find((it) => it.label === val)
if (tab) {
editor.commands.insertTable({
cols: tab.cols,
rows: tab.rows,
withHeaderRow: tab.header
})
needFocus = true
updateFormattingState()
}
}
}
)
}
function tableOptions (event: MouseEvent) {
const ops = [
{
id: '#addColumnBefore',
label: textEditorPlugin.string.AddColumnBefore,
action: () => editor.commands.addColumnBefore(),
category: {
label: textEditorPlugin.string.CategoryColumn
}
},
{
id: '#addColumnAfter',
label: textEditorPlugin.string.AddColumnAfter,
action: () => editor.commands.addColumnAfter(),
category: {
label: textEditorPlugin.string.CategoryColumn
}
},
{
id: '#deleteColumn',
label: textEditorPlugin.string.DeleteColumn,
action: () => editor.commands.deleteColumn(),
category: {
label: textEditorPlugin.string.CategoryColumn
}
},
{
id: '#addRowBefore',
label: textEditorPlugin.string.AddRowBefore,
action: () => editor.commands.addRowBefore(),
category: {
label: textEditorPlugin.string.CategoryRow
}
},
{
id: '#addRowAfter',
label: textEditorPlugin.string.AddRowAfter,
action: () => editor.commands.addRowAfter(),
category: {
label: textEditorPlugin.string.CategoryRow
}
},
{
id: '#deleteRow',
label: textEditorPlugin.string.DeleteRow,
action: () => editor.commands.deleteRow(),
category: {
label: textEditorPlugin.string.CategoryRow
}
},
{
id: '#deleteTable',
label: textEditorPlugin.string.DeleteTable,
action: () => editor.commands.deleteTable(),
category: {
label: textEditorPlugin.string.Table
}
}
]
showPopup(
SelectPopup,
{
value: ops
},
getEventPositionElement(event),
(val) => {
if (val !== undefined) {
const op = ops.find((it) => it.id === val)
if (op) {
op.action()
needFocus = true
updateFormattingState()
}
}
}
)
}
async function formatLink (): Promise<void> {
const link = editor.getAttributes('link').href
@ -435,6 +577,24 @@
showTooltip={{ label: textEditorPlugin.string.CodeBlock }}
on:click={getToggler(toggleCodeBlock)}
/>
<StyleButton
icon={IconTable}
iconProps={{ style: 'table' }}
size={buttonSize}
selected={activeModes.has('table')}
on:click={insertTable}
showTooltip={{ label: textEditorPlugin.string.InsertTable }}
/>
{#if activeModes.has('table')}
<StyleButton
icon={IconTable}
iconProps={{ style: 'grid' }}
size={buttonSize}
on:click={tableOptions}
showTooltip={{ label: textEditorPlugin.string.TableOptions }}
/>
{/if}
</div>
{/if}
<div class="flex-grow" />

View File

@ -17,6 +17,7 @@
import { AnySvelteComponent, Icon, IconSize, LabelAndProps, tooltip } from '@hcengineering/ui'
export let icon: Asset | AnySvelteComponent
export let iconProps: any = undefined
export let size: IconSize
export let selected: boolean = false
export let showTooltip: LabelAndProps | undefined = undefined
@ -32,7 +33,7 @@
on:click|preventDefault|stopPropagation
>
<div class="icon {size} flex">
<Icon {icon} {size} />
<Icon {icon} {size} {iconProps} />
</div>
</button>

View File

@ -17,16 +17,12 @@
import { IntlString, translate } from '@hcengineering/platform'
import { AnyExtension, Editor, Extension, HTMLContent } from '@tiptap/core'
import Highlight from '@tiptap/extension-highlight'
import Link from '@tiptap/extension-link'
// import Typography from '@tiptap/extension-typography'
import Placeholder from '@tiptap/extension-placeholder'
import StarterKit from '@tiptap/starter-kit'
import TaskList from '@tiptap/extension-task-list'
import TaskItem from '@tiptap/extension-task-item'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import textEditorPlugin from '../plugin'
import { FormatMode } from '../types'
import { defaultExtensions } from './extensions'
export let content: string = ''
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
@ -144,19 +140,9 @@
element,
content,
extensions: [
StarterKit,
Highlight,
Link.configure({ openOnClick: false }),
...defaultExtensions,
...(supportSubmit ? [Handle] : []), // order important
// Typography, // we need to disable 1/2 -> ½ rule (https://github.com/hcengineering/anticrm/issues/345)
Placeholder.configure({ placeholder: placeHolderStr }),
TaskList,
TaskItem.configure({
nested: true,
HTMLAttributes: {
class: 'flex flex-grow gap-1 checkbox_style'
}
}),
...extensions
],
onTransaction: () => {

View File

@ -0,0 +1,66 @@
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import Heading, { Level } from '@tiptap/extension-heading'
import Highlight from '@tiptap/extension-highlight'
import StarterKit from '@tiptap/starter-kit'
import TipTapCodeBlock from '@tiptap/extension-code-block'
import Gapcursor from '@tiptap/extension-gapcursor'
import Link from '@tiptap/extension-link'
export const tableExtensions = [
Table.configure({
resizable: false,
HTMLAttributes: {
class: 'antiTable editable '
}
}),
TableRow.configure({
HTMLAttributes: {
class: 'antiTable-body__row'
}
}),
TableHeader.configure({}),
TableCell.configure({})
]
export const taskListExtensions = [
TaskList,
TaskItem.configure({
nested: true,
HTMLAttributes: {
class: 'flex flex-grow gap-1 checkbox_style'
}
})
]
export const headingLevels: Level[] = [1, 2, 3, 4, 5, 6]
export const defaultExtensions = [
StarterKit,
Highlight.configure({
multicolor: false
}),
TipTapCodeBlock.configure({
languageClassPrefix: 'language-',
exitOnArrowDown: true,
exitOnTripleEnter: true,
HTMLAttributes: {
class: 'code-block'
}
}),
Gapcursor,
Heading.configure({
levels: headingLevels
}),
Link.configure({ openOnClick: false }),
...tableExtensions,
...taskListExtensions
]

View File

@ -0,0 +1,25 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
export let style: 'table' | 'grid' = 'table'
const fill: string = 'currentColor'
</script>
{#if style === 'table'}
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2 8C2 6.34315 3.34315 5 5 5H19C20.6569 5 22 6.34315 22 8V16C22 17.6569 20.6569 19 19 19H5C3.34315 19 2 17.6569 2 16V8ZM5 7H19C19.5523 7 20 7.44771 20 8V11H4V8C4 7.44772 4.44772 7 5 7ZM4 13V16C4 16.5523 4.44772 17 5 17H8V13H4ZM10 17H19C19.5523 17 20 16.5523 20 16V13H10V17Z"
fill="currentColor"
/>
</svg>
{:else if style === 'grid'}
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"
><path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5 5C3.34315 5 2 6.34315 2 8V16C2 17.6569 3.34315 19 5 19H19C20.6569 19 22 17.6569 22 16V8C22 6.34315 20.6569 5 19 5H5ZM8 7H5C4.44772 7 4 7.44772 4 8V9H8V7ZM10 7V9H14V7H10ZM16 7V9H20V8C20 7.44771 19.5523 7 19 7H16ZM14 11H10V13H14V11ZM16 13V11H20V13H16ZM14 15H10V17H14V15ZM16 17V15H20V16C20 16.5523 19.5523 17 19 17H16ZM8 17V15H4V16C4 16.5523 4.44772 17 5 17H8ZM8 13V11H4V13H8Z"
fill="currentColor"
/></svg
>
{/if}

View File

@ -55,6 +55,19 @@ export default plugin(textEditorId, {
Objects: '' as IntlString,
FullDescription: '' as IntlString,
NoFullDescription: '' as IntlString,
EnableDiffMode: '' as IntlString
EnableDiffMode: '' as IntlString,
InsertTable: '' as IntlString,
AddColumnBefore: '' as IntlString,
AddColumnAfter: '' as IntlString,
DeleteColumn: '' as IntlString,
AddRowBefore: '' as IntlString,
AddRowAfter: '' as IntlString,
DeleteRow: '' as IntlString,
DeleteTable: '' as IntlString,
CategoryRow: '' as IntlString,
CategoryColumn: '' as IntlString,
Table: '' as IntlString,
TableOptions: '' as IntlString
}
})

View File

@ -35,7 +35,8 @@ export const FORMAT_MODES = [
'blockquote',
'code',
'codeBlock',
'heading'
'heading',
'table'
] as const
export type FormatMode = typeof FORMAT_MODES[number]

View File

@ -372,6 +372,13 @@
.checkall { visibility: visible; }
}
&.editable {
th, td, tr {
border: 1px dashed var(--divider-color);
}
}
&.metaColumn {
th, td {
&:first-child {

View File

@ -234,8 +234,8 @@
let mode: ModelType = 'view'
const modeLabels = {
view: document.string.ViewMode,
edit: document.string.EditMode,
suggest: document.string.SuggestMode
edit: document.string.EditMode
// suggest: document.string.SuggestMode
}
function selectMode (event: MouseEvent): void {
@ -534,7 +534,7 @@
<DocumentEditor
object={version}
initialContentId={version.initialContentId}
comparedVersion={compareTo?.content ?? versions[versions.length - 1].content}
comparedVersion={compareTo?.content ?? versions[versions.length - 2]?.content}
readonly={mode === 'view'}
bind:this={editor}
/>