@tiptap/core 3.0.0-next.0 → 3.0.0-next.2

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 (214) hide show
  1. package/dist/index.cjs +4934 -4574
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +3576 -0
  4. package/dist/index.d.ts +3576 -0
  5. package/dist/index.js +4839 -4481
  6. package/dist/index.js.map +1 -1
  7. package/package.json +6 -8
  8. package/src/Editor.ts +66 -10
  9. package/src/EventEmitter.ts +9 -0
  10. package/src/Extension.ts +4 -3
  11. package/src/ExtensionManager.ts +18 -12
  12. package/src/InputRule.ts +45 -30
  13. package/src/Mark.ts +4 -3
  14. package/src/Node.ts +23 -3
  15. package/src/NodePos.ts +9 -4
  16. package/src/NodeView.ts +43 -12
  17. package/src/PasteRule.ts +96 -42
  18. package/src/commands/focus.ts +1 -6
  19. package/src/commands/insertContent.ts +9 -9
  20. package/src/commands/insertContentAt.ts +23 -3
  21. package/src/commands/selectAll.ts +10 -5
  22. package/src/commands/setContent.ts +10 -14
  23. package/src/commands/setMeta.ts +3 -1
  24. package/src/commands/setNode.ts +9 -2
  25. package/src/commands/splitListItem.ts +27 -17
  26. package/src/commands/toggleNode.ts +11 -2
  27. package/src/commands/updateAttributes.ts +72 -12
  28. package/src/extensions/drop.ts +26 -0
  29. package/src/extensions/index.ts +2 -0
  30. package/src/extensions/keymap.ts +5 -2
  31. package/src/extensions/paste.ts +26 -0
  32. package/src/helpers/createDocument.ts +4 -2
  33. package/src/helpers/createNodeFromContent.ts +11 -2
  34. package/src/helpers/getAttributesFromExtensions.ts +1 -1
  35. package/src/helpers/getMarkRange.ts +35 -8
  36. package/src/helpers/getMarksBetween.ts +1 -1
  37. package/src/helpers/getRenderedAttributes.ts +3 -0
  38. package/src/helpers/getSchemaByResolvedExtensions.ts +3 -2
  39. package/src/helpers/isList.ts +1 -1
  40. package/src/helpers/isNodeEmpty.ts +33 -12
  41. package/src/inputRules/markInputRule.ts +1 -1
  42. package/src/inputRules/nodeInputRule.ts +1 -1
  43. package/src/inputRules/textInputRule.ts +1 -1
  44. package/src/inputRules/textblockTypeInputRule.ts +1 -1
  45. package/src/inputRules/wrappingInputRule.ts +1 -1
  46. package/src/pasteRules/markPasteRule.ts +1 -1
  47. package/src/pasteRules/nodePasteRule.ts +15 -6
  48. package/src/pasteRules/textPasteRule.ts +1 -1
  49. package/src/style.ts +2 -2
  50. package/src/types.ts +107 -19
  51. package/src/utilities/mergeAttributes.ts +18 -1
  52. package/dist/index.umd.js +0 -5130
  53. package/dist/index.umd.js.map +0 -1
  54. package/dist/packages/core/src/CommandManager.d.ts +0 -20
  55. package/dist/packages/core/src/Editor.d.ts +0 -159
  56. package/dist/packages/core/src/EventEmitter.d.ts +0 -11
  57. package/dist/packages/core/src/Extension.d.ts +0 -343
  58. package/dist/packages/core/src/ExtensionManager.d.ts +0 -55
  59. package/dist/packages/core/src/InputRule.d.ts +0 -42
  60. package/dist/packages/core/src/Mark.d.ts +0 -451
  61. package/dist/packages/core/src/Node.d.ts +0 -611
  62. package/dist/packages/core/src/NodePos.d.ts +0 -44
  63. package/dist/packages/core/src/NodeView.d.ts +0 -31
  64. package/dist/packages/core/src/PasteRule.d.ts +0 -50
  65. package/dist/packages/core/src/Tracker.d.ts +0 -11
  66. package/dist/packages/core/src/commands/blur.d.ts +0 -13
  67. package/dist/packages/core/src/commands/clearContent.d.ts +0 -14
  68. package/dist/packages/core/src/commands/clearNodes.d.ts +0 -13
  69. package/dist/packages/core/src/commands/command.d.ts +0 -18
  70. package/dist/packages/core/src/commands/createParagraphNear.d.ts +0 -13
  71. package/dist/packages/core/src/commands/cut.d.ts +0 -20
  72. package/dist/packages/core/src/commands/deleteCurrentNode.d.ts +0 -13
  73. package/dist/packages/core/src/commands/deleteNode.d.ts +0 -15
  74. package/dist/packages/core/src/commands/deleteRange.d.ts +0 -14
  75. package/dist/packages/core/src/commands/deleteSelection.d.ts +0 -13
  76. package/dist/packages/core/src/commands/enter.d.ts +0 -13
  77. package/dist/packages/core/src/commands/exitCode.d.ts +0 -13
  78. package/dist/packages/core/src/commands/extendMarkRange.d.ts +0 -25
  79. package/dist/packages/core/src/commands/first.d.ts +0 -14
  80. package/dist/packages/core/src/commands/focus.d.ts +0 -27
  81. package/dist/packages/core/src/commands/forEach.d.ts +0 -14
  82. package/dist/packages/core/src/commands/index.d.ts +0 -55
  83. package/dist/packages/core/src/commands/insertContent.d.ts +0 -34
  84. package/dist/packages/core/src/commands/insertContentAt.d.ts +0 -47
  85. package/dist/packages/core/src/commands/join.d.ts +0 -41
  86. package/dist/packages/core/src/commands/joinItemBackward.d.ts +0 -13
  87. package/dist/packages/core/src/commands/joinItemForward.d.ts +0 -13
  88. package/dist/packages/core/src/commands/joinTextblockBackward.d.ts +0 -12
  89. package/dist/packages/core/src/commands/joinTextblockForward.d.ts +0 -12
  90. package/dist/packages/core/src/commands/keyboardShortcut.d.ts +0 -14
  91. package/dist/packages/core/src/commands/lift.d.ts +0 -17
  92. package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +0 -13
  93. package/dist/packages/core/src/commands/liftListItem.d.ts +0 -15
  94. package/dist/packages/core/src/commands/newlineInCode.d.ts +0 -13
  95. package/dist/packages/core/src/commands/resetAttributes.d.ts +0 -16
  96. package/dist/packages/core/src/commands/scrollIntoView.d.ts +0 -13
  97. package/dist/packages/core/src/commands/selectAll.d.ts +0 -13
  98. package/dist/packages/core/src/commands/selectNodeBackward.d.ts +0 -13
  99. package/dist/packages/core/src/commands/selectNodeForward.d.ts +0 -13
  100. package/dist/packages/core/src/commands/selectParentNode.d.ts +0 -13
  101. package/dist/packages/core/src/commands/selectTextblockEnd.d.ts +0 -13
  102. package/dist/packages/core/src/commands/selectTextblockStart.d.ts +0 -13
  103. package/dist/packages/core/src/commands/setContent.d.ts +0 -40
  104. package/dist/packages/core/src/commands/setMark.d.ts +0 -15
  105. package/dist/packages/core/src/commands/setMeta.d.ts +0 -15
  106. package/dist/packages/core/src/commands/setNode.d.ts +0 -16
  107. package/dist/packages/core/src/commands/setNodeSelection.d.ts +0 -14
  108. package/dist/packages/core/src/commands/setTextSelection.d.ts +0 -14
  109. package/dist/packages/core/src/commands/sinkListItem.d.ts +0 -15
  110. package/dist/packages/core/src/commands/splitBlock.d.ts +0 -17
  111. package/dist/packages/core/src/commands/splitListItem.d.ts +0 -15
  112. package/dist/packages/core/src/commands/toggleList.d.ts +0 -18
  113. package/dist/packages/core/src/commands/toggleMark.d.ts +0 -30
  114. package/dist/packages/core/src/commands/toggleNode.d.ts +0 -17
  115. package/dist/packages/core/src/commands/toggleWrap.d.ts +0 -16
  116. package/dist/packages/core/src/commands/undoInputRule.d.ts +0 -13
  117. package/dist/packages/core/src/commands/unsetAllMarks.d.ts +0 -13
  118. package/dist/packages/core/src/commands/unsetMark.d.ts +0 -25
  119. package/dist/packages/core/src/commands/updateAttributes.d.ts +0 -24
  120. package/dist/packages/core/src/commands/wrapIn.d.ts +0 -16
  121. package/dist/packages/core/src/commands/wrapInList.d.ts +0 -16
  122. package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +0 -5
  123. package/dist/packages/core/src/extensions/commands.d.ts +0 -3
  124. package/dist/packages/core/src/extensions/editable.d.ts +0 -2
  125. package/dist/packages/core/src/extensions/focusEvents.d.ts +0 -2
  126. package/dist/packages/core/src/extensions/index.d.ts +0 -6
  127. package/dist/packages/core/src/extensions/keymap.d.ts +0 -2
  128. package/dist/packages/core/src/extensions/tabindex.d.ts +0 -2
  129. package/dist/packages/core/src/helpers/combineTransactionSteps.d.ts +0 -10
  130. package/dist/packages/core/src/helpers/createChainableState.d.ts +0 -10
  131. package/dist/packages/core/src/helpers/createDocument.d.ts +0 -12
  132. package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +0 -15
  133. package/dist/packages/core/src/helpers/defaultBlockAt.d.ts +0 -7
  134. package/dist/packages/core/src/helpers/findChildren.d.ts +0 -9
  135. package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +0 -10
  136. package/dist/packages/core/src/helpers/findParentNode.d.ts +0 -16
  137. package/dist/packages/core/src/helpers/findParentNodeClosestToPos.d.ts +0 -17
  138. package/dist/packages/core/src/helpers/generateHTML.d.ts +0 -8
  139. package/dist/packages/core/src/helpers/generateJSON.d.ts +0 -8
  140. package/dist/packages/core/src/helpers/generateText.d.ts +0 -12
  141. package/dist/packages/core/src/helpers/getAttributes.d.ts +0 -9
  142. package/dist/packages/core/src/helpers/getAttributesFromExtensions.d.ts +0 -6
  143. package/dist/packages/core/src/helpers/getChangedRanges.d.ts +0 -11
  144. package/dist/packages/core/src/helpers/getDebugJSON.d.ts +0 -8
  145. package/dist/packages/core/src/helpers/getExtensionField.d.ts +0 -9
  146. package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +0 -2
  147. package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +0 -3
  148. package/dist/packages/core/src/helpers/getMarkRange.d.ts +0 -3
  149. package/dist/packages/core/src/helpers/getMarkType.d.ts +0 -2
  150. package/dist/packages/core/src/helpers/getMarksBetween.d.ts +0 -3
  151. package/dist/packages/core/src/helpers/getNodeAtPosition.d.ts +0 -11
  152. package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +0 -3
  153. package/dist/packages/core/src/helpers/getNodeType.d.ts +0 -2
  154. package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +0 -3
  155. package/dist/packages/core/src/helpers/getSchema.d.ts +0 -4
  156. package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +0 -10
  157. package/dist/packages/core/src/helpers/getSchemaTypeByName.d.ts +0 -8
  158. package/dist/packages/core/src/helpers/getSchemaTypeNameByName.d.ts +0 -8
  159. package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +0 -9
  160. package/dist/packages/core/src/helpers/getText.d.ts +0 -15
  161. package/dist/packages/core/src/helpers/getTextBetween.d.ts +0 -14
  162. package/dist/packages/core/src/helpers/getTextContentFromNodes.d.ts +0 -8
  163. package/dist/packages/core/src/helpers/getTextSerializersFromSchema.d.ts +0 -8
  164. package/dist/packages/core/src/helpers/index.d.ts +0 -50
  165. package/dist/packages/core/src/helpers/injectExtensionAttributesToParseRule.d.ts +0 -9
  166. package/dist/packages/core/src/helpers/isActive.d.ts +0 -2
  167. package/dist/packages/core/src/helpers/isAtEndOfNode.d.ts +0 -2
  168. package/dist/packages/core/src/helpers/isAtStartOfNode.d.ts +0 -2
  169. package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +0 -2
  170. package/dist/packages/core/src/helpers/isList.d.ts +0 -2
  171. package/dist/packages/core/src/helpers/isMarkActive.d.ts +0 -3
  172. package/dist/packages/core/src/helpers/isNodeActive.d.ts +0 -3
  173. package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +0 -8
  174. package/dist/packages/core/src/helpers/isNodeSelection.d.ts +0 -2
  175. package/dist/packages/core/src/helpers/isTextSelection.d.ts +0 -2
  176. package/dist/packages/core/src/helpers/posToDOMRect.d.ts +0 -2
  177. package/dist/packages/core/src/helpers/resolveFocusPosition.d.ts +0 -4
  178. package/dist/packages/core/src/helpers/selectionToInsertionEnd.d.ts +0 -2
  179. package/dist/packages/core/src/helpers/splitExtensions.d.ts +0 -9
  180. package/dist/packages/core/src/index.d.ts +0 -24
  181. package/dist/packages/core/src/inputRules/index.d.ts +0 -5
  182. package/dist/packages/core/src/inputRules/markInputRule.d.ts +0 -13
  183. package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +0 -23
  184. package/dist/packages/core/src/inputRules/textInputRule.d.ts +0 -10
  185. package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +0 -15
  186. package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +0 -28
  187. package/dist/packages/core/src/pasteRules/index.d.ts +0 -3
  188. package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +0 -13
  189. package/dist/packages/core/src/pasteRules/nodePasteRule.d.ts +0 -13
  190. package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +0 -10
  191. package/dist/packages/core/src/style.d.ts +0 -1
  192. package/dist/packages/core/src/types.d.ts +0 -253
  193. package/dist/packages/core/src/utilities/callOrReturn.d.ts +0 -9
  194. package/dist/packages/core/src/utilities/createStyleTag.d.ts +0 -1
  195. package/dist/packages/core/src/utilities/deleteProps.d.ts +0 -6
  196. package/dist/packages/core/src/utilities/elementFromString.d.ts +0 -1
  197. package/dist/packages/core/src/utilities/escapeForRegEx.d.ts +0 -1
  198. package/dist/packages/core/src/utilities/findDuplicates.d.ts +0 -1
  199. package/dist/packages/core/src/utilities/fromString.d.ts +0 -1
  200. package/dist/packages/core/src/utilities/index.d.ts +0 -20
  201. package/dist/packages/core/src/utilities/isAndroid.d.ts +0 -1
  202. package/dist/packages/core/src/utilities/isEmptyObject.d.ts +0 -1
  203. package/dist/packages/core/src/utilities/isFunction.d.ts +0 -1
  204. package/dist/packages/core/src/utilities/isMacOS.d.ts +0 -1
  205. package/dist/packages/core/src/utilities/isNumber.d.ts +0 -1
  206. package/dist/packages/core/src/utilities/isPlainObject.d.ts +0 -1
  207. package/dist/packages/core/src/utilities/isRegExp.d.ts +0 -1
  208. package/dist/packages/core/src/utilities/isString.d.ts +0 -1
  209. package/dist/packages/core/src/utilities/isiOS.d.ts +0 -1
  210. package/dist/packages/core/src/utilities/mergeAttributes.d.ts +0 -1
  211. package/dist/packages/core/src/utilities/mergeDeep.d.ts +0 -1
  212. package/dist/packages/core/src/utilities/minMax.d.ts +0 -1
  213. package/dist/packages/core/src/utilities/objectIncludes.d.ts +0 -8
  214. package/dist/packages/core/src/utilities/removeDuplicates.d.ts +0 -8
@@ -1,4 +1,7 @@
1
- import { MarkType, NodeType } from '@tiptap/pm/model'
1
+ import {
2
+ Mark, MarkType, Node, NodeType,
3
+ } from '@tiptap/pm/model'
4
+ import { SelectionRange } from '@tiptap/pm/state'
2
5
 
3
6
  import { getMarkType } from '../helpers/getMarkType.js'
4
7
  import { getNodeType } from '../helpers/getNodeType.js'
@@ -30,6 +33,7 @@ declare module '@tiptap/core' {
30
33
  }
31
34
 
32
35
  export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
36
+
33
37
  let nodeType: NodeType | null = null
34
38
  let markType: MarkType | null = null
35
39
 
@@ -51,24 +55,80 @@ export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, at
51
55
  }
52
56
 
53
57
  if (dispatch) {
54
- tr.selection.ranges.forEach(range => {
58
+ tr.selection.ranges.forEach((range: SelectionRange) => {
59
+
55
60
  const from = range.$from.pos
56
61
  const to = range.$to.pos
57
62
 
58
- state.doc.nodesBetween(from, to, (node, pos) => {
59
- if (nodeType && nodeType === node.type) {
60
- tr.setNodeMarkup(pos, undefined, {
61
- ...node.attrs,
63
+ let lastPos: number | undefined
64
+ let lastNode: Node | undefined
65
+ let trimmedFrom: number
66
+ let trimmedTo: number
67
+
68
+ if (tr.selection.empty) {
69
+ state.doc.nodesBetween(from, to, (node: Node, pos: number) => {
70
+
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) {
89
+
90
+ if (nodeType && nodeType === node.type) {
91
+ tr.setNodeMarkup(pos, undefined, {
92
+ ...node.attrs,
93
+ ...attributes,
94
+ })
95
+ }
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
+ })
114
+ }
115
+ }
116
+ })
117
+ }
118
+
119
+ if (lastNode) {
120
+
121
+ if (lastPos !== undefined) {
122
+ tr.setNodeMarkup(lastPos, undefined, {
123
+ ...lastNode.attrs,
62
124
  ...attributes,
63
125
  })
64
126
  }
65
127
 
66
- if (markType && node.marks.length) {
67
- node.marks.forEach(mark => {
68
- if (markType === mark.type) {
69
- const trimmedFrom = Math.max(pos, from)
70
- const trimmedTo = Math.min(pos + node.nodeSize, to)
128
+ if (markType && lastNode.marks.length) {
129
+ lastNode.marks.forEach((mark: Mark) => {
71
130
 
131
+ if (markType === mark.type) {
72
132
  tr.addMark(
73
133
  trimmedFrom,
74
134
  trimmedTo,
@@ -80,7 +140,7 @@ export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, at
80
140
  }
81
141
  })
82
142
  }
83
- })
143
+ }
84
144
  })
85
145
  }
86
146
 
@@ -0,0 +1,26 @@
1
+ import { Plugin, PluginKey } from '@tiptap/pm/state'
2
+
3
+ import { Extension } from '../Extension.js'
4
+
5
+ export const Drop = Extension.create({
6
+ name: 'drop',
7
+
8
+ addProseMirrorPlugins() {
9
+ return [
10
+ new Plugin({
11
+ key: new PluginKey('tiptapDrop'),
12
+
13
+ props: {
14
+ handleDrop: (_, e, slice, moved) => {
15
+ this.editor.emit('drop', {
16
+ editor: this.editor,
17
+ event: e,
18
+ slice,
19
+ moved,
20
+ })
21
+ },
22
+ },
23
+ }),
24
+ ]
25
+ },
26
+ })
@@ -1,6 +1,8 @@
1
1
  export { ClipboardTextSerializer } from './clipboardTextSerializer.js'
2
2
  export { Commands } from './commands.js'
3
+ export { Drop } from './drop.js'
3
4
  export { Editable } from './editable.js'
4
5
  export { FocusEvents } from './focusEvents.js'
5
6
  export { Keymap } from './keymap.js'
7
+ export { Paste } from './paste.js'
6
8
  export { Tabindex } from './tabindex.js'
@@ -3,6 +3,7 @@ import { Plugin, PluginKey, Selection } from '@tiptap/pm/state'
3
3
  import { CommandManager } from '../CommandManager.js'
4
4
  import { Extension } from '../Extension.js'
5
5
  import { createChainableState } from '../helpers/createChainableState.js'
6
+ import { isNodeEmpty } from '../helpers/isNodeEmpty.js'
6
7
  import { isiOS } from '../utilities/isiOS.js'
7
8
  import { isMacOS } from '../utilities/isMacOS.js'
8
9
 
@@ -106,7 +107,9 @@ export const Keymap = Extension.create({
106
107
  const docChanges = transactions.some(transaction => transaction.docChanged)
107
108
  && !oldState.doc.eq(newState.doc)
108
109
 
109
- if (!docChanges) {
110
+ const ignoreTr = transactions.some(transaction => transaction.getMeta('preventClearDocument'))
111
+
112
+ if (!docChanges || ignoreTr) {
110
113
  return
111
114
  }
112
115
 
@@ -119,7 +122,7 @@ export const Keymap = Extension.create({
119
122
  return
120
123
  }
121
124
 
122
- const isEmpty = newState.doc.textBetween(0, newState.doc.content.size, ' ', ' ').length === 0
125
+ const isEmpty = isNodeEmpty(newState.doc)
123
126
 
124
127
  if (!isEmpty) {
125
128
  return
@@ -0,0 +1,26 @@
1
+ import { Plugin, PluginKey } from '@tiptap/pm/state'
2
+
3
+ import { Extension } from '../Extension.js'
4
+
5
+ export const Paste = Extension.create({
6
+ name: 'paste',
7
+
8
+ addProseMirrorPlugins() {
9
+
10
+ return [
11
+ new Plugin({
12
+ key: new PluginKey('tiptapPaste'),
13
+
14
+ props: {
15
+ handlePaste: (_view, e, slice) => {
16
+ this.editor.emit('paste', {
17
+ editor: this.editor,
18
+ event: e,
19
+ slice,
20
+ })
21
+ },
22
+ },
23
+ }),
24
+ ]
25
+ },
26
+ })
@@ -1,4 +1,6 @@
1
- import { Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model'
1
+ import {
2
+ Fragment, Node as ProseMirrorNode, ParseOptions, Schema,
3
+ } from '@tiptap/pm/model'
2
4
 
3
5
  import { Content } from '../types.js'
4
6
  import { createNodeFromContent } from './createNodeFromContent.js'
@@ -11,7 +13,7 @@ import { createNodeFromContent } from './createNodeFromContent.js'
11
13
  * @returns The created Prosemirror document node
12
14
  */
13
15
  export function createDocument(
14
- content: Content,
16
+ content: Content | ProseMirrorNode | Fragment,
15
17
  schema: Schema,
16
18
  parseOptions: ParseOptions = {},
17
19
  options: { errorOnInvalidContent?: boolean } = {},
@@ -23,10 +23,13 @@ export type CreateNodeFromContentOptions = {
23
23
  * @returns The created Prosemirror node or fragment
24
24
  */
25
25
  export function createNodeFromContent(
26
- content: Content,
26
+ content: Content | ProseMirrorNode | Fragment,
27
27
  schema: Schema,
28
28
  options?: CreateNodeFromContentOptions,
29
29
  ): ProseMirrorNode | Fragment {
30
+ if (content instanceof ProseMirrorNode || content instanceof Fragment) {
31
+ return content
32
+ }
30
33
  options = {
31
34
  slice: true,
32
35
  parseOptions: {},
@@ -45,7 +48,13 @@ export function createNodeFromContent(
45
48
  return Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)))
46
49
  }
47
50
 
48
- return schema.nodeFromJSON(content)
51
+ const node = schema.nodeFromJSON(content)
52
+
53
+ if (options.errorOnInvalidContent) {
54
+ node.check()
55
+ }
56
+
57
+ return node
49
58
  } catch (error) {
50
59
  if (options.errorOnInvalidContent) {
51
60
  throw new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })
@@ -1,4 +1,4 @@
1
- import { MarkConfig, NodeConfig } from '../index.js'
1
+ import type { MarkConfig, NodeConfig } from '../index.js'
2
2
  import {
3
3
  AnyConfig,
4
4
  Attribute,
@@ -9,7 +9,14 @@ function findMarkInSet(
9
9
  attributes: Record<string, any> = {},
10
10
  ): ProseMirrorMark | undefined {
11
11
  return marks.find(item => {
12
- return item.type === type && objectIncludes(item.attrs, attributes)
12
+ return (
13
+ item.type === type
14
+ && objectIncludes(
15
+ // Only check equality for the attributes that are provided
16
+ Object.fromEntries(Object.keys(attributes).map(k => [k, item.attrs[k]])),
17
+ attributes,
18
+ )
19
+ )
13
20
  })
14
21
  }
15
22
 
@@ -21,25 +28,44 @@ function isMarkInSet(
21
28
  return !!findMarkInSet(marks, type, attributes)
22
29
  }
23
30
 
31
+ /**
32
+ * Get the range of a mark at a resolved position.
33
+ */
24
34
  export function getMarkRange(
35
+ /**
36
+ * The position to get the mark range for.
37
+ */
25
38
  $pos: ResolvedPos,
39
+ /**
40
+ * The mark type to get the range for.
41
+ */
26
42
  type: MarkType,
27
- attributes: Record<string, any> = {},
43
+ /**
44
+ * The attributes to match against.
45
+ * If not provided, only the first mark at the position will be matched.
46
+ */
47
+ attributes?: Record<string, any>,
28
48
  ): Range | void {
29
49
  if (!$pos || !type) {
30
50
  return
31
51
  }
32
-
33
52
  let start = $pos.parent.childAfter($pos.parentOffset)
34
53
 
35
- if ($pos.parentOffset === start.offset && start.offset !== 0) {
54
+ // If the cursor is at the start of a text node that does not have the mark, look backward
55
+ if (!start.node || !start.node.marks.some(mark => mark.type === type)) {
36
56
  start = $pos.parent.childBefore($pos.parentOffset)
37
57
  }
38
58
 
39
- if (!start.node) {
59
+ // If there is no text node with the mark even backward, return undefined
60
+ if (!start.node || !start.node.marks.some(mark => mark.type === type)) {
40
61
  return
41
62
  }
42
63
 
64
+ // Default to only matching against the first mark's attributes
65
+ attributes = attributes || start.node.marks[0]?.attrs
66
+
67
+ // We now know that the cursor is either at the start, middle or end of a text node with the specified mark
68
+ // so we can look it up on the targeted mark
43
69
  const mark = findMarkInSet([...start.node.marks], type, attributes)
44
70
 
45
71
  if (!mark) {
@@ -51,9 +77,10 @@ export function getMarkRange(
51
77
  let endIndex = startIndex + 1
52
78
  let endPos = startPos + start.node.nodeSize
53
79
 
54
- findMarkInSet([...start.node.marks], type, attributes)
55
-
56
- while (startIndex > 0 && mark.isInSet($pos.parent.child(startIndex - 1).marks)) {
80
+ while (
81
+ startIndex > 0
82
+ && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)
83
+ ) {
57
84
  startIndex -= 1
58
85
  startPos -= $pos.parent.child(startIndex).nodeSize
59
86
  }
@@ -12,7 +12,7 @@ export function getMarksBetween(from: number, to: number, doc: ProseMirrorNode):
12
12
  .resolve(from)
13
13
  .marks()
14
14
  .forEach(mark => {
15
- const $pos = doc.resolve(from - 1)
15
+ const $pos = doc.resolve(from)
16
16
  const range = getMarkRange($pos, mark.type)
17
17
 
18
18
  if (!range) {
@@ -8,6 +8,9 @@ export function getRenderedAttributes(
8
8
  extensionAttributes: ExtensionAttribute[],
9
9
  ): Record<string, any> {
10
10
  return extensionAttributes
11
+ .filter(
12
+ attribute => attribute.type === nodeOrMark.type.name,
13
+ )
11
14
  .filter(item => item.attribute.rendered)
12
15
  .map(item => {
13
16
  if (!item.attribute.renderHTML) {
@@ -2,7 +2,7 @@ import {
2
2
  MarkSpec, NodeSpec, Schema, TagParseRule,
3
3
  } from '@tiptap/pm/model'
4
4
 
5
- import { Editor, MarkConfig, NodeConfig } from '../index.js'
5
+ import type { Editor, MarkConfig, NodeConfig } from '../index.js'
6
6
  import { AnyConfig, Extensions } from '../types.js'
7
7
  import { callOrReturn } from '../utilities/callOrReturn.js'
8
8
  import { isEmptyObject } from '../utilities/isEmptyObject.js'
@@ -78,6 +78,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
78
78
  ),
79
79
  code: callOrReturn(getExtensionField<NodeConfig['code']>(extension, 'code', context)),
80
80
  whitespace: callOrReturn(getExtensionField<NodeConfig['whitespace']>(extension, 'whitespace', context)),
81
+ linebreakReplacement: callOrReturn(getExtensionField<NodeConfig['linebreakReplacement']>(extension, 'linebreakReplacement', context)),
81
82
  defining: callOrReturn(
82
83
  getExtensionField<NodeConfig['defining']>(extension, 'defining', context),
83
84
  ),
@@ -147,7 +148,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
147
148
 
148
149
  return {
149
150
  ...fields,
150
- ...(extendMarkSchema ? extendMarkSchema(extension) : {}),
151
+ ...(extendMarkSchema ? extendMarkSchema(extension as any) : {}),
151
152
  }
152
153
  }, {})
153
154
 
@@ -1,5 +1,5 @@
1
1
  import { getExtensionField } from '../helpers/getExtensionField.js'
2
- import { NodeConfig } from '../index.js'
2
+ import type { NodeConfig } from '../index.js'
3
3
  import { Extensions } from '../types.js'
4
4
  import { callOrReturn } from '../utilities/callOrReturn.js'
5
5
  import { splitExtensions } from './splitExtensions.js'
@@ -1,40 +1,61 @@
1
1
  import { Node as ProseMirrorNode } from '@tiptap/pm/model'
2
2
 
3
3
  /**
4
- * Returns true if the given node is empty.
5
- * When `checkChildren` is true (default), it will also check if all children are empty.
4
+ * Returns true if the given prosemirror node is empty.
6
5
  */
7
6
  export function isNodeEmpty(
8
7
  node: ProseMirrorNode,
9
- { checkChildren }: { checkChildren: boolean } = { checkChildren: true },
8
+ {
9
+ checkChildren = true,
10
+ ignoreWhitespace = false,
11
+ }: {
12
+ /**
13
+ * When true (default), it will also check if all children are empty.
14
+ */
15
+ checkChildren?: boolean;
16
+ /**
17
+ * When true, it will ignore whitespace when checking for emptiness.
18
+ */
19
+ ignoreWhitespace?: boolean;
20
+ } = {},
10
21
  ): boolean {
22
+ if (ignoreWhitespace) {
23
+ if (node.type.name === 'hardBreak') {
24
+ // Hard breaks are considered empty
25
+ return true
26
+ }
27
+ if (node.isText) {
28
+ return /^\s*$/m.test(node.text ?? '')
29
+ }
30
+ }
31
+
11
32
  if (node.isText) {
12
33
  return !node.text
13
34
  }
14
35
 
15
- if (node.content.childCount === 0) {
16
- return true
36
+ if (node.isAtom || node.isLeaf) {
37
+ return false
17
38
  }
18
39
 
19
- if (node.isLeaf) {
20
- return false
40
+ if (node.content.childCount === 0) {
41
+ return true
21
42
  }
22
43
 
23
44
  if (checkChildren) {
24
- let hasSameContent = true
45
+ let isContentEmpty = true
25
46
 
26
47
  node.content.forEach(childNode => {
27
- if (hasSameContent === false) {
48
+ if (isContentEmpty === false) {
28
49
  // Exit early for perf
29
50
  return
30
51
  }
31
52
 
32
- if (!isNodeEmpty(childNode)) {
33
- hasSameContent = false
53
+ if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {
54
+ isContentEmpty = false
34
55
  }
35
56
  })
36
57
 
37
- return hasSameContent
58
+ return isContentEmpty
38
59
  }
39
60
 
40
61
  return false
@@ -8,7 +8,7 @@ import { callOrReturn } from '../utilities/callOrReturn.js'
8
8
  /**
9
9
  * Build an input rule that adds a mark when the
10
10
  * matched text is typed into it.
11
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
11
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
12
12
  */
13
13
  export function markInputRule(config: {
14
14
  find: InputRuleFinder
@@ -7,7 +7,7 @@ import { callOrReturn } from '../utilities/callOrReturn.js'
7
7
  /**
8
8
  * Build an input rule that adds a node when the
9
9
  * matched text is typed into it.
10
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
10
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
11
11
  */
12
12
  export function nodeInputRule(config: {
13
13
  /**
@@ -3,7 +3,7 @@ import { InputRule, InputRuleFinder } from '../InputRule.js'
3
3
  /**
4
4
  * Build an input rule that replaces text when the
5
5
  * matched text is typed into it.
6
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
6
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
7
7
  */
8
8
  export function textInputRule(config: {
9
9
  find: InputRuleFinder,
@@ -9,7 +9,7 @@ import { callOrReturn } from '../utilities/callOrReturn.js'
9
9
  * matched text is typed into it. When using a regular expresion you’ll
10
10
  * probably want the regexp to start with `^`, so that the pattern can
11
11
  * only occur at the start of a textblock.
12
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
12
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
13
13
  */
14
14
  export function textblockTypeInputRule(config: {
15
15
  find: InputRuleFinder
@@ -19,7 +19,7 @@ import { callOrReturn } from '../utilities/callOrReturn.js'
19
19
  * two nodes. You can pass a join predicate, which takes a regular
20
20
  * expression match and the node before the wrapped node, and can
21
21
  * return a boolean to indicate whether a join should happen.
22
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
22
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
23
23
  */
24
24
  export function wrappingInputRule(config: {
25
25
  find: InputRuleFinder,
@@ -8,7 +8,7 @@ import { callOrReturn } from '../utilities/callOrReturn.js'
8
8
  /**
9
9
  * Build an paste rule that adds a mark when the
10
10
  * matched text is pasted into it.
11
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
11
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
12
12
  */
13
13
  export function markPasteRule(config: {
14
14
  find: PasteRuleFinder
@@ -1,13 +1,13 @@
1
1
  import { NodeType } from '@tiptap/pm/model'
2
2
 
3
3
  import { PasteRule, PasteRuleFinder } from '../PasteRule.js'
4
- import { ExtendedRegExpMatchArray } from '../types.js'
4
+ import { ExtendedRegExpMatchArray, JSONContent } from '../types.js'
5
5
  import { callOrReturn } from '../utilities/index.js'
6
6
 
7
7
  /**
8
8
  * Build an paste rule that adds a node when the
9
9
  * matched text is pasted into it.
10
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
10
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
11
11
  */
12
12
  export function nodePasteRule(config: {
13
13
  find: PasteRuleFinder
@@ -17,6 +17,11 @@ export function nodePasteRule(config: {
17
17
  | ((match: ExtendedRegExpMatchArray, event: ClipboardEvent) => Record<string, any>)
18
18
  | false
19
19
  | null
20
+ getContent?:
21
+ | JSONContent[]
22
+ | ((attrs: Record<string, any>) => JSONContent[])
23
+ | false
24
+ | null
20
25
  }) {
21
26
  return new PasteRule({
22
27
  find: config.find,
@@ -24,16 +29,20 @@ export function nodePasteRule(config: {
24
29
  match, chain, range, pasteEvent,
25
30
  }) {
26
31
  const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent)
32
+ const content = callOrReturn(config.getContent, undefined, attributes)
27
33
 
28
34
  if (attributes === false || attributes === null) {
29
35
  return null
30
36
  }
31
37
 
38
+ const node = { type: config.type.name, attrs: attributes } as JSONContent
39
+
40
+ if (content) {
41
+ node.content = content
42
+ }
43
+
32
44
  if (match.input) {
33
- chain().deleteRange(range).insertContentAt(range.from, {
34
- type: config.type.name,
35
- attrs: attributes,
36
- })
45
+ chain().deleteRange(range).insertContentAt(range.from, node)
37
46
  }
38
47
  },
39
48
  })
@@ -3,7 +3,7 @@ import { PasteRule, PasteRuleFinder } from '../PasteRule.js'
3
3
  /**
4
4
  * Build an paste rule that replaces text when the
5
5
  * matched text is pasted into it.
6
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
6
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
7
7
  */
8
8
  export function textPasteRule(config: {
9
9
  find: PasteRuleFinder,
package/src/style.ts CHANGED
@@ -27,8 +27,8 @@ img.ProseMirror-separator {
27
27
  display: inline !important;
28
28
  border: none !important;
29
29
  margin: 0 !important;
30
- width: 1px !important;
31
- height: 1px !important;
30
+ width: 0 !important;
31
+ height: 0 !important;
32
32
  }
33
33
 
34
34
  .ProseMirror-gapcursor {