diff --git a/.changeset/five-melons-compete.md b/.changeset/five-melons-compete.md deleted file mode 100644 index 0d4b8d20663..00000000000 --- a/.changeset/five-melons-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/vue-3": patch ---- - -Fix editor content being destroyed before transition end diff --git a/.changeset/fresh-coats-relate.md b/.changeset/fresh-coats-relate.md new file mode 100644 index 00000000000..e187ea460be --- /dev/null +++ b/.changeset/fresh-coats-relate.md @@ -0,0 +1,5 @@ +--- +"@tiptap/core": minor +--- + +Previously, only a json representation of the node could be inserted into the editor. This change allows for the insertion of Prosemirror `Node`s and `Fragment`s directly into the editor through the `insertContentAt`, `setContent` and `insertContent` commands. diff --git a/.changeset/funny-otters-protect.md b/.changeset/funny-otters-protect.md new file mode 100644 index 00000000000..63cb6a9e331 --- /dev/null +++ b/.changeset/funny-otters-protect.md @@ -0,0 +1,5 @@ +--- +"@tiptap/core": patch +--- + +Addresses a bug with `insertContentAt`'s `simulatedPasteRules` option where it could only accept text and not Prosemirror `Node` and `Content` diff --git a/.changeset/khaki-timers-jump.md b/.changeset/khaki-timers-jump.md deleted file mode 100644 index 08d4492a6aa..00000000000 --- a/.changeset/khaki-timers-jump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/pm": patch ---- - -Update prosemirror-view version diff --git a/.changeset/nervous-bats-film.md b/.changeset/nervous-bats-film.md deleted file mode 100644 index 0defe712520..00000000000 --- a/.changeset/nervous-bats-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/core": patch ---- - -Fixed an issue while updating attributes on a NodePos that was not a text diff --git a/.changeset/slow-peaches-film.md b/.changeset/slow-peaches-film.md deleted file mode 100644 index 5fe3d8c2861..00000000000 --- a/.changeset/slow-peaches-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/core": patch ---- - -Updates the typings of `DecorationsWithTypes` to be more accurate to the prosemirror implementation even though it is not completely exposed as an API diff --git a/.changeset/sweet-feet-dream.md b/.changeset/sweet-feet-dream.md deleted file mode 100644 index 8d1b9e180ff..00000000000 --- a/.changeset/sweet-feet-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/core": patch ---- - -Fixed issues with NodePos child positions causing wrong positions when used on non-text atoms diff --git a/.changeset/sweet-masks-smash.md b/.changeset/sweet-masks-smash.md deleted file mode 100644 index 44bc236500f..00000000000 --- a/.changeset/sweet-masks-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/core": patch ---- - -Fixed an issue with getMarkRange not returning the correct range when cursor is at the start of the specified mark diff --git a/.changeset/thirty-apes-boil.md b/.changeset/thirty-apes-boil.md deleted file mode 100644 index d86eb94f266..00000000000 --- a/.changeset/thirty-apes-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tiptap/starter-kit": patch ---- - -Adds @tiptap/extension-text-style to @tiptap/starter-kit deps but does not install the extension, since it is only to resolve a peer dep install for list-items diff --git a/.changeset/violet-cameras-brush.md b/.changeset/violet-cameras-brush.md deleted file mode 100644 index 0cb10af92d4..00000000000 --- a/.changeset/violet-cameras-brush.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@tiptap/extension-table-cell": patch -"@tiptap/extension-table-header": patch ---- - -Tables now properly respect colwidths with multiple values, fixing resizeable columns when the first row has a colspan - diff --git a/demos/src/Commands/InsertContent/React/index.jsx b/demos/src/Commands/InsertContent/React/index.jsx index 2da2ad9bbde..d3c67072474 100644 --- a/demos/src/Commands/InsertContent/React/index.jsx +++ b/demos/src/Commands/InsertContent/React/index.jsx @@ -1,6 +1,7 @@ import './styles.scss' import { Color } from '@tiptap/extension-color' +import Link from '@tiptap/extension-link' import ListItem from '@tiptap/extension-list-item' import TextStyle from '@tiptap/extension-text-style' import { EditorProvider, useCurrentEditor } from '@tiptap/react' @@ -48,6 +49,7 @@ const MenuBar = () => { const extensions = [ Color.configure({ types: [TextStyle.name, ListItem.name] }), TextStyle.configure({ types: [ListItem.name] }), + Link, StarterKit.configure({ bulletList: { keepMarks: true, diff --git a/demos/src/Commands/InsertContent/React/index.spec.js b/demos/src/Commands/InsertContent/React/index.spec.js index 9bfe7083773..58dff9562ab 100644 --- a/demos/src/Commands/InsertContent/React/index.spec.js +++ b/demos/src/Commands/InsertContent/React/index.spec.js @@ -11,7 +11,7 @@ context('/src/Commands/InsertContent/React/', () => { cy.get('button[data-test-id="html-content"]').click() // check if the content html is correct - cy.get('.tiptap').should('contain.html', '

Tiptap

Hello World

This is a paragraph
with a break.

And this is some additional string content.

') + cy.get('.tiptap').should('contain.html', '

Tiptap

Hello World

This is a paragraph
with a break.

And this is some additional string content.

') }) it('should keep spaces inbetween tags in html content', () => { @@ -91,4 +91,28 @@ context('/src/Commands/InsertContent/React/', () => { }) }) + it('should respect editor.options.parseOptions if defined to be `false`', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.options.parseOptions = { preserveWhitespace: false } + editor.commands.insertContent('\n

Tiptap

Hello\n World\n

\n') + cy.get('.tiptap').should('contain.html', '

Tiptap

Hello World

') + }) + }) + + it('should respect editor.options.parseOptions if defined to be `full`', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.options.parseOptions = { preserveWhitespace: 'full' } + editor.commands.insertContent('\n

Tiptap

Hello\n World\n

\n') + cy.get('.tiptap').should('contain.html', '

Tiptap

Hello\n World

') + }) + }) + + it('should respect editor.options.parseOptions if defined to be `true`', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.options.parseOptions = { preserveWhitespace: true } + editor.commands.insertContent('

Tiptap

Hello\n World\n

') + cy.get('.tiptap').should('contain.html', '

Tiptap

Hello World

') + }) + }) + }) diff --git a/demos/src/Commands/SetContent/React/index.spec.js b/demos/src/Commands/SetContent/React/index.spec.js index f6fbdf20ea9..f2d9b03c7a2 100644 --- a/demos/src/Commands/SetContent/React/index.spec.js +++ b/demos/src/Commands/SetContent/React/index.spec.js @@ -14,6 +14,27 @@ context('/src/Commands/SetContent/React/', () => { }) }) + it('should insert raw JSON content', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.setContent({ type: 'paragraph', content: [{ type: 'text', text: 'Hello World.' }] }) + cy.get('.tiptap').should('contain.html', '

Hello World.

') + }) + }) + + it('should insert a Prosemirror Node as content', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.setContent(editor.schema.node('paragraph', null, editor.schema.text('Hello World.'))) + cy.get('.tiptap').should('contain.html', '

Hello World.

') + }) + }) + + it('should insert a Prosemirror Fragment as content', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.setContent(editor.schema.node('doc', null, editor.schema.node('paragraph', null, editor.schema.text('Hello World.'))).content) + cy.get('.tiptap').should('contain.html', '

Hello World.

') + }) + }) + it('should emit updates', () => { cy.get('.tiptap').then(([{ editor }]) => { let updateCount = 0 diff --git a/demos/src/Demos/CollaborationSplitPane/React/Editor.jsx b/demos/src/Demos/CollaborationSplitPane/React/Editor.jsx index feedcbc6acd..dbf4863797d 100644 --- a/demos/src/Demos/CollaborationSplitPane/React/Editor.jsx +++ b/demos/src/Demos/CollaborationSplitPane/React/Editor.jsx @@ -72,19 +72,23 @@ const getRandomColor = () => getRandomElement(colors) const getRandomName = () => getRandomElement(names) const getInitialUser = () => { - return ( - { - name: getRandomName(), - color: getRandomColor(), - } - ) + return { + name: getRandomName(), + color: getRandomColor(), + } } -const Editor = ({ ydoc, provider, room }) => { +const Editor = ({ + ydoc, provider, room, +}) => { const [status, setStatus] = useState('connecting') const [currentUser, setCurrentUser] = useState(getInitialUser) const editor = useEditor({ + enableContentCheck: true, + onContentError: ({ disableCollaboration }) => { + disableCollaboration() + }, onCreate: ({ editor: currentEditor }) => { provider.on('synced', () => { if (currentEditor.isEmpty) { @@ -99,13 +103,13 @@ const Editor = ({ ydoc, provider, room }) => { Highlight, TaskList, TaskItem, - CharacterCount.configure({ + CharacterCount.extend().configure({ limit: 10000, }), - Collaboration.configure({ + Collaboration.extend().configure({ document: ydoc, }), - CollaborationCursor.configure({ + CollaborationCursor.extend().configure({ provider, }), ], @@ -183,7 +187,10 @@ const Editor = ({ ydoc, provider, room }) => { -
+
- +
) diff --git a/demos/src/Demos/CollaborationSplitPane/React/index.jsx b/demos/src/Demos/CollaborationSplitPane/React/index.jsx index 5a624ee8a5a..4464403a274 100644 --- a/demos/src/Demos/CollaborationSplitPane/React/index.jsx +++ b/demos/src/Demos/CollaborationSplitPane/React/index.jsx @@ -9,7 +9,7 @@ const appId = '7j9y6m10' const room = `room.${new Date() .getFullYear() .toString() - .slice(-2)}${new Date().getMonth() + 1}${new Date().getDate()}` + .slice(-2)}${new Date().getMonth() + 1}${new Date().getDate()}-ok` // ydoc and provider for Editor A const ydocA = new Y.Doc() diff --git a/demos/src/Examples/Transition/Vue/index.spec.js b/demos/src/Examples/Transition/Vue/index.spec.js index 4185db85a80..5bceb87e198 100644 --- a/demos/src/Examples/Transition/Vue/index.spec.js +++ b/demos/src/Examples/Transition/Vue/index.spec.js @@ -6,23 +6,23 @@ context('/src/Examples/Transition/Vue/', () => { it('should not have an active tiptap instance but a button', () => { cy.get('.tiptap').should('not.exist') - cy.get('button').should('exist') + cy.get('#toggle-editor').should('exist') }) it('clicking the button should show the editor', () => { - cy.get('button').click() + cy.get('#toggle-editor').click() cy.get('.tiptap').should('exist') cy.get('.tiptap').should('be.visible') }) it('clicking the button again should hide the editor', () => { - cy.get('button').click() + cy.get('#toggle-editor').click() cy.get('.tiptap').should('exist') cy.get('.tiptap').should('be.visible') - cy.get('button').click() + cy.get('#toggle-editor').click() cy.get('.tiptap').should('not.exist') }) diff --git a/demos/src/Examples/Transition/Vue/index.vue b/demos/src/Examples/Transition/Vue/index.vue index 96cfd2f63c3..46e65dc0c5b 100644 --- a/demos/src/Examples/Transition/Vue/index.vue +++ b/demos/src/Examples/Transition/Vue/index.vue @@ -34,7 +34,12 @@ const showEditor = ref(false)