Skip to content

Commit 90665db

Browse files
authored
feat(core): support for Tiptap V3 (#2001)
1 parent f28cdc2 commit 90665db

File tree

305 files changed

+3091
-2798
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

305 files changed

+3091
-2798
lines changed

docs/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@
3535
"@fumadocs/mdx-remote": "1.3.0",
3636
"@headlessui/react": "^1.7.19",
3737
"@heroicons/react": "^2.2.0",
38-
"@liveblocks/client": "^3",
39-
"@liveblocks/react": "^3",
40-
"@liveblocks/react-blocknote": "^3",
41-
"@liveblocks/react-tiptap": "^3",
42-
"@liveblocks/react-ui": "^3",
38+
"@liveblocks/client": "3.7.1-tiptap3",
39+
"@liveblocks/react": "3.7.1-tiptap3",
40+
"@liveblocks/react-blocknote": "3.7.1-tiptap3",
41+
"@liveblocks/react-tiptap": "3.7.1-tiptap3",
42+
"@liveblocks/react-ui": "3.7.1-tiptap3",
4343
"@mantine/core": "^7.17.3",
4444
"@mui/icons-material": "^5.16.1",
4545
"@mui/material": "^5.16.1",
@@ -54,7 +54,7 @@
5454
"@shikijs/langs-precompiled": "^3.2.1",
5555
"@shikijs/themes": "^3.2.1",
5656
"@shikijs/types": "^3.2.1",
57-
"@tiptap/core": "^2.26.1",
57+
"@tiptap/core": "^3.4.3",
5858
"@uppy/core": "^3.13.1",
5959
"@uppy/dashboard": "^3.9.1",
6060
"@uppy/drag-drop": "^3.1.1",

examples/07-collaboration/02-liveblocks/.bnexample.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
"author": "yousefed",
55
"tags": ["Advanced", "Saving/Loading", "Collaboration"],
66
"dependencies": {
7-
"@liveblocks/client": "^3",
8-
"@liveblocks/react": "^3",
9-
"@liveblocks/react-blocknote": "^3",
10-
"@liveblocks/react-tiptap": "^3",
11-
"@liveblocks/react-ui": "^3",
7+
"@liveblocks/client": "3.7.1-tiptap3",
8+
"@liveblocks/react": "3.7.1-tiptap3",
9+
"@liveblocks/react-blocknote": "3.7.1-tiptap3",
10+
"@liveblocks/react-tiptap": "3.7.1-tiptap3",
11+
"@liveblocks/react-ui": "3.7.1-tiptap3",
1212
"yjs": "^13.6.27"
1313
}
1414
}

examples/07-collaboration/02-liveblocks/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
"@blocknote/shadcn": "latest",
1818
"react": "^19.1.0",
1919
"react-dom": "^19.1.0",
20-
"@liveblocks/client": "^3",
21-
"@liveblocks/react": "^3",
22-
"@liveblocks/react-blocknote": "^3",
23-
"@liveblocks/react-tiptap": "^3",
24-
"@liveblocks/react-ui": "^3",
20+
"@liveblocks/client": "3.7.1-tiptap3",
21+
"@liveblocks/react": "3.7.1-tiptap3",
22+
"@liveblocks/react-blocknote": "3.7.1-tiptap3",
23+
"@liveblocks/react-tiptap": "3.7.1-tiptap3",
24+
"@liveblocks/react-ui": "3.7.1-tiptap3",
2525
"yjs": "^13.6.27"
2626
},
2727
"devDependencies": {

examples/08-extensions/01-tiptap-arrow-conversion/.bnexample.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"tags": ["Extension"],
66
"pro": true,
77
"dependencies": {
8-
"@tiptap/core": "^2.26.1"
8+
"@tiptap/core": "^3.4.3"
99
}
1010
}

examples/08-extensions/01-tiptap-arrow-conversion/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"@blocknote/shadcn": "latest",
1818
"react": "^19.1.0",
1919
"react-dom": "^19.1.0",
20-
"@tiptap/core": "^2.26.1"
20+
"@tiptap/core": "^3.4.3"
2121
},
2222
"devDependencies": {
2323
"@types/react": "^19.1.0",

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
"msw",
3434
"nx",
3535
"unrs-resolver"
36-
]
36+
],
37+
"patchedDependencies": {
38+
"y-prosemirror": "patches/y-prosemirror.patch"
39+
}
3740
},
3841
"packageManager": "[email protected]+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad",
3942
"private": true,

packages/core/package.json

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,19 @@
8181
"dependencies": {
8282
"@emoji-mart/data": "^1.2.1",
8383
"@shikijs/types": "3.2.1",
84-
"@tiptap/core": "^2.26.1",
85-
"@tiptap/extension-bold": "^2.26.1",
86-
"@tiptap/extension-code": "^2.26.1",
87-
"@tiptap/extension-gapcursor": "^2.26.1",
88-
"@tiptap/extension-history": "^2.26.1",
89-
"@tiptap/extension-horizontal-rule": "^2.26.1",
90-
"@tiptap/extension-italic": "^2.26.1",
91-
"@tiptap/extension-link": "^2.26.1",
92-
"@tiptap/extension-paragraph": "^2.26.1",
93-
"@tiptap/extension-strike": "^2.26.1",
94-
"@tiptap/extension-table-cell": "^2.26.1",
95-
"@tiptap/extension-table-header": "^2.26.1",
96-
"@tiptap/extension-text": "^2.26.1",
97-
"@tiptap/extension-underline": "^2.26.1",
98-
"@tiptap/pm": "^2.26.1",
84+
"@tiptap/core": "^3.4.3",
85+
"@tiptap/extension-bold": "^3",
86+
"@tiptap/extension-code": "^3",
87+
"@tiptap/extension-gapcursor": "^3",
88+
"@tiptap/extension-history": "^3",
89+
"@tiptap/extension-horizontal-rule": "^3",
90+
"@tiptap/extension-italic": "^3",
91+
"@tiptap/extension-link": "^3",
92+
"@tiptap/extension-paragraph": "^3",
93+
"@tiptap/extension-strike": "^3",
94+
"@tiptap/extension-text": "^3",
95+
"@tiptap/extension-underline": "^3",
96+
"@tiptap/pm": "^3.4.3",
9997
"emoji-mart": "^5.6.0",
10098
"fast-deep-equal": "^3",
10199
"hast-util-from-dom": "^5.0.1",

packages/core/src/api/blockManipulation/setupTestEnv.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export function setupTestEnv() {
1313
});
1414

1515
afterAll(() => {
16-
editor.mount(undefined);
1716
editor._tiptapEditor.destroy();
1817
editor = undefined as any;
1918
});

packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,38 @@ export function serializeInlineContentExternalHTML<
6060

6161
for (const node of nodes) {
6262
// Check if this is a custom inline content node with toExternalHTML
63-
if (editor.schema.inlineContentSchema[node.type.name]) {
63+
if (
64+
node.type.name !== "text" &&
65+
editor.schema.inlineContentSchema[node.type.name]
66+
) {
6467
const inlineContentImplementation =
6568
editor.schema.inlineContentSpecs[node.type.name].implementation;
6669

67-
if (inlineContentImplementation?.toExternalHTML) {
70+
if (inlineContentImplementation) {
6871
// Convert the node to inline content format
6972
const inlineContent = nodeToCustomInlineContent(
7073
node,
7174
editor.schema.inlineContentSchema,
7275
editor.schema.styleSchema,
7376
);
7477

75-
// Use the custom toExternalHTML method
76-
const output = inlineContentImplementation.toExternalHTML(
77-
inlineContent as any,
78-
editor as any,
79-
);
78+
// Use the custom toExternalHTML method or fallback to `render`
79+
const output = inlineContentImplementation.toExternalHTML
80+
? inlineContentImplementation.toExternalHTML(
81+
inlineContent as any,
82+
editor as any,
83+
)
84+
: inlineContentImplementation.render.call(
85+
{
86+
renderType: "dom",
87+
props: undefined,
88+
},
89+
inlineContent as any,
90+
() => {
91+
// No-op
92+
},
93+
editor as any,
94+
);
8095

8196
if (output) {
8297
fragment.appendChild(output.dom);
@@ -93,14 +108,40 @@ export function serializeInlineContentExternalHTML<
93108
continue;
94109
}
95110
}
96-
}
111+
} else if (node.type.name === "text") {
112+
// We serialize text nodes manually as we need to serialize the styles/
113+
// marks using `styleSpec.implementation.render`. When left up to
114+
// ProseMirror, it'll use `toDOM` which is incorrect.
115+
let dom: globalThis.Node | Text = document.createTextNode(
116+
node.textContent,
117+
);
118+
// Reverse the order of marks to maintain the correct priority.
119+
for (const mark of node.marks.toReversed()) {
120+
if (mark.type.name in editor.schema.styleSpecs) {
121+
const newDom = (
122+
editor.schema.styleSpecs[mark.type.name].implementation
123+
.toExternalHTML ??
124+
editor.schema.styleSpecs[mark.type.name].implementation.render
125+
)(mark.attrs["stringValue"], editor);
126+
newDom.contentDOM!.appendChild(dom);
127+
dom = newDom.dom;
128+
} else {
129+
const domOutputSpec = mark.type.spec.toDOM!(mark, true);
130+
const newDom = DOMSerializer.renderSpec(document, domOutputSpec);
131+
newDom.contentDOM!.appendChild(dom);
132+
dom = newDom.dom;
133+
}
134+
}
97135

98-
// Fall back to default serialization for this node
99-
const nodeFragment = serializer.serializeFragment(
100-
Fragment.from([node]),
101-
options,
102-
);
103-
fragment.appendChild(nodeFragment);
136+
fragment.appendChild(dom);
137+
} else {
138+
// Fall back to default serialization for this node
139+
const nodeFragment = serializer.serializeFragment(
140+
Fragment.from([node]),
141+
options,
142+
);
143+
fragment.appendChild(nodeFragment);
144+
}
104145
}
105146

106147
if (

packages/core/src/api/exporters/html/util/serializeBlocksInternalHTML.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export function serializeInlineContentInternalHTML<
4848
// Check if this is a custom inline content node with toExternalHTML
4949
if (
5050
node.type.name !== "text" &&
51-
node.type.name !== "link" &&
5251
editor.schema.inlineContentSchema[node.type.name]
5352
) {
5453
const inlineContentImplementation =
@@ -90,14 +89,38 @@ export function serializeInlineContentInternalHTML<
9089
continue;
9190
}
9291
}
93-
}
92+
} else if (node.type.name === "text") {
93+
// We serialize text nodes manually as we need to serialize the styles/
94+
// marks using `styleSpec.implementation.render`. When left up to
95+
// ProseMirror, it'll use `toDOM` which is incorrect.
96+
let dom: globalThis.Node | Text = document.createTextNode(
97+
node.textContent,
98+
);
99+
// Reverse the order of marks to maintain the correct priority.
100+
for (const mark of node.marks.toReversed()) {
101+
if (mark.type.name in editor.schema.styleSpecs) {
102+
const newDom = editor.schema.styleSpecs[
103+
mark.type.name
104+
].implementation.render(mark.attrs["stringValue"], editor);
105+
newDom.contentDOM!.appendChild(dom);
106+
dom = newDom.dom;
107+
} else {
108+
const domOutputSpec = mark.type.spec.toDOM!(mark, true);
109+
const newDom = DOMSerializer.renderSpec(document, domOutputSpec);
110+
newDom.contentDOM!.appendChild(dom);
111+
dom = newDom.dom;
112+
}
113+
}
94114

95-
// Fall back to default serialization for this node
96-
const nodeFragment = serializer.serializeFragment(
97-
Fragment.from([node]),
98-
options,
99-
);
100-
fragment.appendChild(nodeFragment);
115+
fragment.appendChild(dom);
116+
} else {
117+
// Fall back to default serialization for this node
118+
const nodeFragment = serializer.serializeFragment(
119+
Fragment.from([node]),
120+
options,
121+
);
122+
fragment.appendChild(nodeFragment);
123+
}
101124
}
102125

103126
return fragment;

0 commit comments

Comments
 (0)