@tiptap/core 3.0.0-next.3 → 3.0.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +5 -1
  3. package/dist/index.cjs +2402 -2540
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +1355 -1273
  6. package/dist/index.d.ts +1355 -1273
  7. package/dist/index.js +2408 -2562
  8. package/dist/index.js.map +1 -1
  9. package/package.json +9 -5
  10. package/src/CommandManager.ts +2 -9
  11. package/src/Editor.ts +87 -72
  12. package/src/EventEmitter.ts +7 -10
  13. package/src/Extension.ts +8 -14
  14. package/src/ExtensionManager.ts +26 -125
  15. package/src/InputRule.ts +35 -48
  16. package/src/Mark.ts +9 -9
  17. package/src/Node.ts +9 -9
  18. package/src/NodePos.ts +1 -3
  19. package/src/NodeView.ts +10 -20
  20. package/src/PasteRule.ts +43 -55
  21. package/src/Tracker.ts +7 -9
  22. package/src/commands/blur.ts +14 -12
  23. package/src/commands/clearContent.ts +6 -4
  24. package/src/commands/clearNodes.ts +32 -30
  25. package/src/commands/command.ts +1 -1
  26. package/src/commands/createParagraphNear.ts +5 -3
  27. package/src/commands/cut.ts +12 -10
  28. package/src/commands/deleteCurrentNode.ts +23 -21
  29. package/src/commands/deleteNode.ts +18 -16
  30. package/src/commands/deleteRange.ts +10 -8
  31. package/src/commands/deleteSelection.ts +5 -3
  32. package/src/commands/enter.ts +6 -4
  33. package/src/commands/exitCode.ts +5 -3
  34. package/src/commands/extendMarkRange.ts +14 -12
  35. package/src/commands/first.ts +2 -4
  36. package/src/commands/focus.ts +45 -48
  37. package/src/commands/forEach.ts +2 -2
  38. package/src/commands/insertContent.ts +12 -14
  39. package/src/commands/insertContentAt.ts +101 -98
  40. package/src/commands/join.ts +20 -12
  41. package/src/commands/joinItemBackward.ts +16 -18
  42. package/src/commands/joinItemForward.ts +16 -18
  43. package/src/commands/joinTextblockBackward.ts +5 -3
  44. package/src/commands/joinTextblockForward.ts +5 -3
  45. package/src/commands/keyboardShortcut.ts +29 -34
  46. package/src/commands/lift.ts +10 -8
  47. package/src/commands/liftEmptyBlock.ts +6 -4
  48. package/src/commands/liftListItem.ts +6 -4
  49. package/src/commands/newlineInCode.ts +5 -3
  50. package/src/commands/resetAttributes.ts +36 -41
  51. package/src/commands/scrollIntoView.ts +9 -7
  52. package/src/commands/selectAll.ts +10 -8
  53. package/src/commands/selectNodeBackward.ts +5 -3
  54. package/src/commands/selectNodeForward.ts +5 -3
  55. package/src/commands/selectParentNode.ts +5 -3
  56. package/src/commands/selectTextblockEnd.ts +5 -3
  57. package/src/commands/selectTextblockStart.ts +5 -3
  58. package/src/commands/setContent.ts +25 -25
  59. package/src/commands/setMark.ts +55 -57
  60. package/src/commands/setMeta.ts +7 -5
  61. package/src/commands/setNode.ts +32 -30
  62. package/src/commands/setNodeSelection.ts +11 -9
  63. package/src/commands/setTextSelection.ts +15 -13
  64. package/src/commands/sinkListItem.ts +6 -4
  65. package/src/commands/splitBlock.ts +67 -76
  66. package/src/commands/splitListItem.ts +93 -106
  67. package/src/commands/toggleList.ts +73 -71
  68. package/src/commands/toggleMark.ts +11 -9
  69. package/src/commands/toggleNode.ts +18 -16
  70. package/src/commands/toggleWrap.ts +10 -8
  71. package/src/commands/undoInputRule.ts +31 -29
  72. package/src/commands/unsetAllMarks.ts +16 -14
  73. package/src/commands/unsetMark.ts +27 -25
  74. package/src/commands/updateAttributes.ts +92 -100
  75. package/src/commands/wrapIn.ts +6 -4
  76. package/src/commands/wrapInList.ts +6 -4
  77. package/src/extensions/clipboardTextSerializer.ts +2 -4
  78. package/src/extensions/focusEvents.ts +2 -6
  79. package/src/extensions/keymap.ts +54 -50
  80. package/src/extensions/paste.ts +0 -1
  81. package/src/extensions/tabindex.ts +1 -1
  82. package/src/helpers/combineTransactionSteps.ts +1 -4
  83. package/src/helpers/createChainableState.ts +1 -4
  84. package/src/helpers/createDocument.ts +1 -3
  85. package/src/helpers/createNodeFromContent.ts +4 -10
  86. package/src/helpers/findChildrenInRange.ts +1 -5
  87. package/src/helpers/findParentNode.ts +3 -1
  88. package/src/helpers/flattenExtensions.ts +30 -0
  89. package/src/helpers/getAttributes.ts +1 -4
  90. package/src/helpers/getAttributesFromExtensions.ts +28 -37
  91. package/src/helpers/getChangedRanges.ts +13 -11
  92. package/src/helpers/getExtensionField.ts +1 -4
  93. package/src/helpers/getMarkAttributes.ts +1 -4
  94. package/src/helpers/getMarkRange.ts +5 -15
  95. package/src/helpers/getMarkType.ts +1 -3
  96. package/src/helpers/getNodeAttributes.ts +1 -4
  97. package/src/helpers/getNodeType.ts +1 -3
  98. package/src/helpers/getRenderedAttributes.ts +1 -3
  99. package/src/helpers/getSchema.ts +2 -2
  100. package/src/helpers/getSchemaByResolvedExtensions.ts +45 -77
  101. package/src/helpers/getSplittedAttributes.ts +4 -4
  102. package/src/helpers/getTextContentFromNodes.ts +8 -11
  103. package/src/helpers/index.ts +4 -0
  104. package/src/helpers/injectExtensionAttributesToParseRule.ts +1 -1
  105. package/src/helpers/isActive.ts +1 -5
  106. package/src/helpers/isExtensionRulesEnabled.ts +1 -3
  107. package/src/helpers/isNodeEmpty.ts +2 -2
  108. package/src/helpers/resolveExtensions.ts +25 -0
  109. package/src/helpers/resolveFocusPosition.ts +3 -14
  110. package/src/helpers/rewriteUnknownContent.ts +149 -0
  111. package/src/helpers/sortExtensions.ts +26 -0
  112. package/src/inputRules/markInputRule.ts +1 -5
  113. package/src/inputRules/nodeInputRule.ts +2 -9
  114. package/src/inputRules/textInputRule.ts +1 -4
  115. package/src/inputRules/textblockTypeInputRule.ts +2 -8
  116. package/src/inputRules/wrappingInputRule.ts +13 -19
  117. package/src/pasteRules/markPasteRule.ts +1 -3
  118. package/src/pasteRules/nodePasteRule.ts +2 -8
  119. package/src/pasteRules/textPasteRule.ts +1 -4
  120. package/src/types.ts +212 -172
  121. package/src/utilities/createStyleTag.ts +3 -1
  122. package/src/utilities/deleteProps.ts +7 -11
  123. package/src/utilities/findDuplicates.ts +4 -1
  124. package/src/utilities/isFunction.ts +1 -0
  125. package/src/utilities/isMacOS.ts +1 -3
  126. package/src/utilities/isiOS.ts +5 -10
  127. package/src/utilities/mergeAttributes.ts +16 -6
  128. package/src/utilities/removeDuplicates.ts +1 -3
@@ -30,34 +30,36 @@ declare module '@tiptap/core' {
30
30
  }
31
31
  }
32
32
 
33
- export const unsetMark: RawCommands['unsetMark'] = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
34
- const { extendEmptyMarkRange = false } = options
35
- const { selection } = tr
36
- const type = getMarkType(typeOrName, state.schema)
37
- const { $from, empty, ranges } = selection
33
+ export const unsetMark: RawCommands['unsetMark'] =
34
+ (typeOrName, options = {}) =>
35
+ ({ tr, state, dispatch }) => {
36
+ const { extendEmptyMarkRange = false } = options
37
+ const { selection } = tr
38
+ const type = getMarkType(typeOrName, state.schema)
39
+ const { $from, empty, ranges } = selection
38
40
 
39
- if (!dispatch) {
40
- return true
41
- }
41
+ if (!dispatch) {
42
+ return true
43
+ }
42
44
 
43
- if (empty && extendEmptyMarkRange) {
44
- let { from, to } = selection
45
- const attrs = $from.marks().find(mark => mark.type === type)?.attrs
46
- const range = getMarkRange($from, type, attrs)
45
+ if (empty && extendEmptyMarkRange) {
46
+ let { from, to } = selection
47
+ const attrs = $from.marks().find(mark => mark.type === type)?.attrs
48
+ const range = getMarkRange($from, type, attrs)
47
49
 
48
- if (range) {
49
- from = range.from
50
- to = range.to
51
- }
50
+ if (range) {
51
+ from = range.from
52
+ to = range.to
53
+ }
52
54
 
53
- tr.removeMark(from, to, type)
54
- } else {
55
- ranges.forEach(range => {
56
- tr.removeMark(range.$from.pos, range.$to.pos, type)
57
- })
58
- }
55
+ tr.removeMark(from, to, type)
56
+ } else {
57
+ ranges.forEach(range => {
58
+ tr.removeMark(range.$from.pos, range.$to.pos, type)
59
+ })
60
+ }
59
61
 
60
- tr.removeStoredMark(type)
62
+ tr.removeStoredMark(type)
61
63
 
62
- return true
63
- }
64
+ return true
65
+ }
@@ -1,6 +1,4 @@
1
- import {
2
- Mark, MarkType, Node, NodeType,
3
- } from '@tiptap/pm/model'
1
+ import { Mark, MarkType, Node, NodeType } from '@tiptap/pm/model'
4
2
  import { SelectionRange } from '@tiptap/pm/state'
5
3
 
6
4
  import { getMarkType } from '../helpers/getMarkType.js'
@@ -32,117 +30,111 @@ declare module '@tiptap/core' {
32
30
  }
33
31
  }
34
32
 
35
- export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
33
+ export const updateAttributes: RawCommands['updateAttributes'] =
34
+ (typeOrName, attributes = {}) =>
35
+ ({ tr, state, dispatch }) => {
36
+ let nodeType: NodeType | null = null
37
+ let markType: MarkType | null = null
36
38
 
37
- let nodeType: NodeType | null = null
38
- let markType: MarkType | null = null
39
+ const schemaType = getSchemaTypeNameByName(
40
+ typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
41
+ state.schema,
42
+ )
39
43
 
40
- const schemaType = getSchemaTypeNameByName(
41
- typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
42
- state.schema,
43
- )
44
-
45
- if (!schemaType) {
46
- return false
47
- }
48
-
49
- if (schemaType === 'node') {
50
- nodeType = getNodeType(typeOrName as NodeType, state.schema)
51
- }
52
-
53
- if (schemaType === 'mark') {
54
- markType = getMarkType(typeOrName as MarkType, state.schema)
55
- }
56
-
57
- if (dispatch) {
58
- tr.selection.ranges.forEach((range: SelectionRange) => {
44
+ if (!schemaType) {
45
+ return false
46
+ }
59
47
 
60
- const from = range.$from.pos
61
- const to = range.$to.pos
48
+ if (schemaType === 'node') {
49
+ nodeType = getNodeType(typeOrName as NodeType, state.schema)
50
+ }
62
51
 
63
- let lastPos: number | undefined
64
- let lastNode: Node | undefined
65
- let trimmedFrom: number
66
- let trimmedTo: number
52
+ if (schemaType === 'mark') {
53
+ markType = getMarkType(typeOrName as MarkType, state.schema)
54
+ }
67
55
 
68
- if (tr.selection.empty) {
69
- state.doc.nodesBetween(from, to, (node: Node, pos: number) => {
56
+ if (dispatch) {
57
+ tr.selection.ranges.forEach((range: SelectionRange) => {
58
+ const from = range.$from.pos
59
+ const to = range.$to.pos
70
60
 
71
- if (nodeType && nodeType === node.type) {
72
- trimmedFrom = Math.max(pos, from)
73
- trimmedTo = Math.min(pos + node.nodeSize, to)
74
- lastPos = pos
75
- lastNode = node
76
- }
77
- })
78
- } else {
79
- state.doc.nodesBetween(from, to, (node: Node, pos: number) => {
80
-
81
- if (pos < from && nodeType && nodeType === node.type) {
82
- trimmedFrom = Math.max(pos, from)
83
- trimmedTo = Math.min(pos + node.nodeSize, to)
84
- lastPos = pos
85
- lastNode = node
86
- }
87
-
88
- if (pos >= from && pos <= to) {
61
+ let lastPos: number | undefined
62
+ let lastNode: Node | undefined
63
+ let trimmedFrom: number
64
+ let trimmedTo: number
89
65
 
66
+ if (tr.selection.empty) {
67
+ state.doc.nodesBetween(from, to, (node: Node, pos: number) => {
90
68
  if (nodeType && nodeType === node.type) {
91
- tr.setNodeMarkup(pos, undefined, {
92
- ...node.attrs,
93
- ...attributes,
94
- })
69
+ trimmedFrom = Math.max(pos, from)
70
+ trimmedTo = Math.min(pos + node.nodeSize, to)
71
+ lastPos = pos
72
+ lastNode = node
95
73
  }
96
-
97
- if (markType && node.marks.length) {
98
- node.marks.forEach((mark: Mark) => {
99
-
100
- if (markType === mark.type) {
101
- const trimmedFrom2 = Math.max(pos, from)
102
- const trimmedTo2 = Math.min(pos + node.nodeSize, to)
103
-
104
- tr.addMark(
105
- trimmedFrom2,
106
- trimmedTo2,
107
- markType.create({
108
- ...mark.attrs,
109
- ...attributes,
110
- }),
111
- )
112
- }
113
- })
74
+ })
75
+ } else {
76
+ state.doc.nodesBetween(from, to, (node: Node, pos: number) => {
77
+ if (pos < from && nodeType && nodeType === node.type) {
78
+ trimmedFrom = Math.max(pos, from)
79
+ trimmedTo = Math.min(pos + node.nodeSize, to)
80
+ lastPos = pos
81
+ lastNode = node
114
82
  }
115
- }
116
- })
117
- }
118
83
 
119
- if (lastNode) {
120
-
121
- if (lastPos !== undefined) {
122
- tr.setNodeMarkup(lastPos, undefined, {
123
- ...lastNode.attrs,
124
- ...attributes,
84
+ if (pos >= from && pos <= to) {
85
+ if (nodeType && nodeType === node.type) {
86
+ tr.setNodeMarkup(pos, undefined, {
87
+ ...node.attrs,
88
+ ...attributes,
89
+ })
90
+ }
91
+
92
+ if (markType && node.marks.length) {
93
+ node.marks.forEach((mark: Mark) => {
94
+ if (markType === mark.type) {
95
+ const trimmedFrom2 = Math.max(pos, from)
96
+ const trimmedTo2 = Math.min(pos + node.nodeSize, to)
97
+
98
+ tr.addMark(
99
+ trimmedFrom2,
100
+ trimmedTo2,
101
+ markType.create({
102
+ ...mark.attrs,
103
+ ...attributes,
104
+ }),
105
+ )
106
+ }
107
+ })
108
+ }
109
+ }
125
110
  })
126
111
  }
127
112
 
128
- if (markType && lastNode.marks.length) {
129
- lastNode.marks.forEach((mark: Mark) => {
113
+ if (lastNode) {
114
+ if (lastPos !== undefined) {
115
+ tr.setNodeMarkup(lastPos, undefined, {
116
+ ...lastNode.attrs,
117
+ ...attributes,
118
+ })
119
+ }
130
120
 
131
- if (markType === mark.type) {
132
- tr.addMark(
133
- trimmedFrom,
134
- trimmedTo,
135
- markType.create({
136
- ...mark.attrs,
137
- ...attributes,
138
- }),
139
- )
140
- }
141
- })
121
+ if (markType && lastNode.marks.length) {
122
+ lastNode.marks.forEach((mark: Mark) => {
123
+ if (markType === mark.type) {
124
+ tr.addMark(
125
+ trimmedFrom,
126
+ trimmedTo,
127
+ markType.create({
128
+ ...mark.attrs,
129
+ ...attributes,
130
+ }),
131
+ )
132
+ }
133
+ })
134
+ }
142
135
  }
143
- }
144
- })
145
- }
136
+ })
137
+ }
146
138
 
147
- return true
148
- }
139
+ return true
140
+ }
@@ -18,8 +18,10 @@ declare module '@tiptap/core' {
18
18
  }
19
19
  }
20
20
 
21
- export const wrapIn: RawCommands['wrapIn'] = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
22
- const type = getNodeType(typeOrName, state.schema)
21
+ export const wrapIn: RawCommands['wrapIn'] =
22
+ (typeOrName, attributes = {}) =>
23
+ ({ state, dispatch }) => {
24
+ const type = getNodeType(typeOrName, state.schema)
23
25
 
24
- return originalWrapIn(type, attributes)(state, dispatch)
25
- }
26
+ return originalWrapIn(type, attributes)(state, dispatch)
27
+ }
@@ -18,8 +18,10 @@ declare module '@tiptap/core' {
18
18
  }
19
19
  }
20
20
 
21
- export const wrapInList: RawCommands['wrapInList'] = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
22
- const type = getNodeType(typeOrName, state.schema)
21
+ export const wrapInList: RawCommands['wrapInList'] =
22
+ (typeOrName, attributes = {}) =>
23
+ ({ state, dispatch }) => {
24
+ const type = getNodeType(typeOrName, state.schema)
23
25
 
24
- return originalWrapInList(type, attributes)(state, dispatch)
25
- }
26
+ return originalWrapInList(type, attributes)(state, dispatch)
27
+ }
@@ -5,7 +5,7 @@ import { getTextBetween } from '../helpers/getTextBetween.js'
5
5
  import { getTextSerializersFromSchema } from '../helpers/getTextSerializersFromSchema.js'
6
6
 
7
7
  export type ClipboardTextSerializerOptions = {
8
- blockSeparator?: string,
8
+ blockSeparator?: string
9
9
  }
10
10
 
11
11
  export const ClipboardTextSerializer = Extension.create<ClipboardTextSerializerOptions>({
@@ -33,9 +33,7 @@ export const ClipboardTextSerializer = Extension.create<ClipboardTextSerializerO
33
33
  const range = { from, to }
34
34
 
35
35
  return getTextBetween(doc, range, {
36
- ...(this.options.blockSeparator !== undefined
37
- ? { blockSeparator: this.options.blockSeparator }
38
- : {}),
36
+ ...(this.options.blockSeparator !== undefined ? { blockSeparator: this.options.blockSeparator } : {}),
39
37
  textSerializers,
40
38
  })
41
39
  },
@@ -16,9 +16,7 @@ export const FocusEvents = Extension.create({
16
16
  focus: (view, event: Event) => {
17
17
  editor.isFocused = true
18
18
 
19
- const transaction = editor.state.tr
20
- .setMeta('focus', { event })
21
- .setMeta('addToHistory', false)
19
+ const transaction = editor.state.tr.setMeta('focus', { event }).setMeta('addToHistory', false)
22
20
 
23
21
  view.dispatch(transaction)
24
22
 
@@ -27,9 +25,7 @@ export const FocusEvents = Extension.create({
27
25
  blur: (view, event: Event) => {
28
26
  editor.isFocused = false
29
27
 
30
- const transaction = editor.state.tr
31
- .setMeta('blur', { event })
32
- .setMeta('addToHistory', false)
28
+ const transaction = editor.state.tr.setMeta('blur', { event }).setMeta('addToHistory', false)
33
29
 
34
30
  view.dispatch(transaction)
35
31
 
@@ -11,54 +11,59 @@ export const Keymap = Extension.create({
11
11
  name: 'keymap',
12
12
 
13
13
  addKeyboardShortcuts() {
14
- const handleBackspace = () => this.editor.commands.first(({ commands }) => [
15
- () => commands.undoInputRule(),
16
-
17
- // maybe convert first text block node to default node
18
- () => commands.command(({ tr }) => {
19
- const { selection, doc } = tr
20
- const { empty, $anchor } = selection
21
- const { pos, parent } = $anchor
22
- const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor
23
- const parentIsIsolating = $parentPos.parent.type.spec.isolating
24
-
25
- const parentPos = $anchor.pos - $anchor.parentOffset
26
-
27
- const isAtStart = (parentIsIsolating && $parentPos.parent.childCount === 1)
28
- ? parentPos === $anchor.pos
29
- : Selection.atStart(doc).from === pos
30
-
31
- if (
32
- !empty
33
- || !parent.type.isTextblock
34
- || parent.textContent.length
35
- || !isAtStart
36
- || (isAtStart && $anchor.parent.type.name === 'paragraph') // prevent clearNodes when no nodes to clear, otherwise history stack is appended
37
- ) {
38
- return false
39
- }
40
-
41
- return commands.clearNodes()
42
- }),
43
-
44
- () => commands.deleteSelection(),
45
- () => commands.joinBackward(),
46
- () => commands.selectNodeBackward(),
47
- ])
48
-
49
- const handleDelete = () => this.editor.commands.first(({ commands }) => [
50
- () => commands.deleteSelection(),
51
- () => commands.deleteCurrentNode(),
52
- () => commands.joinForward(),
53
- () => commands.selectNodeForward(),
54
- ])
55
-
56
- const handleEnter = () => this.editor.commands.first(({ commands }) => [
57
- () => commands.newlineInCode(),
58
- () => commands.createParagraphNear(),
59
- () => commands.liftEmptyBlock(),
60
- () => commands.splitBlock(),
61
- ])
14
+ const handleBackspace = () =>
15
+ this.editor.commands.first(({ commands }) => [
16
+ () => commands.undoInputRule(),
17
+
18
+ // maybe convert first text block node to default node
19
+ () =>
20
+ commands.command(({ tr }) => {
21
+ const { selection, doc } = tr
22
+ const { empty, $anchor } = selection
23
+ const { pos, parent } = $anchor
24
+ const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor
25
+ const parentIsIsolating = $parentPos.parent.type.spec.isolating
26
+
27
+ const parentPos = $anchor.pos - $anchor.parentOffset
28
+
29
+ const isAtStart =
30
+ parentIsIsolating && $parentPos.parent.childCount === 1
31
+ ? parentPos === $anchor.pos
32
+ : Selection.atStart(doc).from === pos
33
+
34
+ if (
35
+ !empty ||
36
+ !parent.type.isTextblock ||
37
+ parent.textContent.length ||
38
+ !isAtStart ||
39
+ (isAtStart && $anchor.parent.type.name === 'paragraph') // prevent clearNodes when no nodes to clear, otherwise history stack is appended
40
+ ) {
41
+ return false
42
+ }
43
+
44
+ return commands.clearNodes()
45
+ }),
46
+
47
+ () => commands.deleteSelection(),
48
+ () => commands.joinBackward(),
49
+ () => commands.selectNodeBackward(),
50
+ ])
51
+
52
+ const handleDelete = () =>
53
+ this.editor.commands.first(({ commands }) => [
54
+ () => commands.deleteSelection(),
55
+ () => commands.deleteCurrentNode(),
56
+ () => commands.joinForward(),
57
+ () => commands.selectNodeForward(),
58
+ ])
59
+
60
+ const handleEnter = () =>
61
+ this.editor.commands.first(({ commands }) => [
62
+ () => commands.newlineInCode(),
63
+ () => commands.createParagraphNear(),
64
+ () => commands.liftEmptyBlock(),
65
+ () => commands.splitBlock(),
66
+ ])
62
67
 
63
68
  const baseKeymap = {
64
69
  Enter: handleEnter,
@@ -104,8 +109,7 @@ export const Keymap = Extension.create({
104
109
  new Plugin({
105
110
  key: new PluginKey('clearDocument'),
106
111
  appendTransaction: (transactions, oldState, newState) => {
107
- const docChanges = transactions.some(transaction => transaction.docChanged)
108
- && !oldState.doc.eq(newState.doc)
112
+ const docChanges = transactions.some(transaction => transaction.docChanged) && !oldState.doc.eq(newState.doc)
109
113
 
110
114
  const ignoreTr = transactions.some(transaction => transaction.getMeta('preventClearDocument'))
111
115
 
@@ -6,7 +6,6 @@ export const Paste = Extension.create({
6
6
  name: 'paste',
7
7
 
8
8
  addProseMirrorPlugins() {
9
-
10
9
  return [
11
10
  new Plugin({
12
11
  key: new PluginKey('tiptapPaste'),
@@ -10,7 +10,7 @@ export const Tabindex = Extension.create({
10
10
  new Plugin({
11
11
  key: new PluginKey('tabindex'),
12
12
  props: {
13
- attributes: (): { [name: string]: string; } => (this.editor.isEditable ? { tabindex: '0' } : {}),
13
+ attributes: (): { [name: string]: string } => (this.editor.isEditable ? { tabindex: '0' } : {}),
14
14
  },
15
15
  }),
16
16
  ]
@@ -8,10 +8,7 @@ import { Transform } from '@tiptap/pm/transform'
8
8
  * @param transactions The transactions to combine
9
9
  * @returns A new `Transform` with all steps of the passed transactions
10
10
  */
11
- export function combineTransactionSteps(
12
- oldDoc: ProseMirrorNode,
13
- transactions: Transaction[],
14
- ): Transform {
11
+ export function combineTransactionSteps(oldDoc: ProseMirrorNode, transactions: Transaction[]): Transform {
15
12
  const transform = new Transform(oldDoc)
16
13
 
17
14
  transactions.forEach(transaction => {
@@ -5,10 +5,7 @@ import { EditorState, Transaction } from '@tiptap/pm/state'
5
5
  * @param config The transaction and state to create the chainable state from
6
6
  * @returns A chainable Editor state object
7
7
  */
8
- export function createChainableState(config: {
9
- transaction: Transaction
10
- state: EditorState
11
- }): EditorState {
8
+ export function createChainableState(config: { transaction: Transaction; state: EditorState }): EditorState {
12
9
  const { state, transaction } = config
13
10
  let { selection } = transaction
14
11
  let { doc } = transaction
@@ -1,6 +1,4 @@
1
- import {
2
- Fragment, Node as ProseMirrorNode, ParseOptions, Schema,
3
- } from '@tiptap/pm/model'
1
+ import { Fragment, Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model'
4
2
 
5
3
  import { Content } from '../types.js'
6
4
  import { createNodeFromContent } from './createNodeFromContent.js'
@@ -1,10 +1,4 @@
1
- import {
2
- DOMParser,
3
- Fragment,
4
- Node as ProseMirrorNode,
5
- ParseOptions,
6
- Schema,
7
- } from '@tiptap/pm/model'
1
+ import { DOMParser, Fragment, Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model'
8
2
 
9
3
  import { Content } from '../types.js'
10
4
  import { elementFromString } from '../utilities/elementFromString.js'
@@ -67,7 +61,6 @@ export function createNodeFromContent(
67
61
  }
68
62
 
69
63
  if (isTextContent) {
70
-
71
64
  // Check for invalid content
72
65
  if (options.errorOnInvalidContent) {
73
66
  let hasInvalidContent = false
@@ -106,7 +99,9 @@ export function createNodeFromContent(
106
99
  }
107
100
 
108
101
  if (options.errorOnInvalidContent && hasInvalidContent) {
109
- throw new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) })
102
+ throw new Error('[tiptap error]: Invalid HTML content', {
103
+ cause: new Error(`Invalid element found: ${invalidContent}`),
104
+ })
110
105
  }
111
106
  }
112
107
 
@@ -117,7 +112,6 @@ export function createNodeFromContent(
117
112
  }
118
113
 
119
114
  return parser.parse(elementFromString(content), options.parseOptions)
120
-
121
115
  }
122
116
 
123
117
  return createNodeFromContent('', schema, options)
@@ -9,11 +9,7 @@ import { NodeWithPos, Predicate, Range } from '../types.js'
9
9
  * @param predicate The predicate to match
10
10
  * @returns An array of nodes with their positions
11
11
  */
12
- export function findChildrenInRange(
13
- node: ProseMirrorNode,
14
- range: Range,
15
- predicate: Predicate,
16
- ): NodeWithPos[] {
12
+ export function findChildrenInRange(node: ProseMirrorNode, range: Range, predicate: Predicate): NodeWithPos[] {
17
13
  const nodesWithPos: NodeWithPos[] = []
18
14
 
19
15
  // if (range.from === range.to) {
@@ -11,6 +11,8 @@ import { findParentNodeClosestToPos } from './findParentNodeClosestToPos.js'
11
11
  * findParentNode(node => node.type.name === 'paragraph')
12
12
  * ```
13
13
  */
14
- export function findParentNode(predicate: Predicate) {
14
+ export function findParentNode(
15
+ predicate: Predicate,
16
+ ): (selection: Selection) => ReturnType<typeof findParentNodeClosestToPos> {
15
17
  return (selection: Selection) => findParentNodeClosestToPos(selection.$from, predicate)
16
18
  }
@@ -0,0 +1,30 @@
1
+ import { AnyConfig, Extensions } from '../types.js'
2
+ import { getExtensionField } from './getExtensionField.js'
3
+
4
+ /**
5
+ * Create a flattened array of extensions by traversing the `addExtensions` field.
6
+ * @param extensions An array of Tiptap extensions
7
+ * @returns A flattened array of Tiptap extensions
8
+ */
9
+ export function flattenExtensions(extensions: Extensions): Extensions {
10
+ return (
11
+ extensions
12
+ .map(extension => {
13
+ const context = {
14
+ name: extension.name,
15
+ options: extension.options,
16
+ storage: extension.storage,
17
+ }
18
+
19
+ const addExtensions = getExtensionField<AnyConfig['addExtensions']>(extension, 'addExtensions', context)
20
+
21
+ if (addExtensions) {
22
+ return [extension, ...flattenExtensions(addExtensions())]
23
+ }
24
+
25
+ return extension
26
+ })
27
+ // `Infinity` will break TypeScript so we set a number that is probably high enough
28
+ .flat(10)
29
+ )
30
+ }
@@ -11,10 +11,7 @@ import { getSchemaTypeNameByName } from './getSchemaTypeNameByName.js'
11
11
  * @param typeOrName The node or mark type or name
12
12
  * @returns The attributes of the node or mark or an empty object
13
13
  */
14
- export function getAttributes(
15
- state: EditorState,
16
- typeOrName: string | NodeType | MarkType,
17
- ): Record<string, any> {
14
+ export function getAttributes(state: EditorState, typeOrName: string | NodeType | MarkType): Record<string, any> {
18
15
  const schemaType = getSchemaTypeNameByName(
19
16
  typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
20
17
  state.schema,