@tiptap/core 2.0.0-beta.18 → 2.0.0-beta.180

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 (331) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +2 -2
  3. package/dist/packages/core/src/CommandManager.d.ts +13 -6
  4. package/dist/packages/core/src/Editor.d.ts +24 -19
  5. package/dist/packages/core/src/EventEmitter.d.ts +8 -4
  6. package/dist/packages/core/src/Extension.d.ts +99 -27
  7. package/dist/packages/core/src/ExtensionManager.d.ts +7 -13
  8. package/dist/packages/core/src/InputRule.d.ts +42 -0
  9. package/dist/packages/core/src/Mark.d.ts +131 -31
  10. package/dist/packages/core/src/Node.d.ts +157 -33
  11. package/dist/packages/core/src/NodeView.d.ts +8 -12
  12. package/dist/packages/core/src/PasteRule.d.ts +42 -0
  13. package/dist/packages/core/src/Tracker.d.ts +11 -0
  14. package/dist/packages/core/src/commands/blur.d.ts +3 -3
  15. package/dist/packages/core/src/commands/clearContent.d.ts +3 -3
  16. package/dist/packages/core/src/commands/clearNodes.d.ts +3 -3
  17. package/dist/packages/core/src/commands/command.d.ts +2 -2
  18. package/dist/packages/core/src/commands/createParagraphNear.d.ts +3 -3
  19. package/dist/packages/core/src/commands/deleteNode.d.ts +13 -0
  20. package/dist/packages/core/src/commands/deleteRange.d.ts +3 -3
  21. package/dist/packages/core/src/commands/deleteSelection.d.ts +3 -3
  22. package/dist/packages/core/src/commands/enter.d.ts +3 -3
  23. package/dist/packages/core/src/commands/exitCode.d.ts +3 -3
  24. package/dist/packages/core/src/commands/extendMarkRange.d.ts +3 -3
  25. package/dist/packages/core/src/commands/first.d.ts +3 -3
  26. package/dist/packages/core/src/commands/focus.d.ts +5 -3
  27. package/dist/packages/core/src/commands/forEach.d.ts +14 -0
  28. package/dist/packages/core/src/commands/index.d.ts +50 -0
  29. package/dist/packages/core/src/commands/insertContent.d.ts +7 -3
  30. package/dist/packages/core/src/commands/insertContentAt.d.ts +16 -0
  31. package/dist/packages/core/src/commands/joinBackward.d.ts +3 -3
  32. package/dist/packages/core/src/commands/joinForward.d.ts +3 -3
  33. package/dist/packages/core/src/commands/keyboardShortcut.d.ts +3 -3
  34. package/dist/packages/core/src/commands/lift.d.ts +3 -3
  35. package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +3 -3
  36. package/dist/packages/core/src/commands/liftListItem.d.ts +3 -3
  37. package/dist/packages/core/src/commands/newlineInCode.d.ts +3 -3
  38. package/dist/packages/core/src/commands/resetAttributes.d.ts +13 -0
  39. package/dist/packages/core/src/commands/scrollIntoView.d.ts +3 -3
  40. package/dist/packages/core/src/commands/selectAll.d.ts +3 -3
  41. package/dist/packages/core/src/commands/selectNodeBackward.d.ts +3 -3
  42. package/dist/packages/core/src/commands/selectNodeForward.d.ts +3 -3
  43. package/dist/packages/core/src/commands/selectParentNode.d.ts +3 -3
  44. package/dist/packages/core/src/commands/selectTextblockEnd.d.ts +12 -0
  45. package/dist/packages/core/src/commands/selectTextblockStart.d.ts +12 -0
  46. package/dist/packages/core/src/commands/setContent.d.ts +4 -3
  47. package/dist/packages/core/src/commands/setMark.d.ts +3 -3
  48. package/dist/packages/core/src/commands/setMeta.d.ts +12 -0
  49. package/dist/packages/core/src/commands/setNode.d.ts +3 -3
  50. package/dist/packages/core/src/commands/setNodeSelection.d.ts +12 -0
  51. package/dist/packages/core/src/commands/setTextSelection.d.ts +12 -0
  52. package/dist/packages/core/src/commands/sinkListItem.d.ts +3 -3
  53. package/dist/packages/core/src/commands/splitBlock.d.ts +3 -3
  54. package/dist/packages/core/src/commands/splitListItem.d.ts +3 -3
  55. package/dist/packages/core/src/commands/toggleList.d.ts +3 -3
  56. package/dist/packages/core/src/commands/toggleMark.d.ts +8 -3
  57. package/dist/packages/core/src/commands/toggleNode.d.ts +3 -3
  58. package/dist/packages/core/src/commands/toggleWrap.d.ts +3 -3
  59. package/dist/packages/core/src/commands/undoInputRule.d.ts +3 -3
  60. package/dist/packages/core/src/commands/unsetAllMarks.d.ts +3 -3
  61. package/dist/packages/core/src/commands/unsetMark.d.ts +8 -3
  62. package/dist/packages/core/src/commands/updateAttributes.d.ts +13 -0
  63. package/dist/packages/core/src/commands/wrapIn.d.ts +3 -3
  64. package/dist/packages/core/src/commands/wrapInList.d.ts +3 -3
  65. package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +1 -1
  66. package/dist/packages/core/src/extensions/commands.d.ts +2 -95
  67. package/dist/packages/core/src/extensions/editable.d.ts +1 -1
  68. package/dist/packages/core/src/extensions/focusEvents.d.ts +1 -1
  69. package/dist/packages/core/src/extensions/index.d.ts +1 -0
  70. package/dist/packages/core/src/extensions/keymap.d.ts +1 -1
  71. package/dist/packages/core/src/extensions/tabindex.d.ts +2 -0
  72. package/dist/packages/core/src/helpers/combineTransactionSteps.d.ts +7 -0
  73. package/dist/packages/core/src/helpers/createChainableState.d.ts +5 -0
  74. package/dist/packages/core/src/helpers/createDocument.d.ts +3 -4
  75. package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +4 -5
  76. package/dist/packages/core/src/helpers/defaultBlockAt.d.ts +2 -0
  77. package/dist/packages/core/src/helpers/findChildren.d.ts +3 -0
  78. package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +6 -0
  79. package/dist/packages/core/src/helpers/findParentNode.d.ts +2 -3
  80. package/dist/packages/core/src/helpers/findParentNodeClosestToPos.d.ts +2 -2
  81. package/dist/packages/core/src/helpers/generateHTML.d.ts +2 -2
  82. package/dist/packages/core/src/helpers/generateJSON.d.ts +2 -0
  83. package/dist/packages/core/src/helpers/generateText.d.ts +5 -0
  84. package/dist/packages/core/src/helpers/getAttributes.d.ts +3 -0
  85. package/dist/packages/core/src/helpers/getAttributesFromExtensions.d.ts +2 -2
  86. package/dist/packages/core/src/helpers/getChangedRanges.d.ts +11 -0
  87. package/dist/packages/core/src/helpers/getDebugJSON.d.ts +8 -0
  88. package/dist/packages/core/src/helpers/getExtensionField.d.ts +2 -0
  89. package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +2 -2
  90. package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +2 -3
  91. package/dist/packages/core/src/helpers/getMarkRange.d.ts +1 -1
  92. package/dist/packages/core/src/helpers/getMarkType.d.ts +1 -1
  93. package/dist/packages/core/src/helpers/getMarksBetween.d.ts +2 -2
  94. package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +2 -3
  95. package/dist/packages/core/src/helpers/getNodeType.d.ts +1 -1
  96. package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +3 -3
  97. package/dist/packages/core/src/helpers/getSchema.d.ts +1 -1
  98. package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +3 -0
  99. package/dist/packages/core/src/helpers/getSchemaTypeByName.d.ts +1 -1
  100. package/dist/packages/core/src/helpers/getSchemaTypeNameByName.d.ts +1 -1
  101. package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +2 -2
  102. package/dist/packages/core/src/helpers/getText.d.ts +6 -0
  103. package/dist/packages/core/src/helpers/getTextBetween.d.ts +6 -0
  104. package/dist/packages/core/src/helpers/getTextContentFromNodes.d.ts +2 -0
  105. package/dist/packages/core/src/helpers/getTextSerializersFromSchema.d.ts +3 -0
  106. package/dist/packages/core/src/helpers/index.d.ts +33 -0
  107. package/dist/packages/core/src/helpers/injectExtensionAttributesToParseRule.d.ts +1 -1
  108. package/dist/packages/core/src/helpers/isActive.d.ts +1 -2
  109. package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +2 -0
  110. package/dist/packages/core/src/helpers/isList.d.ts +1 -1
  111. package/dist/packages/core/src/helpers/isMarkActive.d.ts +2 -3
  112. package/dist/packages/core/src/helpers/isNodeActive.d.ts +2 -3
  113. package/dist/packages/core/src/helpers/isNodeEmpty.d.ts +1 -1
  114. package/dist/packages/core/src/helpers/isNodeSelection.d.ts +1 -1
  115. package/dist/packages/core/src/helpers/isTextSelection.d.ts +1 -1
  116. package/dist/packages/core/src/helpers/posToDOMRect.d.ts +2 -0
  117. package/dist/packages/core/src/helpers/resolveFocusPosition.d.ts +4 -0
  118. package/dist/packages/core/src/helpers/selectionToInsertionEnd.d.ts +1 -1
  119. package/dist/packages/core/src/helpers/splitExtensions.d.ts +6 -6
  120. package/dist/packages/core/src/index.d.ts +14 -21
  121. package/dist/packages/core/src/inputRules/index.d.ts +5 -0
  122. package/dist/packages/core/src/inputRules/markInputRule.d.ts +11 -2
  123. package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +11 -2
  124. package/dist/packages/core/src/inputRules/textInputRule.d.ts +9 -0
  125. package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +14 -0
  126. package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +23 -0
  127. package/dist/packages/core/src/pasteRules/index.d.ts +2 -0
  128. package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +11 -2
  129. package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +9 -0
  130. package/dist/packages/core/src/style.d.ts +1 -2
  131. package/dist/packages/core/src/types.d.ts +112 -56
  132. package/dist/packages/core/src/utilities/callOrReturn.d.ts +2 -1
  133. package/dist/packages/core/src/utilities/createStyleTag.d.ts +1 -1
  134. package/dist/packages/core/src/utilities/deleteProps.d.ts +1 -2
  135. package/dist/packages/core/src/utilities/elementFromString.d.ts +1 -1
  136. package/dist/packages/core/src/utilities/escapeForRegEx.d.ts +1 -0
  137. package/dist/packages/core/src/utilities/findDuplicates.d.ts +1 -0
  138. package/dist/packages/core/src/utilities/fromString.d.ts +1 -1
  139. package/dist/packages/core/src/utilities/index.d.ts +3 -0
  140. package/dist/packages/core/src/utilities/isClass.d.ts +1 -1
  141. package/dist/packages/core/src/utilities/isEmptyObject.d.ts +1 -1
  142. package/dist/packages/core/src/utilities/isFunction.d.ts +1 -0
  143. package/dist/packages/core/src/utilities/isMacOS.d.ts +1 -0
  144. package/dist/packages/core/src/utilities/isNumber.d.ts +1 -0
  145. package/dist/packages/core/src/utilities/isObject.d.ts +1 -1
  146. package/dist/packages/core/src/utilities/isPlainObject.d.ts +1 -1
  147. package/dist/packages/core/src/utilities/isRegExp.d.ts +1 -0
  148. package/dist/packages/core/src/utilities/isiOS.d.ts +1 -0
  149. package/dist/packages/core/src/utilities/mergeAttributes.d.ts +1 -2
  150. package/dist/packages/core/src/utilities/mergeDeep.d.ts +1 -2
  151. package/dist/packages/core/src/utilities/minMax.d.ts +1 -1
  152. package/dist/packages/core/src/utilities/objectIncludes.d.ts +3 -2
  153. package/dist/packages/core/src/utilities/removeDuplicates.d.ts +8 -0
  154. package/dist/tiptap-core.cjs.js +2675 -1378
  155. package/dist/tiptap-core.cjs.js.map +1 -1
  156. package/dist/tiptap-core.esm.js +2605 -1341
  157. package/dist/tiptap-core.esm.js.map +1 -1
  158. package/dist/tiptap-core.umd.js +2622 -1324
  159. package/dist/tiptap-core.umd.js.map +1 -1
  160. package/package.json +17 -20
  161. package/src/CommandManager.ts +63 -64
  162. package/src/Editor.ts +115 -78
  163. package/src/EventEmitter.ts +14 -4
  164. package/src/Extension.ts +193 -43
  165. package/src/ExtensionManager.ts +232 -83
  166. package/src/InputRule.ts +265 -0
  167. package/src/Mark.ts +239 -49
  168. package/src/Node.ts +279 -57
  169. package/src/NodeView.ts +115 -47
  170. package/src/PasteRule.ts +247 -0
  171. package/src/Tracker.ts +42 -0
  172. package/src/commands/blur.ts +12 -6
  173. package/src/commands/clearContent.ts +3 -3
  174. package/src/commands/clearNodes.ts +30 -18
  175. package/src/commands/command.ts +2 -2
  176. package/src/commands/createParagraphNear.ts +4 -3
  177. package/src/commands/deleteNode.ts +37 -0
  178. package/src/commands/deleteRange.ts +3 -3
  179. package/src/commands/deleteSelection.ts +4 -3
  180. package/src/commands/enter.ts +3 -3
  181. package/src/commands/exitCode.ts +4 -3
  182. package/src/commands/extendMarkRange.ts +12 -11
  183. package/src/commands/first.ts +3 -3
  184. package/src/commands/focus.ts +45 -44
  185. package/src/commands/forEach.ts +24 -0
  186. package/src/commands/index.ts +50 -0
  187. package/src/commands/insertContent.ts +13 -24
  188. package/src/commands/insertContentAt.ts +108 -0
  189. package/src/commands/joinBackward.ts +4 -3
  190. package/src/commands/joinForward.ts +4 -3
  191. package/src/commands/keyboardShortcut.ts +6 -6
  192. package/src/commands/lift.ts +6 -5
  193. package/src/commands/liftEmptyBlock.ts +4 -3
  194. package/src/commands/liftListItem.ts +6 -5
  195. package/src/commands/newlineInCode.ts +4 -3
  196. package/src/commands/resetAttributes.ts +62 -0
  197. package/src/commands/scrollIntoView.ts +3 -3
  198. package/src/commands/selectAll.ts +8 -6
  199. package/src/commands/selectNodeBackward.ts +4 -3
  200. package/src/commands/selectNodeForward.ts +4 -3
  201. package/src/commands/selectParentNode.ts +4 -3
  202. package/src/commands/selectTextblockEnd.ts +20 -0
  203. package/src/commands/selectTextblockStart.ts +20 -0
  204. package/src/commands/setContent.ts +8 -11
  205. package/src/commands/setMark.ts +36 -12
  206. package/src/commands/setMeta.ts +18 -0
  207. package/src/commands/setNode.ts +29 -7
  208. package/src/commands/setNodeSelection.ts +29 -0
  209. package/src/commands/setTextSelection.ts +33 -0
  210. package/src/commands/sinkListItem.ts +6 -5
  211. package/src/commands/splitBlock.ts +16 -10
  212. package/src/commands/splitListItem.ts +45 -18
  213. package/src/commands/toggleList.ts +83 -18
  214. package/src/commands/toggleMark.ts +18 -7
  215. package/src/commands/toggleNode.ts +6 -5
  216. package/src/commands/toggleWrap.ts +9 -9
  217. package/src/commands/undoInputRule.ts +34 -5
  218. package/src/commands/unsetAllMarks.ts +7 -11
  219. package/src/commands/unsetMark.ts +35 -22
  220. package/src/commands/updateAttributes.ts +73 -0
  221. package/src/commands/wrapIn.ts +5 -10
  222. package/src/commands/wrapInList.ts +6 -5
  223. package/src/extensions/clipboardTextSerializer.ts +14 -35
  224. package/src/extensions/commands.ts +3 -141
  225. package/src/extensions/editable.ts +1 -0
  226. package/src/extensions/focusEvents.ts +1 -3
  227. package/src/extensions/index.ts +1 -0
  228. package/src/extensions/keymap.ts +110 -13
  229. package/src/extensions/tabindex.ts +18 -0
  230. package/src/helpers/combineTransactionSteps.ts +18 -0
  231. package/src/helpers/createChainableState.ts +38 -0
  232. package/src/helpers/createDocument.ts +5 -6
  233. package/src/helpers/createNodeFromContent.ts +17 -19
  234. package/src/helpers/defaultBlockAt.ts +13 -0
  235. package/src/helpers/findChildren.ts +18 -0
  236. package/src/helpers/findChildrenInRange.ts +32 -0
  237. package/src/helpers/findParentNode.ts +3 -2
  238. package/src/helpers/findParentNodeClosestToPos.ts +3 -2
  239. package/src/helpers/generateHTML.ts +6 -5
  240. package/src/helpers/generateJSON.ts +14 -0
  241. package/src/helpers/generateText.ts +30 -0
  242. package/src/helpers/getAttributes.ts +28 -0
  243. package/src/helpers/getAttributesFromExtensions.ts +42 -14
  244. package/src/helpers/getChangedRanges.ts +83 -0
  245. package/src/helpers/getDebugJSON.ts +54 -0
  246. package/src/helpers/getExtensionField.ts +25 -0
  247. package/src/helpers/getHTMLFromFragment.ts +6 -5
  248. package/src/helpers/getMarkAttributes.ts +14 -10
  249. package/src/helpers/getMarkRange.ts +41 -8
  250. package/src/helpers/getMarkType.ts +5 -1
  251. package/src/helpers/getMarksBetween.ts +32 -10
  252. package/src/helpers/getNodeAttributes.ts +9 -9
  253. package/src/helpers/getNodeType.ts +5 -1
  254. package/src/helpers/getRenderedAttributes.ts +6 -7
  255. package/src/helpers/getSchema.ts +7 -133
  256. package/src/helpers/getSchemaByResolvedExtensions.ts +148 -0
  257. package/src/helpers/getSchemaTypeByName.ts +2 -10
  258. package/src/helpers/getSchemaTypeNameByName.ts +1 -1
  259. package/src/helpers/getSplittedAttributes.ts +4 -4
  260. package/src/helpers/getText.ts +19 -0
  261. package/src/helpers/getTextBetween.ts +49 -0
  262. package/src/helpers/getTextContentFromNodes.ts +17 -0
  263. package/src/helpers/getTextSerializersFromSchema.ts +10 -0
  264. package/src/helpers/index.ts +33 -0
  265. package/src/helpers/injectExtensionAttributesToParseRule.ts +17 -19
  266. package/src/helpers/isActive.ts +5 -5
  267. package/src/helpers/isExtensionRulesEnabled.ts +15 -0
  268. package/src/helpers/isList.ts +14 -7
  269. package/src/helpers/isMarkActive.ts +45 -20
  270. package/src/helpers/isNodeActive.ts +28 -35
  271. package/src/helpers/isNodeEmpty.ts +1 -1
  272. package/src/helpers/isNodeSelection.ts +3 -2
  273. package/src/helpers/isTextSelection.ts +3 -2
  274. package/src/helpers/posToDOMRect.ts +35 -0
  275. package/src/helpers/resolveFocusPosition.ts +43 -0
  276. package/src/helpers/selectionToInsertionEnd.ts +2 -2
  277. package/src/helpers/splitExtensions.ts +3 -3
  278. package/src/index.ts +15 -24
  279. package/src/inputRules/index.ts +5 -0
  280. package/src/inputRules/markInputRule.ts +59 -39
  281. package/src/inputRules/nodeInputRule.ts +45 -11
  282. package/src/inputRules/textInputRule.ts +35 -0
  283. package/src/inputRules/textblockTypeInputRule.ts +38 -0
  284. package/src/inputRules/wrappingInputRule.ts +60 -0
  285. package/src/pasteRules/index.ts +2 -0
  286. package/src/pasteRules/markPasteRule.ts +62 -53
  287. package/src/pasteRules/textPasteRule.ts +35 -0
  288. package/src/style.ts +16 -3
  289. package/src/types.ts +129 -43
  290. package/src/utilities/callOrReturn.ts +6 -3
  291. package/src/utilities/createStyleTag.ts +12 -1
  292. package/src/utilities/deleteProps.ts +2 -4
  293. package/src/utilities/elementFromString.ts +4 -5
  294. package/src/utilities/escapeForRegEx.ts +4 -0
  295. package/src/utilities/findDuplicates.ts +5 -0
  296. package/src/utilities/fromString.ts +2 -2
  297. package/src/utilities/index.ts +3 -0
  298. package/src/utilities/isClass.ts +2 -2
  299. package/src/utilities/isEmptyObject.ts +2 -2
  300. package/src/utilities/isFunction.ts +3 -0
  301. package/src/utilities/isMacOS.ts +5 -0
  302. package/src/utilities/isNumber.ts +3 -0
  303. package/src/utilities/isObject.ts +6 -6
  304. package/src/utilities/isPlainObject.ts +8 -5
  305. package/src/utilities/isRegExp.ts +3 -0
  306. package/src/utilities/isString.ts +3 -0
  307. package/src/utilities/isiOS.ts +12 -0
  308. package/src/utilities/mergeAttributes.ts +2 -3
  309. package/src/utilities/mergeDeep.ts +2 -3
  310. package/src/utilities/minMax.ts +1 -1
  311. package/src/utilities/objectIncludes.ts +17 -5
  312. package/src/utilities/removeDuplicates.ts +15 -0
  313. package/CHANGELOG.md +0 -335
  314. package/dist/packages/core/src/commands/insertHTML.d.ts +0 -12
  315. package/dist/packages/core/src/commands/insertNode.d.ts +0 -13
  316. package/dist/packages/core/src/commands/insertText.d.ts +0 -12
  317. package/dist/packages/core/src/commands/replace.d.ts +0 -13
  318. package/dist/packages/core/src/commands/replaceRange.d.ts +0 -13
  319. package/dist/packages/core/src/commands/resetNodeAttributes.d.ts +0 -13
  320. package/dist/packages/core/src/commands/updateNodeAttributes.d.ts +0 -13
  321. package/dist/packages/core/src/utilities/removeElement.d.ts +0 -1
  322. package/dist/tiptap-core.bundle.umd.min.js +0 -17
  323. package/dist/tiptap-core.bundle.umd.min.js.map +0 -1
  324. package/src/commands/insertHTML.ts +0 -30
  325. package/src/commands/insertNode.ts +0 -33
  326. package/src/commands/insertText.ts +0 -22
  327. package/src/commands/replace.ts +0 -20
  328. package/src/commands/replaceRange.ts +0 -36
  329. package/src/commands/resetNodeAttributes.ts +0 -31
  330. package/src/commands/updateNodeAttributes.ts +0 -33
  331. package/src/utilities/removeElement.ts +0 -5
@@ -1,276 +1,66 @@
1
1
  import { Plugin, PluginKey, TextSelection, Selection, NodeSelection, EditorState } from 'prosemirror-state';
2
2
  import { EditorView } from 'prosemirror-view';
3
- import { DOMParser, DOMSerializer, Schema, Fragment, Slice, Node as Node$1 } from 'prosemirror-model';
4
3
  import { keymap } from 'prosemirror-keymap';
5
- import { inputRules, undoInputRule as undoInputRule$2, InputRule } from 'prosemirror-inputrules';
6
- import { liftTarget, ReplaceStep, ReplaceAroundStep, canSplit } from 'prosemirror-transform';
7
- import { createParagraphNear as createParagraphNear$2, deleteSelection as deleteSelection$2, exitCode as exitCode$2, joinBackward as joinBackward$2, joinForward as joinForward$2, lift as lift$2, liftEmptyBlock as liftEmptyBlock$2, newlineInCode as newlineInCode$2, selectAll as selectAll$2, selectNodeBackward as selectNodeBackward$2, selectNodeForward as selectNodeForward$2, selectParentNode as selectParentNode$2, setBlockType, wrapIn as wrapIn$2 } from 'prosemirror-commands';
8
- import { liftListItem as liftListItem$2, sinkListItem as sinkListItem$2, wrapInList as wrapInList$2 } from 'prosemirror-schema-list';
9
-
10
- function getNodeType(nameOrType, schema) {
11
- if (typeof nameOrType === 'string') {
12
- return schema.nodes[nameOrType];
13
- }
14
- return nameOrType;
15
- }
16
-
17
- function getNodeAttributes(state, typeOrName) {
18
- const type = getNodeType(typeOrName, state.schema);
19
- const { from, to } = state.selection;
20
- let nodes = [];
21
- state.doc.nodesBetween(from, to, node => {
22
- nodes = [...nodes, node];
23
- });
24
- const node = nodes
25
- .reverse()
26
- .find(nodeItem => nodeItem.type.name === type.name);
27
- if (node) {
28
- return { ...node.attrs };
29
- }
30
- return {};
31
- }
32
-
33
- function getMarkType(nameOrType, schema) {
34
- if (typeof nameOrType === 'string') {
35
- return schema.marks[nameOrType];
36
- }
37
- return nameOrType;
38
- }
39
-
40
- function getMarkAttributes(state, typeOrName) {
41
- const type = getMarkType(typeOrName, state.schema);
42
- const { from, to, empty } = state.selection;
43
- let marks = [];
44
- if (empty) {
45
- marks = state.selection.$head.marks();
46
- }
47
- else {
48
- state.doc.nodesBetween(from, to, node => {
49
- marks = [...marks, ...node.marks];
50
- });
51
- }
52
- const mark = marks.find(markItem => markItem.type.name === type.name);
53
- if (mark) {
54
- return { ...mark.attrs };
55
- }
56
- return {};
57
- }
58
-
59
- /**
60
- * Check if object1 includes object2
61
- * @param object1 Object
62
- * @param object2 Object
63
- */
64
- function objectIncludes(object1, object2) {
65
- const keys = Object.keys(object2);
66
- if (!keys.length) {
67
- return true;
68
- }
69
- return !!keys
70
- .filter(key => object2[key] === object1[key])
71
- .length;
72
- }
73
-
74
- function isNodeActive(state, typeOrName, attributes = {}) {
75
- const { from, to, empty } = state.selection;
76
- const type = typeOrName
77
- ? getNodeType(typeOrName, state.schema)
78
- : null;
79
- let nodeRanges = [];
80
- state.doc.nodesBetween(from, to, (node, pos) => {
81
- if (!node.isText) {
82
- const relativeFrom = Math.max(from, pos);
83
- const relativeTo = Math.min(to, pos + node.nodeSize);
84
- nodeRanges = [...nodeRanges, {
85
- node,
86
- from: relativeFrom,
87
- to: relativeTo,
88
- }];
89
- }
90
- });
91
- if (empty) {
92
- return !!nodeRanges
93
- .filter(nodeRange => {
94
- if (!type) {
95
- return true;
96
- }
97
- return type.name === nodeRange.node.type.name;
98
- })
99
- .find(nodeRange => objectIncludes(nodeRange.node.attrs, attributes));
100
- }
101
- const selectionRange = to - from;
102
- const range = nodeRanges
103
- .filter(nodeRange => {
104
- if (!type) {
105
- return true;
106
- }
107
- return type.name === nodeRange.node.type.name;
108
- })
109
- .filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
110
- .reduce((sum, nodeRange) => {
111
- const size = nodeRange.to - nodeRange.from;
112
- return sum + size;
113
- }, 0);
114
- return range >= selectionRange;
115
- }
116
-
117
- function isMarkActive(state, typeOrName, attributes = {}) {
118
- const { from, to, empty } = state.selection;
119
- const type = typeOrName
120
- ? getMarkType(typeOrName, state.schema)
121
- : null;
122
- if (empty) {
123
- return !!(state.storedMarks || state.selection.$from.marks())
124
- .filter(mark => {
125
- if (!type) {
126
- return true;
127
- }
128
- return type.name === mark.type.name;
129
- })
130
- .find(mark => objectIncludes(mark.attrs, attributes));
131
- }
132
- let selectionRange = 0;
133
- let markRanges = [];
134
- state.doc.nodesBetween(from, to, (node, pos) => {
135
- if (node.isText) {
136
- const relativeFrom = Math.max(from, pos);
137
- const relativeTo = Math.min(to, pos + node.nodeSize);
138
- const range = relativeTo - relativeFrom;
139
- selectionRange += range;
140
- markRanges = [...markRanges, ...node.marks.map(mark => ({
141
- mark,
142
- from: relativeFrom,
143
- to: relativeTo,
144
- }))];
145
- }
146
- });
147
- if (selectionRange === 0) {
148
- return false;
149
- }
150
- const range = markRanges
151
- .filter(markRange => {
152
- if (!type) {
153
- return true;
154
- }
155
- return type.name === markRange.mark.type.name;
156
- })
157
- .filter(markRange => objectIncludes(markRange.mark.attrs, attributes))
158
- .reduce((sum, markRange) => {
159
- const size = markRange.to - markRange.from;
160
- return sum + size;
161
- }, 0);
162
- return range >= selectionRange;
163
- }
164
-
165
- function getSchemaTypeNameByName(name, schema) {
166
- if (schema.nodes[name]) {
167
- return 'node';
168
- }
169
- if (schema.marks[name]) {
170
- return 'mark';
171
- }
172
- return null;
173
- }
174
-
175
- function isActive(state, name, attributes = {}) {
176
- if (!name) {
177
- return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
178
- }
179
- const schemaType = getSchemaTypeNameByName(name, state.schema);
180
- if (schemaType === 'node') {
181
- return isNodeActive(state, name, attributes);
182
- }
183
- if (schemaType === 'mark') {
184
- return isMarkActive(state, name, attributes);
185
- }
186
- return false;
187
- }
188
-
189
- function removeElement(element) {
190
- if (element && element.parentNode) {
191
- element.parentNode.removeChild(element);
192
- }
193
- }
194
-
195
- function elementFromString(value) {
196
- const htmlString = `<div>${value}</div>`;
197
- const parser = new window.DOMParser();
198
- const element = parser.parseFromString(htmlString, 'text/html').body;
199
- return element;
200
- }
201
-
202
- function createNodeFromContent(content, schema, options) {
203
- options = {
204
- slice: true,
205
- parseOptions: {},
206
- ...options,
4
+ import { Schema, Fragment, DOMParser, Slice, DOMSerializer, Node as Node$1 } from 'prosemirror-model';
5
+ import { liftTarget, ReplaceStep, ReplaceAroundStep, canSplit, canJoin, Transform, findWrapping } from 'prosemirror-transform';
6
+ import { createParagraphNear as createParagraphNear$1, deleteSelection as deleteSelection$1, exitCode as exitCode$1, joinBackward as joinBackward$1, joinForward as joinForward$1, lift as lift$1, liftEmptyBlock as liftEmptyBlock$1, newlineInCode as newlineInCode$1, selectNodeBackward as selectNodeBackward$1, selectNodeForward as selectNodeForward$1, selectParentNode as selectParentNode$1, selectTextblockEnd as selectTextblockEnd$1, selectTextblockStart as selectTextblockStart$1, setBlockType, wrapIn as wrapIn$1 } from 'prosemirror-commands';
7
+ import { liftListItem as liftListItem$1, sinkListItem as sinkListItem$1, wrapInList as wrapInList$1 } from 'prosemirror-schema-list';
8
+
9
+ function createChainableState(config) {
10
+ const { state, transaction } = config;
11
+ let { selection } = transaction;
12
+ let { doc } = transaction;
13
+ let { storedMarks } = transaction;
14
+ return {
15
+ ...state,
16
+ apply: state.apply.bind(state),
17
+ applyTransaction: state.applyTransaction.bind(state),
18
+ filterTransaction: state.filterTransaction,
19
+ plugins: state.plugins,
20
+ schema: state.schema,
21
+ reconfigure: state.reconfigure.bind(state),
22
+ toJSON: state.toJSON.bind(state),
23
+ get storedMarks() {
24
+ return storedMarks;
25
+ },
26
+ get selection() {
27
+ return selection;
28
+ },
29
+ get doc() {
30
+ return doc;
31
+ },
32
+ get tr() {
33
+ selection = transaction.selection;
34
+ doc = transaction.doc;
35
+ storedMarks = transaction.storedMarks;
36
+ return transaction;
37
+ },
207
38
  };
208
- if (content && typeof content === 'object') {
209
- try {
210
- return schema.nodeFromJSON(content);
211
- }
212
- catch (error) {
213
- console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
214
- return createNodeFromContent('', schema, options);
215
- }
216
- }
217
- if (typeof content === 'string') {
218
- const isHTML = content.trim().startsWith('<') && content.trim().endsWith('>');
219
- if (isHTML || !options.slice) {
220
- const parser = DOMParser.fromSchema(schema);
221
- return options.slice
222
- ? parser.parseSlice(elementFromString(content), options.parseOptions).content
223
- : parser.parse(elementFromString(content), options.parseOptions);
224
- }
225
- return content;
226
- }
227
- return createNodeFromContent('', schema, options);
228
- }
229
-
230
- function createDocument(content, schema, parseOptions = {}) {
231
- return createNodeFromContent(content, schema, { slice: false, parseOptions });
232
- }
233
-
234
- function getHTMLFromFragment(doc, schema) {
235
- const fragment = DOMSerializer
236
- .fromSchema(schema)
237
- .serializeFragment(doc.content);
238
- const temporaryDocument = document.implementation.createHTMLDocument();
239
- const container = temporaryDocument.createElement('div');
240
- container.appendChild(fragment);
241
- return container.innerHTML;
242
- }
243
-
244
- function isNodeEmpty(node) {
245
- var _a;
246
- const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
247
- const content = node.toJSON();
248
- return JSON.stringify(defaultContent) === JSON.stringify(content);
249
- }
250
-
251
- function createStyleTag(style) {
252
- const styleNode = document.createElement('style');
253
- styleNode.innerHTML = style;
254
- document.getElementsByTagName('head')[0].appendChild(styleNode);
255
- return styleNode;
256
39
  }
257
40
 
258
41
  class CommandManager {
259
- constructor(editor, commands) {
260
- this.editor = editor;
261
- this.commands = commands;
42
+ constructor(props) {
43
+ this.editor = props.editor;
44
+ this.rawCommands = this.editor.extensionManager.commands;
45
+ this.customState = props.state;
46
+ }
47
+ get hasCustomState() {
48
+ return !!this.customState;
262
49
  }
263
- createCommands() {
264
- const { commands, editor } = this;
265
- const { state, view } = editor;
50
+ get state() {
51
+ return this.customState || this.editor.state;
52
+ }
53
+ get commands() {
54
+ const { rawCommands, editor, state } = this;
55
+ const { view } = editor;
266
56
  const { tr } = state;
267
57
  const props = this.buildProps(tr);
268
58
  return Object.fromEntries(Object
269
- .entries(commands)
59
+ .entries(rawCommands)
270
60
  .map(([name, command]) => {
271
61
  const method = (...args) => {
272
62
  const callback = command(...args)(props);
273
- if (!tr.getMeta('preventDispatch')) {
63
+ if (!tr.getMeta('preventDispatch') && !this.hasCustomState) {
274
64
  view.dispatch(tr);
275
65
  }
276
66
  return callback;
@@ -278,20 +68,29 @@ class CommandManager {
278
68
  return [name, method];
279
69
  }));
280
70
  }
71
+ get chain() {
72
+ return () => this.createChain();
73
+ }
74
+ get can() {
75
+ return () => this.createCan();
76
+ }
281
77
  createChain(startTr, shouldDispatch = true) {
282
- const { commands, editor } = this;
283
- const { state, view } = editor;
78
+ const { rawCommands, editor, state } = this;
79
+ const { view } = editor;
284
80
  const callbacks = [];
285
81
  const hasStartTransaction = !!startTr;
286
82
  const tr = startTr || state.tr;
287
83
  const run = () => {
288
- if (!hasStartTransaction && shouldDispatch && !tr.getMeta('preventDispatch')) {
84
+ if (!hasStartTransaction
85
+ && shouldDispatch
86
+ && !tr.getMeta('preventDispatch')
87
+ && !this.hasCustomState) {
289
88
  view.dispatch(tr);
290
89
  }
291
- return () => callbacks.every(callback => callback === true);
90
+ return callbacks.every(callback => callback === true);
292
91
  };
293
92
  const chain = {
294
- ...Object.fromEntries(Object.entries(commands).map(([name, command]) => {
93
+ ...Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
295
94
  const chainedCommand = (...args) => {
296
95
  const props = this.buildProps(tr, shouldDispatch);
297
96
  const callback = command(...args)(props);
@@ -305,13 +104,12 @@ class CommandManager {
305
104
  return chain;
306
105
  }
307
106
  createCan(startTr) {
308
- const { commands, editor } = this;
309
- const { state } = editor;
107
+ const { rawCommands, state } = this;
310
108
  const dispatch = undefined;
311
109
  const tr = startTr || state.tr;
312
110
  const props = this.buildProps(tr, dispatch);
313
111
  const formattedCommands = Object.fromEntries(Object
314
- .entries(commands)
112
+ .entries(rawCommands)
315
113
  .map(([name, command]) => {
316
114
  return [name, (...args) => command(...args)({ ...props, dispatch })];
317
115
  }));
@@ -321,8 +119,8 @@ class CommandManager {
321
119
  };
322
120
  }
323
121
  buildProps(tr, shouldDispatch = true) {
324
- const { editor, commands } = this;
325
- const { state, view } = editor;
122
+ const { rawCommands, editor, state } = this;
123
+ const { view } = editor;
326
124
  if (state.storedMarks) {
327
125
  tr.setStoredMarks(state.storedMarks);
328
126
  }
@@ -330,7 +128,10 @@ class CommandManager {
330
128
  tr,
331
129
  editor,
332
130
  view,
333
- state: this.chainableState(tr, state),
131
+ state: createChainableState({
132
+ state,
133
+ transaction: tr,
134
+ }),
334
135
  dispatch: shouldDispatch
335
136
  ? () => undefined
336
137
  : undefined,
@@ -338,7 +139,7 @@ class CommandManager {
338
139
  can: () => this.createCan(tr),
339
140
  get commands() {
340
141
  return Object.fromEntries(Object
341
- .entries(commands)
142
+ .entries(rawCommands)
342
143
  .map(([name, command]) => {
343
144
  return [name, (...args) => command(...args)(props)];
344
145
  }));
@@ -346,35 +147,57 @@ class CommandManager {
346
147
  };
347
148
  return props;
348
149
  }
349
- chainableState(tr, state) {
350
- let { selection } = tr;
351
- let { doc } = tr;
352
- let { storedMarks } = tr;
353
- return {
354
- ...state,
355
- schema: state.schema,
356
- plugins: state.plugins,
357
- apply: state.apply.bind(state),
358
- applyTransaction: state.applyTransaction.bind(state),
359
- reconfigure: state.reconfigure.bind(state),
360
- toJSON: state.toJSON.bind(state),
361
- get storedMarks() {
362
- return storedMarks;
363
- },
364
- get selection() {
365
- return selection;
366
- },
367
- get doc() {
368
- return doc;
369
- },
370
- get tr() {
371
- selection = tr.selection;
372
- doc = tr.doc;
373
- storedMarks = tr.storedMarks;
374
- return tr;
375
- },
376
- };
150
+ }
151
+
152
+ class EventEmitter {
153
+ constructor() {
154
+ this.callbacks = {};
155
+ }
156
+ on(event, fn) {
157
+ if (!this.callbacks[event]) {
158
+ this.callbacks[event] = [];
159
+ }
160
+ this.callbacks[event].push(fn);
161
+ return this;
162
+ }
163
+ emit(event, ...args) {
164
+ const callbacks = this.callbacks[event];
165
+ if (callbacks) {
166
+ callbacks.forEach(callback => callback.apply(this, args));
167
+ }
168
+ return this;
377
169
  }
170
+ off(event, fn) {
171
+ const callbacks = this.callbacks[event];
172
+ if (callbacks) {
173
+ if (fn) {
174
+ this.callbacks[event] = callbacks.filter(callback => callback !== fn);
175
+ }
176
+ else {
177
+ delete this.callbacks[event];
178
+ }
179
+ }
180
+ return this;
181
+ }
182
+ removeAllListeners() {
183
+ this.callbacks = {};
184
+ }
185
+ }
186
+
187
+ function getExtensionField(extension, field, context) {
188
+ if (extension.config[field] === undefined && extension.parent) {
189
+ return getExtensionField(extension.parent, field, context);
190
+ }
191
+ if (typeof extension.config[field] === 'function') {
192
+ const value = extension.config[field].bind({
193
+ ...context,
194
+ parent: extension.parent
195
+ ? getExtensionField(extension.parent, field, context)
196
+ : null,
197
+ });
198
+ return value;
199
+ }
200
+ return extension.config[field];
378
201
  }
379
202
 
380
203
  function splitExtensions(extensions) {
@@ -402,15 +225,20 @@ function getAttributesFromExtensions(extensions) {
402
225
  renderHTML: null,
403
226
  parseHTML: null,
404
227
  keepOnSplit: true,
228
+ isRequired: false,
405
229
  };
406
230
  extensions.forEach(extension => {
407
231
  const context = {
232
+ name: extension.name,
408
233
  options: extension.options,
234
+ storage: extension.storage,
409
235
  };
410
- if (!extension.config.addGlobalAttributes) {
236
+ const addGlobalAttributes = getExtensionField(extension, 'addGlobalAttributes', context);
237
+ if (!addGlobalAttributes) {
411
238
  return;
412
239
  }
413
- const globalAttributes = extension.config.addGlobalAttributes.bind(context)();
240
+ // TODO: remove `as GlobalAttributes`
241
+ const globalAttributes = addGlobalAttributes();
414
242
  globalAttributes.forEach(globalAttribute => {
415
243
  globalAttribute.types.forEach(type => {
416
244
  Object
@@ -430,28 +258,46 @@ function getAttributesFromExtensions(extensions) {
430
258
  });
431
259
  nodeAndMarkExtensions.forEach(extension => {
432
260
  const context = {
261
+ name: extension.name,
433
262
  options: extension.options,
263
+ storage: extension.storage,
434
264
  };
435
- if (!extension.config.addAttributes) {
265
+ const addAttributes = getExtensionField(extension, 'addAttributes', context);
266
+ if (!addAttributes) {
436
267
  return;
437
268
  }
438
- const attributes = extension.config.addAttributes.bind(context)();
269
+ // TODO: remove `as Attributes`
270
+ const attributes = addAttributes();
439
271
  Object
440
272
  .entries(attributes)
441
273
  .forEach(([name, attribute]) => {
274
+ const mergedAttr = {
275
+ ...defaultAttribute,
276
+ ...attribute,
277
+ };
278
+ if (attribute.isRequired && attribute.default === undefined) {
279
+ delete mergedAttr.default;
280
+ }
442
281
  extensionAttributes.push({
443
- type: extension.config.name,
282
+ type: extension.name,
444
283
  name,
445
- attribute: {
446
- ...defaultAttribute,
447
- ...attribute,
448
- },
284
+ attribute: mergedAttr,
449
285
  });
450
286
  });
451
287
  });
452
288
  return extensionAttributes;
453
289
  }
454
290
 
291
+ function getNodeType(nameOrType, schema) {
292
+ if (typeof nameOrType === 'string') {
293
+ if (!schema.nodes[nameOrType]) {
294
+ throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
295
+ }
296
+ return schema.nodes[nameOrType];
297
+ }
298
+ return nameOrType;
299
+ }
300
+
455
301
  function mergeAttributes(...objects) {
456
302
  return objects
457
303
  .filter(item => !!item)
@@ -488,20 +334,39 @@ function getRenderedAttributes(nodeOrMark, extensionAttributes) {
488
334
  }
489
335
  return item.attribute.renderHTML(nodeOrMark.attrs) || {};
490
336
  })
491
- .reduce((attributes, attribute) => {
492
- return mergeAttributes(attributes, attribute);
493
- }, {});
337
+ .reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {});
338
+ }
339
+
340
+ function isFunction(value) {
341
+ return typeof value === 'function';
342
+ }
343
+
344
+ /**
345
+ * Optionally calls `value` as a function.
346
+ * Otherwise it is returned directly.
347
+ * @param value Function or any value.
348
+ * @param context Optional context to bind to function.
349
+ * @param props Optional props to pass to function.
350
+ */
351
+ function callOrReturn(value, context = undefined, ...props) {
352
+ if (isFunction(value)) {
353
+ if (context) {
354
+ return value.bind(context)(...props);
355
+ }
356
+ return value(...props);
357
+ }
358
+ return value;
494
359
  }
495
360
 
496
- function isEmptyObject(object = {}) {
497
- return Object.keys(object).length === 0 && object.constructor === Object;
361
+ function isEmptyObject(value = {}) {
362
+ return Object.keys(value).length === 0 && value.constructor === Object;
498
363
  }
499
364
 
500
365
  function fromString(value) {
501
366
  if (typeof value !== 'string') {
502
367
  return value;
503
368
  }
504
- if (value.match(/^\d*(\.\d+)?$/)) {
369
+ if (value.match(/^[+-]?(?:\d*\.)?\d+$/)) {
505
370
  return Number(value);
506
371
  }
507
372
  if (value === 'true') {
@@ -532,19 +397,16 @@ function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
532
397
  if (oldAttributes === false) {
533
398
  return false;
534
399
  }
535
- const newAttributes = extensionAttributes
536
- .filter(item => item.attribute.rendered)
537
- .reduce((items, item) => {
538
- const attributes = item.attribute.parseHTML
539
- ? item.attribute.parseHTML(node) || {}
540
- : {
541
- [item.name]: fromString(node.getAttribute(item.name)),
542
- };
543
- const filteredAttributes = Object.fromEntries(Object.entries(attributes)
544
- .filter(([, value]) => value !== undefined && value !== null));
400
+ const newAttributes = extensionAttributes.reduce((items, item) => {
401
+ const value = item.attribute.parseHTML
402
+ ? item.attribute.parseHTML(node)
403
+ : fromString(node.getAttribute(item.name));
404
+ if (value === null || value === undefined) {
405
+ return items;
406
+ }
545
407
  return {
546
408
  ...items,
547
- ...filteredAttributes,
409
+ [item.name]: value,
548
410
  };
549
411
  }, {});
550
412
  return { ...oldAttributes, ...newAttributes };
@@ -552,23 +414,6 @@ function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
552
414
  };
553
415
  }
554
416
 
555
- /**
556
- * Optionally calls `value` as a function.
557
- * Otherwise it is returned directly.
558
- * @param value Function or any value.
559
- * @param context Optional context to bind to function.
560
- * @param props Optional props to pass to function.
561
- */
562
- function callOrReturn(value, context = undefined, ...props) {
563
- if (typeof value === 'function') {
564
- if (context) {
565
- return value.bind(context)(...props);
566
- }
567
- return value(...props);
568
- }
569
- return value;
570
- }
571
-
572
417
  function cleanUpSchemaItem(data) {
573
418
  return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
574
419
  if (key === 'attrs' && isEmptyObject(value)) {
@@ -577,100 +422,99 @@ function cleanUpSchemaItem(data) {
577
422
  return value !== null && value !== undefined;
578
423
  }));
579
424
  }
580
- function getSchema(extensions) {
425
+ function getSchemaByResolvedExtensions(extensions) {
581
426
  var _a;
582
427
  const allAttributes = getAttributesFromExtensions(extensions);
583
428
  const { nodeExtensions, markExtensions } = splitExtensions(extensions);
584
- const topNode = (_a = nodeExtensions.find(extension => extension.config.topNode)) === null || _a === void 0 ? void 0 : _a.config.name;
585
- const nodeSchemaExtenders = [];
586
- const markSchemaExtenders = [];
587
- extensions.forEach(extension => {
588
- if (typeof extension.config.extendNodeSchema === 'function') {
589
- nodeSchemaExtenders.push(extension.config.extendNodeSchema);
590
- }
591
- if (typeof extension.config.extendMarkSchema === 'function') {
592
- markSchemaExtenders.push(extension.config.extendMarkSchema);
593
- }
594
- });
429
+ const topNode = (_a = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))) === null || _a === void 0 ? void 0 : _a.name;
595
430
  const nodes = Object.fromEntries(nodeExtensions.map(extension => {
596
- var _a;
597
- const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name);
598
- const context = { options: extension.options };
599
- const extraNodeFields = nodeSchemaExtenders.reduce((fields, nodeSchemaExtender) => {
600
- const extraFields = callOrReturn(nodeSchemaExtender, context, extension);
431
+ const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name);
432
+ const context = {
433
+ name: extension.name,
434
+ options: extension.options,
435
+ storage: extension.storage,
436
+ };
437
+ const extraNodeFields = extensions.reduce((fields, e) => {
438
+ const extendNodeSchema = getExtensionField(e, 'extendNodeSchema', context);
601
439
  return {
602
440
  ...fields,
603
- ...extraFields,
441
+ ...(extendNodeSchema ? extendNodeSchema(extension) : {}),
604
442
  };
605
443
  }, {});
606
444
  const schema = cleanUpSchemaItem({
607
445
  ...extraNodeFields,
608
- content: callOrReturn(extension.config.content, context),
609
- marks: callOrReturn(extension.config.marks, context),
610
- group: callOrReturn(extension.config.group, context),
611
- inline: callOrReturn(extension.config.inline, context),
612
- atom: callOrReturn(extension.config.atom, context),
613
- selectable: callOrReturn(extension.config.selectable, context),
614
- draggable: callOrReturn(extension.config.draggable, context),
615
- code: callOrReturn(extension.config.code, context),
616
- defining: callOrReturn(extension.config.defining, context),
617
- isolating: callOrReturn(extension.config.isolating, context),
446
+ content: callOrReturn(getExtensionField(extension, 'content', context)),
447
+ marks: callOrReturn(getExtensionField(extension, 'marks', context)),
448
+ group: callOrReturn(getExtensionField(extension, 'group', context)),
449
+ inline: callOrReturn(getExtensionField(extension, 'inline', context)),
450
+ atom: callOrReturn(getExtensionField(extension, 'atom', context)),
451
+ selectable: callOrReturn(getExtensionField(extension, 'selectable', context)),
452
+ draggable: callOrReturn(getExtensionField(extension, 'draggable', context)),
453
+ code: callOrReturn(getExtensionField(extension, 'code', context)),
454
+ defining: callOrReturn(getExtensionField(extension, 'defining', context)),
455
+ isolating: callOrReturn(getExtensionField(extension, 'isolating', context)),
618
456
  attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
619
457
  var _a;
620
458
  return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
621
459
  })),
622
460
  });
623
- if (extension.config.parseHTML) {
624
- schema.parseDOM = (_a = extension.config.parseHTML
625
- .bind(context)()) === null || _a === void 0 ? void 0 : _a.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
461
+ const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
462
+ if (parseHTML) {
463
+ schema.parseDOM = parseHTML
464
+ .map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
626
465
  }
627
- if (extension.config.renderHTML) {
628
- schema.toDOM = node => {
629
- var _a;
630
- return (_a = extension.config.renderHTML) === null || _a === void 0 ? void 0 : _a.bind(context)({
631
- node,
632
- HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
633
- });
634
- };
466
+ const renderHTML = getExtensionField(extension, 'renderHTML', context);
467
+ if (renderHTML) {
468
+ schema.toDOM = node => renderHTML({
469
+ node,
470
+ HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
471
+ });
472
+ }
473
+ const renderText = getExtensionField(extension, 'renderText', context);
474
+ if (renderText) {
475
+ schema.toText = renderText;
635
476
  }
636
- return [extension.config.name, schema];
477
+ return [extension.name, schema];
637
478
  }));
638
479
  const marks = Object.fromEntries(markExtensions.map(extension => {
639
- var _a;
640
- const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name);
641
- const context = { options: extension.options };
642
- const extraMarkFields = markSchemaExtenders.reduce((fields, markSchemaExtender) => {
643
- const extraFields = callOrReturn(markSchemaExtender, context, extension);
480
+ const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name);
481
+ const context = {
482
+ name: extension.name,
483
+ options: extension.options,
484
+ storage: extension.storage,
485
+ };
486
+ const extraMarkFields = extensions.reduce((fields, e) => {
487
+ const extendMarkSchema = getExtensionField(e, 'extendMarkSchema', context);
644
488
  return {
645
489
  ...fields,
646
- ...extraFields,
490
+ ...(extendMarkSchema ? extendMarkSchema(extension) : {}),
647
491
  };
648
492
  }, {});
649
493
  const schema = cleanUpSchemaItem({
650
494
  ...extraMarkFields,
651
- inclusive: callOrReturn(extension.config.inclusive, context),
652
- excludes: callOrReturn(extension.config.excludes, context),
653
- group: callOrReturn(extension.config.group, context),
654
- spanning: callOrReturn(extension.config.spanning, context),
495
+ inclusive: callOrReturn(getExtensionField(extension, 'inclusive', context)),
496
+ excludes: callOrReturn(getExtensionField(extension, 'excludes', context)),
497
+ group: callOrReturn(getExtensionField(extension, 'group', context)),
498
+ spanning: callOrReturn(getExtensionField(extension, 'spanning', context)),
499
+ code: callOrReturn(getExtensionField(extension, 'code', context)),
655
500
  attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
656
501
  var _a;
657
502
  return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
658
503
  })),
659
504
  });
660
- if (extension.config.parseHTML) {
661
- schema.parseDOM = (_a = extension.config.parseHTML
662
- .bind(context)()) === null || _a === void 0 ? void 0 : _a.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
505
+ const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
506
+ if (parseHTML) {
507
+ schema.parseDOM = parseHTML
508
+ .map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
663
509
  }
664
- if (extension.config.renderHTML) {
665
- schema.toDOM = mark => {
666
- var _a;
667
- return (_a = extension.config.renderHTML) === null || _a === void 0 ? void 0 : _a.bind(context)({
668
- mark,
669
- HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
670
- });
671
- };
510
+ const renderHTML = getExtensionField(extension, 'renderHTML', context);
511
+ if (renderHTML) {
512
+ schema.toDOM = mark => renderHTML({
513
+ mark,
514
+ HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
515
+ });
672
516
  }
673
- return [extension.config.name, schema];
517
+ return [extension.name, schema];
674
518
  }));
675
519
  return new Schema({
676
520
  topNode,
@@ -680,67 +524,453 @@ function getSchema(extensions) {
680
524
  }
681
525
 
682
526
  function getSchemaTypeByName(name, schema) {
683
- if (schema.nodes[name]) {
684
- return schema.nodes[name];
527
+ return schema.nodes[name] || schema.marks[name] || null;
528
+ }
529
+
530
+ function isExtensionRulesEnabled(extension, enabled) {
531
+ if (Array.isArray(enabled)) {
532
+ return enabled.some(enabledExtension => {
533
+ const name = typeof enabledExtension === 'string'
534
+ ? enabledExtension
535
+ : enabledExtension.name;
536
+ return name === extension.name;
537
+ });
685
538
  }
686
- if (schema.marks[name]) {
687
- return schema.marks[name];
539
+ return enabled;
540
+ }
541
+
542
+ const getTextContentFromNodes = ($from, maxMatch = 500) => {
543
+ let textBefore = '';
544
+ $from.parent.nodesBetween(Math.max(0, $from.parentOffset - maxMatch), $from.parentOffset, (node, pos, parent, index) => {
545
+ var _a, _b;
546
+ textBefore += ((_b = (_a = node.type.spec).toText) === null || _b === void 0 ? void 0 : _b.call(_a, {
547
+ node, pos, parent, index,
548
+ })) || node.textContent || '%leaf%';
549
+ });
550
+ return textBefore;
551
+ };
552
+
553
+ function isRegExp(value) {
554
+ return Object.prototype.toString.call(value) === '[object RegExp]';
555
+ }
556
+
557
+ class InputRule {
558
+ constructor(config) {
559
+ this.find = config.find;
560
+ this.handler = config.handler;
688
561
  }
689
- return null;
562
+ }
563
+ const inputRuleMatcherHandler = (text, find) => {
564
+ if (isRegExp(find)) {
565
+ return find.exec(text);
566
+ }
567
+ const inputRuleMatch = find(text);
568
+ if (!inputRuleMatch) {
569
+ return null;
570
+ }
571
+ const result = [];
572
+ result.push(inputRuleMatch.text);
573
+ result.index = inputRuleMatch.index;
574
+ result.input = text;
575
+ result.data = inputRuleMatch.data;
576
+ if (inputRuleMatch.replaceWith) {
577
+ if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {
578
+ console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".');
579
+ }
580
+ result.push(inputRuleMatch.replaceWith);
581
+ }
582
+ return result;
583
+ };
584
+ function run$1(config) {
585
+ var _a;
586
+ const { editor, from, to, text, rules, plugin, } = config;
587
+ const { view } = editor;
588
+ if (view.composing) {
589
+ return false;
590
+ }
591
+ const $from = view.state.doc.resolve(from);
592
+ if (
593
+ // check for code node
594
+ $from.parent.type.spec.code
595
+ // check for code mark
596
+ || !!((_a = ($from.nodeBefore || $from.nodeAfter)) === null || _a === void 0 ? void 0 : _a.marks.find(mark => mark.type.spec.code))) {
597
+ return false;
598
+ }
599
+ let matched = false;
600
+ const textBefore = getTextContentFromNodes($from) + text;
601
+ rules.forEach(rule => {
602
+ if (matched) {
603
+ return;
604
+ }
605
+ const match = inputRuleMatcherHandler(textBefore, rule.find);
606
+ if (!match) {
607
+ return;
608
+ }
609
+ const tr = view.state.tr;
610
+ const state = createChainableState({
611
+ state: view.state,
612
+ transaction: tr,
613
+ });
614
+ const range = {
615
+ from: from - (match[0].length - text.length),
616
+ to,
617
+ };
618
+ const { commands, chain, can } = new CommandManager({
619
+ editor,
620
+ state,
621
+ });
622
+ const handler = rule.handler({
623
+ state,
624
+ range,
625
+ match,
626
+ commands,
627
+ chain,
628
+ can,
629
+ });
630
+ // stop if there are no changes
631
+ if (handler === null || !tr.steps.length) {
632
+ return;
633
+ }
634
+ // store transform as meta data
635
+ // so we can undo input rules within the `undoInputRules` command
636
+ tr.setMeta(plugin, {
637
+ transform: tr,
638
+ from,
639
+ to,
640
+ text,
641
+ });
642
+ view.dispatch(tr);
643
+ matched = true;
644
+ });
645
+ return matched;
646
+ }
647
+ /**
648
+ * Create an input rules plugin. When enabled, it will cause text
649
+ * input that matches any of the given rules to trigger the rule’s
650
+ * action.
651
+ */
652
+ function inputRulesPlugin(props) {
653
+ const { editor, rules } = props;
654
+ const plugin = new Plugin({
655
+ state: {
656
+ init() {
657
+ return null;
658
+ },
659
+ apply(tr, prev) {
660
+ const stored = tr.getMeta(plugin);
661
+ if (stored) {
662
+ return stored;
663
+ }
664
+ return tr.selectionSet || tr.docChanged
665
+ ? null
666
+ : prev;
667
+ },
668
+ },
669
+ props: {
670
+ handleTextInput(view, from, to, text) {
671
+ return run$1({
672
+ editor,
673
+ from,
674
+ to,
675
+ text,
676
+ rules,
677
+ plugin,
678
+ });
679
+ },
680
+ handleDOMEvents: {
681
+ compositionend: view => {
682
+ setTimeout(() => {
683
+ const { $cursor } = view.state.selection;
684
+ if ($cursor) {
685
+ run$1({
686
+ editor,
687
+ from: $cursor.pos,
688
+ to: $cursor.pos,
689
+ text: '',
690
+ rules,
691
+ plugin,
692
+ });
693
+ }
694
+ });
695
+ return false;
696
+ },
697
+ },
698
+ // add support for input rules to trigger on enter
699
+ // this is useful for example for code blocks
700
+ handleKeyDown(view, event) {
701
+ if (event.key !== 'Enter') {
702
+ return false;
703
+ }
704
+ const { $cursor } = view.state.selection;
705
+ if ($cursor) {
706
+ return run$1({
707
+ editor,
708
+ from: $cursor.pos,
709
+ to: $cursor.pos,
710
+ text: '\n',
711
+ rules,
712
+ plugin,
713
+ });
714
+ }
715
+ return false;
716
+ },
717
+ },
718
+ // @ts-ignore
719
+ isInputRules: true,
720
+ });
721
+ return plugin;
722
+ }
723
+
724
+ function isNumber(value) {
725
+ return typeof value === 'number';
726
+ }
727
+
728
+ class PasteRule {
729
+ constructor(config) {
730
+ this.find = config.find;
731
+ this.handler = config.handler;
732
+ }
733
+ }
734
+ const pasteRuleMatcherHandler = (text, find) => {
735
+ if (isRegExp(find)) {
736
+ return [...text.matchAll(find)];
737
+ }
738
+ const matches = find(text);
739
+ if (!matches) {
740
+ return [];
741
+ }
742
+ return matches.map(pasteRuleMatch => {
743
+ const result = [];
744
+ result.push(pasteRuleMatch.text);
745
+ result.index = pasteRuleMatch.index;
746
+ result.input = text;
747
+ result.data = pasteRuleMatch.data;
748
+ if (pasteRuleMatch.replaceWith) {
749
+ if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {
750
+ console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".');
751
+ }
752
+ result.push(pasteRuleMatch.replaceWith);
753
+ }
754
+ return result;
755
+ });
756
+ };
757
+ function run(config) {
758
+ const { editor, state, from, to, rule, } = config;
759
+ const { commands, chain, can } = new CommandManager({
760
+ editor,
761
+ state,
762
+ });
763
+ const handlers = [];
764
+ state.doc.nodesBetween(from, to, (node, pos) => {
765
+ if (!node.isTextblock || node.type.spec.code) {
766
+ return;
767
+ }
768
+ const resolvedFrom = Math.max(from, pos);
769
+ const resolvedTo = Math.min(to, pos + node.content.size);
770
+ const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc');
771
+ const matches = pasteRuleMatcherHandler(textToMatch, rule.find);
772
+ matches.forEach(match => {
773
+ if (match.index === undefined) {
774
+ return;
775
+ }
776
+ const start = resolvedFrom + match.index + 1;
777
+ const end = start + match[0].length;
778
+ const range = {
779
+ from: state.tr.mapping.map(start),
780
+ to: state.tr.mapping.map(end),
781
+ };
782
+ const handler = rule.handler({
783
+ state,
784
+ range,
785
+ match,
786
+ commands,
787
+ chain,
788
+ can,
789
+ });
790
+ handlers.push(handler);
791
+ });
792
+ });
793
+ const success = handlers.every(handler => handler !== null);
794
+ return success;
795
+ }
796
+ /**
797
+ * Create an paste rules plugin. When enabled, it will cause pasted
798
+ * text that matches any of the given rules to trigger the rule’s
799
+ * action.
800
+ */
801
+ function pasteRulesPlugin(props) {
802
+ const { editor, rules } = props;
803
+ let dragSourceElement = null;
804
+ let isPastedFromProseMirror = false;
805
+ let isDroppedFromProseMirror = false;
806
+ const plugins = rules.map(rule => {
807
+ return new Plugin({
808
+ // we register a global drag handler to track the current drag source element
809
+ view(view) {
810
+ const handleDragstart = (event) => {
811
+ var _a;
812
+ dragSourceElement = ((_a = view.dom.parentElement) === null || _a === void 0 ? void 0 : _a.contains(event.target))
813
+ ? view.dom.parentElement
814
+ : null;
815
+ };
816
+ window.addEventListener('dragstart', handleDragstart);
817
+ return {
818
+ destroy() {
819
+ window.removeEventListener('dragstart', handleDragstart);
820
+ },
821
+ };
822
+ },
823
+ props: {
824
+ handleDOMEvents: {
825
+ drop: view => {
826
+ isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement;
827
+ return false;
828
+ },
829
+ paste: (view, event) => {
830
+ var _a;
831
+ const html = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/html');
832
+ isPastedFromProseMirror = !!(html === null || html === void 0 ? void 0 : html.includes('data-pm-slice'));
833
+ return false;
834
+ },
835
+ },
836
+ },
837
+ appendTransaction: (transactions, oldState, state) => {
838
+ const transaction = transactions[0];
839
+ const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror;
840
+ const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror;
841
+ if (!isPaste && !isDrop) {
842
+ return;
843
+ }
844
+ // stop if there is no changed range
845
+ const from = oldState.doc.content.findDiffStart(state.doc.content);
846
+ const to = oldState.doc.content.findDiffEnd(state.doc.content);
847
+ if (!isNumber(from) || !to || from === to.b) {
848
+ return;
849
+ }
850
+ // build a chainable state
851
+ // so we can use a single transaction for all paste rules
852
+ const tr = state.tr;
853
+ const chainableState = createChainableState({
854
+ state,
855
+ transaction: tr,
856
+ });
857
+ const handler = run({
858
+ editor,
859
+ state: chainableState,
860
+ from: Math.max(from - 1, 0),
861
+ to: to.b,
862
+ rule,
863
+ });
864
+ // stop if there are no changes
865
+ if (!handler || !tr.steps.length) {
866
+ return;
867
+ }
868
+ return tr;
869
+ },
870
+ });
871
+ });
872
+ return plugins;
873
+ }
874
+
875
+ function findDuplicates(items) {
876
+ const filtered = items.filter((el, index) => items.indexOf(el) !== index);
877
+ return [...new Set(filtered)];
690
878
  }
691
879
 
692
880
  class ExtensionManager {
693
881
  constructor(extensions, editor) {
694
882
  this.splittableMarks = [];
695
883
  this.editor = editor;
696
- this.extensions = this.sort(extensions);
697
- this.schema = getSchema(this.extensions);
884
+ this.extensions = ExtensionManager.resolve(extensions);
885
+ this.schema = getSchemaByResolvedExtensions(this.extensions);
698
886
  this.extensions.forEach(extension => {
699
887
  var _a;
888
+ // store extension storage in editor
889
+ this.editor.extensionStorage[extension.name] = extension.storage;
700
890
  const context = {
891
+ name: extension.name,
701
892
  options: extension.options,
893
+ storage: extension.storage,
702
894
  editor: this.editor,
703
- type: getSchemaTypeByName(extension.config.name, this.schema),
895
+ type: getSchemaTypeByName(extension.name, this.schema),
704
896
  };
705
897
  if (extension.type === 'mark') {
706
- const keepOnSplit = (_a = callOrReturn(extension.config.keepOnSplit, context)) !== null && _a !== void 0 ? _a : true;
898
+ const keepOnSplit = (_a = callOrReturn(getExtensionField(extension, 'keepOnSplit', context))) !== null && _a !== void 0 ? _a : true;
707
899
  if (keepOnSplit) {
708
- this.splittableMarks.push(extension.config.name);
900
+ this.splittableMarks.push(extension.name);
709
901
  }
710
902
  }
711
- if (typeof extension.config.onCreate === 'function') {
712
- this.editor.on('create', extension.config.onCreate.bind(context));
903
+ const onBeforeCreate = getExtensionField(extension, 'onBeforeCreate', context);
904
+ if (onBeforeCreate) {
905
+ this.editor.on('beforeCreate', onBeforeCreate);
713
906
  }
714
- if (typeof extension.config.onUpdate === 'function') {
715
- this.editor.on('update', extension.config.onUpdate.bind(context));
907
+ const onCreate = getExtensionField(extension, 'onCreate', context);
908
+ if (onCreate) {
909
+ this.editor.on('create', onCreate);
716
910
  }
717
- if (typeof extension.config.onSelectionUpdate === 'function') {
718
- this.editor.on('selectionUpdate', extension.config.onSelectionUpdate.bind(context));
911
+ const onUpdate = getExtensionField(extension, 'onUpdate', context);
912
+ if (onUpdate) {
913
+ this.editor.on('update', onUpdate);
719
914
  }
720
- if (typeof extension.config.onViewUpdate === 'function') {
721
- this.editor.on('viewUpdate', extension.config.onViewUpdate.bind(context));
915
+ const onSelectionUpdate = getExtensionField(extension, 'onSelectionUpdate', context);
916
+ if (onSelectionUpdate) {
917
+ this.editor.on('selectionUpdate', onSelectionUpdate);
722
918
  }
723
- if (typeof extension.config.onTransaction === 'function') {
724
- this.editor.on('transaction', extension.config.onTransaction.bind(context));
919
+ const onTransaction = getExtensionField(extension, 'onTransaction', context);
920
+ if (onTransaction) {
921
+ this.editor.on('transaction', onTransaction);
725
922
  }
726
- if (typeof extension.config.onFocus === 'function') {
727
- this.editor.on('focus', extension.config.onFocus.bind(context));
923
+ const onFocus = getExtensionField(extension, 'onFocus', context);
924
+ if (onFocus) {
925
+ this.editor.on('focus', onFocus);
728
926
  }
729
- if (typeof extension.config.onBlur === 'function') {
730
- this.editor.on('blur', extension.config.onBlur.bind(context));
927
+ const onBlur = getExtensionField(extension, 'onBlur', context);
928
+ if (onBlur) {
929
+ this.editor.on('blur', onBlur);
731
930
  }
732
- if (typeof extension.config.onDestroy === 'function') {
733
- this.editor.on('destroy', extension.config.onDestroy.bind(context));
931
+ const onDestroy = getExtensionField(extension, 'onDestroy', context);
932
+ if (onDestroy) {
933
+ this.editor.on('destroy', onDestroy);
734
934
  }
735
935
  });
736
936
  }
737
- sort(extensions) {
937
+ static resolve(extensions) {
938
+ const resolvedExtensions = ExtensionManager.sort(ExtensionManager.flatten(extensions));
939
+ const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name));
940
+ if (duplicatedNames.length) {
941
+ console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames.map(item => `'${item}'`).join(', ')}]. This can lead to issues.`);
942
+ }
943
+ return resolvedExtensions;
944
+ }
945
+ static flatten(extensions) {
946
+ return extensions
947
+ .map(extension => {
948
+ const context = {
949
+ name: extension.name,
950
+ options: extension.options,
951
+ storage: extension.storage,
952
+ };
953
+ const addExtensions = getExtensionField(extension, 'addExtensions', context);
954
+ if (addExtensions) {
955
+ return [
956
+ extension,
957
+ ...this.flatten(addExtensions()),
958
+ ];
959
+ }
960
+ return extension;
961
+ })
962
+ // `Infinity` will break TypeScript so we set a number that is probably high enough
963
+ .flat(10);
964
+ }
965
+ static sort(extensions) {
738
966
  const defaultPriority = 100;
739
967
  return extensions.sort((a, b) => {
740
- if ((a.config.priority || defaultPriority) > (b.config.priority || defaultPriority)) {
968
+ const priorityA = getExtensionField(a, 'priority') || defaultPriority;
969
+ const priorityB = getExtensionField(b, 'priority') || defaultPriority;
970
+ if (priorityA > priorityB) {
741
971
  return -1;
742
972
  }
743
- if ((a.config.priority || defaultPriority) < (b.config.priority || defaultPriority)) {
973
+ if (priorityA < priorityB) {
744
974
  return 1;
745
975
  }
746
976
  return 0;
@@ -749,51 +979,79 @@ class ExtensionManager {
749
979
  get commands() {
750
980
  return this.extensions.reduce((commands, extension) => {
751
981
  const context = {
982
+ name: extension.name,
752
983
  options: extension.options,
984
+ storage: extension.storage,
753
985
  editor: this.editor,
754
- type: getSchemaTypeByName(extension.config.name, this.schema),
986
+ type: getSchemaTypeByName(extension.name, this.schema),
755
987
  };
756
- if (!extension.config.addCommands) {
988
+ const addCommands = getExtensionField(extension, 'addCommands', context);
989
+ if (!addCommands) {
757
990
  return commands;
758
991
  }
759
992
  return {
760
993
  ...commands,
761
- ...extension.config.addCommands.bind(context)(),
994
+ ...addCommands(),
762
995
  };
763
996
  }, {});
764
997
  }
765
998
  get plugins() {
766
- return [...this.extensions]
767
- .reverse()
999
+ const { editor } = this;
1000
+ // With ProseMirror, first plugins within an array are executed first.
1001
+ // In tiptap, we provide the ability to override plugins,
1002
+ // so it feels more natural to run plugins at the end of an array first.
1003
+ // That’s why we have to reverse the `extensions` array and sort again
1004
+ // based on the `priority` option.
1005
+ const extensions = ExtensionManager.sort([...this.extensions].reverse());
1006
+ const inputRules = [];
1007
+ const pasteRules = [];
1008
+ const allPlugins = extensions
768
1009
  .map(extension => {
769
1010
  const context = {
1011
+ name: extension.name,
770
1012
  options: extension.options,
771
- editor: this.editor,
772
- type: getSchemaTypeByName(extension.config.name, this.schema),
1013
+ storage: extension.storage,
1014
+ editor,
1015
+ type: getSchemaTypeByName(extension.name, this.schema),
773
1016
  };
774
1017
  const plugins = [];
775
- if (extension.config.addKeyboardShortcuts) {
776
- const keyMapPlugin = keymap(extension.config.addKeyboardShortcuts.bind(context)());
1018
+ const addKeyboardShortcuts = getExtensionField(extension, 'addKeyboardShortcuts', context);
1019
+ if (addKeyboardShortcuts) {
1020
+ const bindings = Object.fromEntries(Object
1021
+ .entries(addKeyboardShortcuts())
1022
+ .map(([shortcut, method]) => {
1023
+ return [shortcut, () => method({ editor })];
1024
+ }));
1025
+ const keyMapPlugin = keymap(bindings);
777
1026
  plugins.push(keyMapPlugin);
778
1027
  }
779
- if (this.editor.options.enableInputRules && extension.config.addInputRules) {
780
- const inputRules$1 = extension.config.addInputRules.bind(context)();
781
- const inputRulePlugins = inputRules$1.length
782
- ? [inputRules({ rules: inputRules$1 })]
783
- : [];
784
- plugins.push(...inputRulePlugins);
1028
+ const addInputRules = getExtensionField(extension, 'addInputRules', context);
1029
+ if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {
1030
+ inputRules.push(...addInputRules());
785
1031
  }
786
- if (this.editor.options.enablePasteRules && extension.config.addPasteRules) {
787
- const pasteRulePlugins = extension.config.addPasteRules.bind(context)();
788
- plugins.push(...pasteRulePlugins);
1032
+ const addPasteRules = getExtensionField(extension, 'addPasteRules', context);
1033
+ if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {
1034
+ pasteRules.push(...addPasteRules());
789
1035
  }
790
- if (extension.config.addProseMirrorPlugins) {
791
- const proseMirrorPlugins = extension.config.addProseMirrorPlugins.bind(context)();
1036
+ const addProseMirrorPlugins = getExtensionField(extension, 'addProseMirrorPlugins', context);
1037
+ if (addProseMirrorPlugins) {
1038
+ const proseMirrorPlugins = addProseMirrorPlugins();
792
1039
  plugins.push(...proseMirrorPlugins);
793
1040
  }
794
1041
  return plugins;
795
1042
  })
796
1043
  .flat();
1044
+ return [
1045
+ inputRulesPlugin({
1046
+ editor,
1047
+ rules: inputRules,
1048
+ }),
1049
+ ...pasteRulesPlugin({
1050
+ editor,
1051
+ rules: pasteRules,
1052
+ }),
1053
+ ...allPlugins,
1054
+ ];
797
1055
  }
798
1056
  get attributes() {
799
1057
  return getAttributesFromExtensions(this.extensions);
@@ -802,19 +1060,23 @@ class ExtensionManager {
802
1060
  const { editor } = this;
803
1061
  const { nodeExtensions } = splitExtensions(this.extensions);
804
1062
  return Object.fromEntries(nodeExtensions
805
- .filter(extension => !!extension.config.addNodeView)
1063
+ .filter(extension => !!getExtensionField(extension, 'addNodeView'))
806
1064
  .map(extension => {
807
- var _a;
808
- const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.config.name);
1065
+ const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name);
809
1066
  const context = {
1067
+ name: extension.name,
810
1068
  options: extension.options,
1069
+ storage: extension.storage,
811
1070
  editor,
812
- type: getNodeType(extension.config.name, this.schema),
1071
+ type: getNodeType(extension.name, this.schema),
813
1072
  };
814
- const renderer = (_a = extension.config.addNodeView) === null || _a === void 0 ? void 0 : _a.call(context);
1073
+ const addNodeView = getExtensionField(extension, 'addNodeView', context);
1074
+ if (!addNodeView) {
1075
+ return [];
1076
+ }
815
1077
  const nodeview = (node, view, getPos, decorations) => {
816
1078
  const HTMLAttributes = getRenderedAttributes(node, extensionAttributes);
817
- return renderer({
1079
+ return addNodeView()({
818
1080
  editor,
819
1081
  node,
820
1082
  getPos,
@@ -823,90 +1085,20 @@ class ExtensionManager {
823
1085
  extension,
824
1086
  });
825
1087
  };
826
- return [extension.config.name, nodeview];
827
- }));
828
- }
829
- get textSerializers() {
830
- const { editor } = this;
831
- const { nodeExtensions } = splitExtensions(this.extensions);
832
- return Object.fromEntries(nodeExtensions
833
- .filter(extension => !!extension.config.renderText)
834
- .map(extension => {
835
- const context = {
836
- options: extension.options,
837
- editor,
838
- type: getNodeType(extension.config.name, this.schema),
839
- };
840
- const textSerializer = (props) => { var _a; return (_a = extension.config.renderText) === null || _a === void 0 ? void 0 : _a.call(context, props); };
841
- return [extension.config.name, textSerializer];
1088
+ return [extension.name, nodeview];
842
1089
  }));
843
1090
  }
844
1091
  }
845
1092
 
846
- class EventEmitter {
847
- constructor() {
848
- this.callbacks = {};
849
- }
850
- on(event, fn) {
851
- if (!this.callbacks[event]) {
852
- this.callbacks[event] = [];
853
- }
854
- this.callbacks[event].push(fn);
855
- return this;
856
- }
857
- emit(event, ...args) {
858
- const callbacks = this.callbacks[event];
859
- if (callbacks) {
860
- callbacks.forEach(callback => callback.apply(this, args));
861
- }
862
- return this;
863
- }
864
- off(event, fn) {
865
- const callbacks = this.callbacks[event];
866
- if (callbacks) {
867
- if (fn) {
868
- this.callbacks[event] = callbacks.filter(callback => callback !== fn);
869
- }
870
- else {
871
- delete this.callbacks[event];
872
- }
873
- }
874
- return this;
875
- }
876
- removeAllListeners() {
877
- this.callbacks = {};
878
- }
879
- }
880
-
881
- /*! *****************************************************************************
882
- Copyright (c) Microsoft Corporation.
883
-
884
- Permission to use, copy, modify, and/or distribute this software for any
885
- purpose with or without fee is hereby granted.
886
-
887
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
888
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
889
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
890
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
891
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
892
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
893
- PERFORMANCE OF THIS SOFTWARE.
894
- ***************************************************************************** */
895
-
896
- function __classPrivateFieldGet(receiver, state, kind, f) {
897
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
898
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
899
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
900
- }
901
-
902
1093
  // see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts
903
- function getType(payload) {
904
- return Object.prototype.toString.call(payload).slice(8, -1);
1094
+ function getType(value) {
1095
+ return Object.prototype.toString.call(value).slice(8, -1);
905
1096
  }
906
- function isPlainObject(payload) {
907
- if (getType(payload) !== 'Object')
1097
+ function isPlainObject(value) {
1098
+ if (getType(value) !== 'Object') {
908
1099
  return false;
909
- return payload.constructor === Object && Object.getPrototypeOf(payload) === Object.prototype;
1100
+ }
1101
+ return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
910
1102
  }
911
1103
 
912
1104
  function mergeDeep(target, source) {
@@ -929,69 +1121,115 @@ function mergeDeep(target, source) {
929
1121
  return output;
930
1122
  }
931
1123
 
932
- var _configure$2;
933
1124
  class Extension {
934
- constructor(config) {
1125
+ constructor(config = {}) {
935
1126
  this.type = 'extension';
1127
+ this.name = 'extension';
1128
+ this.parent = null;
1129
+ this.child = null;
936
1130
  this.config = {
937
- name: 'extension',
938
- priority: 100,
1131
+ name: this.name,
939
1132
  defaultOptions: {},
940
1133
  };
941
- _configure$2.set(this, (options) => {
942
- this.options = mergeDeep(this.config.defaultOptions, options);
943
- return this;
944
- });
945
1134
  this.config = {
946
1135
  ...this.config,
947
1136
  ...config,
948
1137
  };
1138
+ this.name = this.config.name;
1139
+ if (config.defaultOptions) {
1140
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
1141
+ }
1142
+ // TODO: remove `addOptions` fallback
949
1143
  this.options = this.config.defaultOptions;
1144
+ if (this.config.addOptions) {
1145
+ this.options = callOrReturn(getExtensionField(this, 'addOptions', {
1146
+ name: this.name,
1147
+ }));
1148
+ }
1149
+ this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
1150
+ name: this.name,
1151
+ options: this.options,
1152
+ })) || {};
950
1153
  }
951
- static create(config) {
1154
+ static create(config = {}) {
952
1155
  return new Extension(config);
953
1156
  }
954
1157
  configure(options = {}) {
955
- var _a;
956
- return __classPrivateFieldGet((_a = Extension
957
- .create(this.config)), _configure$2).call(_a, options);
958
- }
959
- extend(extendedConfig) {
960
- return new Extension({
961
- ...this.config,
962
- ...extendedConfig,
963
- });
1158
+ // return a new instance so we can use the same extension
1159
+ // with different calls of `configure`
1160
+ const extension = this.extend();
1161
+ extension.options = mergeDeep(this.options, options);
1162
+ extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
1163
+ name: extension.name,
1164
+ options: extension.options,
1165
+ }));
1166
+ return extension;
1167
+ }
1168
+ extend(extendedConfig = {}) {
1169
+ const extension = new Extension(extendedConfig);
1170
+ extension.parent = this;
1171
+ this.child = extension;
1172
+ extension.name = extendedConfig.name
1173
+ ? extendedConfig.name
1174
+ : extension.parent.name;
1175
+ if (extendedConfig.defaultOptions) {
1176
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
1177
+ }
1178
+ extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
1179
+ name: extension.name,
1180
+ }));
1181
+ extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
1182
+ name: extension.name,
1183
+ options: extension.options,
1184
+ }));
1185
+ return extension;
964
1186
  }
965
- }
966
- _configure$2 = new WeakMap();
1187
+ }
967
1188
 
968
- const textBetween = (editor, from, to, blockSeparator, leafText) => {
1189
+ function getTextBetween(startNode, range, options) {
1190
+ const { from, to } = range;
1191
+ const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
969
1192
  let text = '';
970
1193
  let separated = true;
971
- editor.state.doc.nodesBetween(from, to, (node, pos) => {
1194
+ startNode.nodesBetween(from, to, (node, pos, parent, index) => {
972
1195
  var _a;
973
- const textSerializer = editor.extensionManager.textSerializers[node.type.name];
1196
+ const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
974
1197
  if (textSerializer) {
975
- text += textSerializer({ node });
976
- separated = !blockSeparator;
1198
+ if (node.isBlock && !separated) {
1199
+ text += blockSeparator;
1200
+ separated = true;
1201
+ }
1202
+ if (parent) {
1203
+ text += textSerializer({
1204
+ node,
1205
+ pos,
1206
+ parent,
1207
+ index,
1208
+ range,
1209
+ });
1210
+ }
977
1211
  }
978
1212
  else if (node.isText) {
979
- text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
980
- separated = !blockSeparator;
1213
+ text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos); // eslint-disable-line
1214
+ separated = false;
981
1215
  }
982
- else if (node.isLeaf && leafText) {
983
- text += leafText;
984
- separated = !blockSeparator;
985
- }
986
- else if (!separated && node.isBlock) {
1216
+ else if (node.isBlock && !separated) {
987
1217
  text += blockSeparator;
988
1218
  separated = true;
989
1219
  }
990
- }, 0);
1220
+ });
991
1221
  return text;
992
- };
1222
+ }
1223
+
1224
+ function getTextSerializersFromSchema(schema) {
1225
+ return Object.fromEntries(Object
1226
+ .entries(schema.nodes)
1227
+ .filter(([, node]) => node.spec.toText)
1228
+ .map(([name, node]) => [name, node.spec.toText]));
1229
+ }
1230
+
993
1231
  const ClipboardTextSerializer = Extension.create({
994
- name: 'editable',
1232
+ name: 'clipboardTextSerializer',
995
1233
  addProseMirrorPlugins() {
996
1234
  return [
997
1235
  new Plugin({
@@ -999,8 +1237,16 @@ const ClipboardTextSerializer = Extension.create({
999
1237
  props: {
1000
1238
  clipboardTextSerializer: () => {
1001
1239
  const { editor } = this;
1002
- const { from, to } = editor.state.selection;
1003
- return textBetween(editor, from, to, '\n');
1240
+ const { state, schema } = editor;
1241
+ const { doc, selection } = state;
1242
+ const { ranges } = selection;
1243
+ const from = Math.min(...ranges.map(range => range.$from.pos));
1244
+ const to = Math.max(...ranges.map(range => range.$to.pos));
1245
+ const textSerializers = getTextSerializersFromSchema(schema);
1246
+ const range = { from, to };
1247
+ return getTextBetween(doc, range, {
1248
+ textSerializers,
1249
+ });
1004
1250
  },
1005
1251
  },
1006
1252
  }),
@@ -1008,72 +1254,78 @@ const ClipboardTextSerializer = Extension.create({
1008
1254
  },
1009
1255
  });
1010
1256
 
1011
- const blur = () => ({ view }) => {
1012
- const element = view.dom;
1013
- element.blur();
1257
+ const blur = () => ({ editor, view }) => {
1258
+ requestAnimationFrame(() => {
1259
+ var _a;
1260
+ if (!editor.isDestroyed) {
1261
+ view.dom.blur();
1262
+ // Browsers should remove the caret on blur but safari does not.
1263
+ // See: https://github.com/ueberdosis/tiptap/issues/2405
1264
+ (_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
1265
+ }
1266
+ });
1014
1267
  return true;
1015
1268
  };
1016
1269
 
1017
- var blur$1 = /*#__PURE__*/Object.freeze({
1018
- __proto__: null,
1019
- blur: blur
1020
- });
1021
-
1022
1270
  const clearContent = (emitUpdate = false) => ({ commands }) => {
1023
1271
  return commands.setContent('', emitUpdate);
1024
1272
  };
1025
1273
 
1026
- var clearContent$1 = /*#__PURE__*/Object.freeze({
1027
- __proto__: null,
1028
- clearContent: clearContent
1029
- });
1030
-
1031
1274
  const clearNodes = () => ({ state, tr, dispatch }) => {
1032
1275
  const { selection } = tr;
1033
1276
  const { ranges } = selection;
1034
- ranges.forEach(range => {
1035
- state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
1036
- if (!node.type.isText) {
1037
- const fromPos = tr.doc.resolve(tr.mapping.map(pos + 1));
1038
- const toPos = tr.doc.resolve(tr.mapping.map(pos + node.nodeSize - 1));
1039
- const nodeRange = fromPos.blockRange(toPos);
1040
- if (nodeRange) {
1041
- const targetLiftDepth = liftTarget(nodeRange);
1042
- if (node.type.isTextblock && dispatch) {
1043
- tr.setNodeMarkup(nodeRange.start, state.doc.type.contentMatch.defaultType);
1044
- }
1045
- if ((targetLiftDepth || targetLiftDepth === 0) && dispatch) {
1046
- tr.lift(nodeRange, targetLiftDepth);
1047
- }
1048
- }
1277
+ if (!dispatch) {
1278
+ return true;
1279
+ }
1280
+ ranges.forEach(({ $from, $to }) => {
1281
+ state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
1282
+ if (node.type.isText) {
1283
+ return;
1284
+ }
1285
+ const { doc, mapping } = tr;
1286
+ const $mappedFrom = doc.resolve(mapping.map(pos));
1287
+ const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
1288
+ const nodeRange = $mappedFrom.blockRange($mappedTo);
1289
+ if (!nodeRange) {
1290
+ return;
1291
+ }
1292
+ const targetLiftDepth = liftTarget(nodeRange);
1293
+ if (node.type.isTextblock) {
1294
+ const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
1295
+ tr.setNodeMarkup(nodeRange.start, defaultType);
1296
+ }
1297
+ if (targetLiftDepth || targetLiftDepth === 0) {
1298
+ tr.lift(nodeRange, targetLiftDepth);
1049
1299
  }
1050
1300
  });
1051
1301
  });
1052
1302
  return true;
1053
1303
  };
1054
1304
 
1055
- var clearNodes$1 = /*#__PURE__*/Object.freeze({
1056
- __proto__: null,
1057
- clearNodes: clearNodes
1058
- });
1059
-
1060
1305
  const command = fn => props => {
1061
1306
  return fn(props);
1062
1307
  };
1063
1308
 
1064
- var command$1 = /*#__PURE__*/Object.freeze({
1065
- __proto__: null,
1066
- command: command
1067
- });
1068
-
1069
1309
  const createParagraphNear = () => ({ state, dispatch }) => {
1070
- return createParagraphNear$2(state, dispatch);
1310
+ return createParagraphNear$1(state, dispatch);
1071
1311
  };
1072
1312
 
1073
- var createParagraphNear$1 = /*#__PURE__*/Object.freeze({
1074
- __proto__: null,
1075
- createParagraphNear: createParagraphNear
1076
- });
1313
+ const deleteNode = typeOrName => ({ tr, state, dispatch }) => {
1314
+ const type = getNodeType(typeOrName, state.schema);
1315
+ const $pos = tr.selection.$anchor;
1316
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
1317
+ const node = $pos.node(depth);
1318
+ if (node.type === type) {
1319
+ if (dispatch) {
1320
+ const from = $pos.before(depth);
1321
+ const to = $pos.after(depth);
1322
+ tr.delete(from, to).scrollIntoView();
1323
+ }
1324
+ return true;
1325
+ }
1326
+ }
1327
+ return false;
1328
+ };
1077
1329
 
1078
1330
  const deleteRange = range => ({ tr, dispatch }) => {
1079
1331
  const { from, to } = range;
@@ -1083,59 +1335,73 @@ const deleteRange = range => ({ tr, dispatch }) => {
1083
1335
  return true;
1084
1336
  };
1085
1337
 
1086
- var deleteRange$1 = /*#__PURE__*/Object.freeze({
1087
- __proto__: null,
1088
- deleteRange: deleteRange
1089
- });
1090
-
1091
1338
  const deleteSelection = () => ({ state, dispatch }) => {
1092
- return deleteSelection$2(state, dispatch);
1339
+ return deleteSelection$1(state, dispatch);
1093
1340
  };
1094
1341
 
1095
- var deleteSelection$1 = /*#__PURE__*/Object.freeze({
1096
- __proto__: null,
1097
- deleteSelection: deleteSelection
1098
- });
1099
-
1100
1342
  const enter = () => ({ commands }) => {
1101
1343
  return commands.keyboardShortcut('Enter');
1102
1344
  };
1103
1345
 
1104
- var enter$1 = /*#__PURE__*/Object.freeze({
1105
- __proto__: null,
1106
- enter: enter
1107
- });
1108
-
1109
1346
  const exitCode = () => ({ state, dispatch }) => {
1110
- return exitCode$2(state, dispatch);
1347
+ return exitCode$1(state, dispatch);
1111
1348
  };
1112
1349
 
1113
- var exitCode$1 = /*#__PURE__*/Object.freeze({
1114
- __proto__: null,
1115
- exitCode: exitCode
1116
- });
1350
+ /**
1351
+ * Check if object1 includes object2
1352
+ * @param object1 Object
1353
+ * @param object2 Object
1354
+ */
1355
+ function objectIncludes(object1, object2, options = { strict: true }) {
1356
+ const keys = Object.keys(object2);
1357
+ if (!keys.length) {
1358
+ return true;
1359
+ }
1360
+ return keys.every(key => {
1361
+ if (options.strict) {
1362
+ return object2[key] === object1[key];
1363
+ }
1364
+ if (isRegExp(object2[key])) {
1365
+ return object2[key].test(object1[key]);
1366
+ }
1367
+ return object2[key] === object1[key];
1368
+ });
1369
+ }
1117
1370
 
1118
- function getMarkRange($pos, type) {
1371
+ function findMarkInSet(marks, type, attributes = {}) {
1372
+ return marks.find(item => {
1373
+ return item.type === type && objectIncludes(item.attrs, attributes);
1374
+ });
1375
+ }
1376
+ function isMarkInSet(marks, type, attributes = {}) {
1377
+ return !!findMarkInSet(marks, type, attributes);
1378
+ }
1379
+ function getMarkRange($pos, type, attributes = {}) {
1119
1380
  if (!$pos || !type) {
1120
1381
  return;
1121
1382
  }
1122
- const start = $pos.parent.childAfter($pos.parentOffset);
1383
+ let start = $pos.parent.childAfter($pos.parentOffset);
1384
+ if ($pos.parentOffset === start.offset && start.offset !== 0) {
1385
+ start = $pos.parent.childBefore($pos.parentOffset);
1386
+ }
1123
1387
  if (!start.node) {
1124
1388
  return;
1125
1389
  }
1126
- const link = start.node.marks.find(mark => mark.type === type);
1127
- if (!link) {
1390
+ const mark = findMarkInSet([...start.node.marks], type, attributes);
1391
+ if (!mark) {
1128
1392
  return;
1129
1393
  }
1130
- let startIndex = $pos.index();
1394
+ let startIndex = start.index;
1131
1395
  let startPos = $pos.start() + start.offset;
1132
1396
  let endIndex = startIndex + 1;
1133
1397
  let endPos = startPos + start.node.nodeSize;
1134
- while (startIndex > 0 && link.isInSet($pos.parent.child(startIndex - 1).marks)) {
1398
+ findMarkInSet([...start.node.marks], type, attributes);
1399
+ while (startIndex > 0 && mark.isInSet($pos.parent.child(startIndex - 1).marks)) {
1135
1400
  startIndex -= 1;
1136
1401
  startPos -= $pos.parent.child(startIndex).nodeSize;
1137
1402
  }
1138
- while (endIndex < $pos.parent.childCount && link.isInSet($pos.parent.child(endIndex).marks)) {
1403
+ while (endIndex < $pos.parent.childCount
1404
+ && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
1139
1405
  endPos += $pos.parent.child(endIndex).nodeSize;
1140
1406
  endIndex += 1;
1141
1407
  }
@@ -1145,13 +1411,23 @@ function getMarkRange($pos, type) {
1145
1411
  };
1146
1412
  }
1147
1413
 
1148
- const extendMarkRange = typeOrName => ({ tr, state, dispatch }) => {
1414
+ function getMarkType(nameOrType, schema) {
1415
+ if (typeof nameOrType === 'string') {
1416
+ if (!schema.marks[nameOrType]) {
1417
+ throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
1418
+ }
1419
+ return schema.marks[nameOrType];
1420
+ }
1421
+ return nameOrType;
1422
+ }
1423
+
1424
+ const extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1149
1425
  const type = getMarkType(typeOrName, state.schema);
1150
1426
  const { doc, selection } = tr;
1151
- const { $from, empty } = selection;
1152
- if (empty && dispatch) {
1153
- const range = getMarkRange($from, type);
1154
- if (range) {
1427
+ const { $from, from, to } = selection;
1428
+ if (dispatch) {
1429
+ const range = getMarkRange($from, type, attributes);
1430
+ if (range && range.from <= from && range.to >= to) {
1155
1431
  const newSelection = TextSelection.create(doc, range.from, range.to);
1156
1432
  tr.setSelection(newSelection);
1157
1433
  }
@@ -1159,11 +1435,6 @@ const extendMarkRange = typeOrName => ({ tr, state, dispatch }) => {
1159
1435
  return true;
1160
1436
  };
1161
1437
 
1162
- var extendMarkRange$1 = /*#__PURE__*/Object.freeze({
1163
- __proto__: null,
1164
- extendMarkRange: extendMarkRange
1165
- });
1166
-
1167
1438
  const first = commands => props => {
1168
1439
  const items = typeof commands === 'function'
1169
1440
  ? commands(props)
@@ -1176,87 +1447,148 @@ const first = commands => props => {
1176
1447
  return false;
1177
1448
  };
1178
1449
 
1179
- var first$1 = /*#__PURE__*/Object.freeze({
1180
- __proto__: null,
1181
- first: first
1182
- });
1183
-
1184
- function minMax(value = 0, min = 0, max = 0) {
1185
- return Math.min(Math.max(value, min), max);
1186
- }
1187
-
1188
- function isClass(item) {
1450
+ function isClass(value) {
1189
1451
  var _a;
1190
- if (((_a = item.constructor) === null || _a === void 0 ? void 0 : _a.toString().substring(0, 5)) !== 'class') {
1452
+ if (((_a = value.constructor) === null || _a === void 0 ? void 0 : _a.toString().substring(0, 5)) !== 'class') {
1191
1453
  return false;
1192
1454
  }
1193
1455
  return true;
1194
1456
  }
1195
1457
 
1196
- function isObject(item) {
1197
- return (item
1198
- && typeof item === 'object'
1199
- && !Array.isArray(item)
1200
- && !isClass(item));
1458
+ function isObject(value) {
1459
+ return (value
1460
+ && typeof value === 'object'
1461
+ && !Array.isArray(value)
1462
+ && !isClass(value));
1201
1463
  }
1202
1464
 
1203
1465
  function isTextSelection(value) {
1204
1466
  return isObject(value) && value instanceof TextSelection;
1205
1467
  }
1206
1468
 
1207
- function resolveSelection(state, position = null) {
1469
+ function minMax(value = 0, min = 0, max = 0) {
1470
+ return Math.min(Math.max(value, min), max);
1471
+ }
1472
+
1473
+ function resolveFocusPosition(doc, position = null) {
1208
1474
  if (!position) {
1209
1475
  return null;
1210
1476
  }
1477
+ const selectionAtStart = Selection.atStart(doc);
1478
+ const selectionAtEnd = Selection.atEnd(doc);
1211
1479
  if (position === 'start' || position === true) {
1212
- return {
1213
- from: 0,
1214
- to: 0,
1215
- };
1480
+ return selectionAtStart;
1216
1481
  }
1217
1482
  if (position === 'end') {
1218
- const { size } = state.doc.content;
1219
- return {
1220
- from: size,
1221
- to: size,
1222
- };
1483
+ return selectionAtEnd;
1223
1484
  }
1224
- return {
1225
- from: position,
1226
- to: position,
1485
+ const minPos = selectionAtStart.from;
1486
+ const maxPos = selectionAtEnd.to;
1487
+ if (position === 'all') {
1488
+ return TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
1489
+ }
1490
+ return TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
1491
+ }
1492
+
1493
+ function isiOS() {
1494
+ return [
1495
+ 'iPad Simulator',
1496
+ 'iPhone Simulator',
1497
+ 'iPod Simulator',
1498
+ 'iPad',
1499
+ 'iPhone',
1500
+ 'iPod',
1501
+ ].includes(navigator.platform)
1502
+ // iPad on iOS 13 detection
1503
+ || (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
1504
+ }
1505
+
1506
+ const focus = (position = null, options = {}) => ({ editor, view, tr, dispatch, }) => {
1507
+ options = {
1508
+ scrollIntoView: true,
1509
+ ...options,
1510
+ };
1511
+ const delayedFocus = () => {
1512
+ // focus within `requestAnimationFrame` breaks focus on iOS
1513
+ // so we have to call this
1514
+ if (isiOS()) {
1515
+ view.dom.focus();
1516
+ }
1517
+ // For React we have to focus asynchronously. Otherwise wild things happen.
1518
+ // see: https://github.com/ueberdosis/tiptap/issues/1520
1519
+ requestAnimationFrame(() => {
1520
+ if (!editor.isDestroyed) {
1521
+ view.focus();
1522
+ if (options === null || options === void 0 ? void 0 : options.scrollIntoView) {
1523
+ editor.commands.scrollIntoView();
1524
+ }
1525
+ }
1526
+ });
1227
1527
  };
1228
- }
1229
- const focus = (position = null) => ({ editor, view, tr, dispatch, }) => {
1230
1528
  if ((view.hasFocus() && position === null) || position === false) {
1231
1529
  return true;
1232
1530
  }
1233
1531
  // we don’t try to resolve a NodeSelection or CellSelection
1234
1532
  if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
1235
- view.focus();
1533
+ delayedFocus();
1236
1534
  return true;
1237
1535
  }
1238
- const { from, to } = resolveSelection(editor.state, position) || editor.state.selection;
1239
- const { doc, storedMarks } = tr;
1240
- const resolvedFrom = minMax(from, 0, doc.content.size);
1241
- const resolvedEnd = minMax(to, 0, doc.content.size);
1242
- const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
1536
+ const selection = resolveFocusPosition(editor.state.doc, position) || editor.state.selection;
1243
1537
  const isSameSelection = editor.state.selection.eq(selection);
1244
1538
  if (dispatch) {
1245
- tr.setSelection(selection);
1539
+ if (!isSameSelection) {
1540
+ tr.setSelection(selection);
1541
+ }
1246
1542
  // `tr.setSelection` resets the stored marks
1247
1543
  // so we’ll restore them if the selection is the same as before
1248
- if (isSameSelection && storedMarks) {
1249
- tr.setStoredMarks(storedMarks);
1544
+ if (isSameSelection && tr.storedMarks) {
1545
+ tr.setStoredMarks(tr.storedMarks);
1250
1546
  }
1251
- view.focus();
1547
+ delayedFocus();
1252
1548
  }
1253
1549
  return true;
1254
1550
  };
1255
1551
 
1256
- var focus$1 = /*#__PURE__*/Object.freeze({
1257
- __proto__: null,
1258
- focus: focus
1259
- });
1552
+ const forEach = (items, fn) => props => {
1553
+ return items.every((item, index) => fn(item, { ...props, index }));
1554
+ };
1555
+
1556
+ const insertContent = (value, options) => ({ tr, commands }) => {
1557
+ return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
1558
+ };
1559
+
1560
+ function elementFromString(value) {
1561
+ // add a wrapper to preserve leading and trailing whitespace
1562
+ const wrappedValue = `<body>${value}</body>`;
1563
+ return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body;
1564
+ }
1565
+
1566
+ function createNodeFromContent(content, schema, options) {
1567
+ options = {
1568
+ slice: true,
1569
+ parseOptions: {},
1570
+ ...options,
1571
+ };
1572
+ if (typeof content === 'object' && content !== null) {
1573
+ try {
1574
+ if (Array.isArray(content)) {
1575
+ return Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)));
1576
+ }
1577
+ return schema.nodeFromJSON(content);
1578
+ }
1579
+ catch (error) {
1580
+ console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
1581
+ return createNodeFromContent('', schema, options);
1582
+ }
1583
+ }
1584
+ if (typeof content === 'string') {
1585
+ const parser = DOMParser.fromSchema(schema);
1586
+ return options.slice
1587
+ ? parser.parseSlice(elementFromString(content), options.parseOptions).content
1588
+ : parser.parse(elementFromString(content), options.parseOptions);
1589
+ }
1590
+ return createNodeFromContent('', schema, options);
1591
+ }
1260
1592
 
1261
1593
  // source: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466
1262
1594
  function selectionToInsertionEnd(tr, startLen, bias) {
@@ -1278,95 +1610,89 @@ function selectionToInsertionEnd(tr, startLen, bias) {
1278
1610
  tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
1279
1611
  }
1280
1612
 
1281
- const insertContent = value => ({ tr, dispatch, editor }) => {
1613
+ const isFragment = (nodeOrFragment) => {
1614
+ return nodeOrFragment.toString().startsWith('<');
1615
+ };
1616
+ const insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
1282
1617
  if (dispatch) {
1283
- const content = createNodeFromContent(value, editor.schema);
1284
- if (typeof content === 'string') {
1285
- tr.insertText(content);
1618
+ options = {
1619
+ parseOptions: {},
1620
+ updateSelection: true,
1621
+ ...options,
1622
+ };
1623
+ const content = createNodeFromContent(value, editor.schema, {
1624
+ parseOptions: {
1625
+ preserveWhitespace: 'full',
1626
+ ...options.parseOptions,
1627
+ },
1628
+ });
1629
+ // don’t dispatch an empty fragment because this can lead to strange errors
1630
+ if (content.toString() === '<>') {
1286
1631
  return true;
1287
1632
  }
1288
- if (!tr.selection.empty) {
1289
- tr.deleteSelection();
1633
+ let { from, to } = typeof position === 'number'
1634
+ ? { from: position, to: position }
1635
+ : position;
1636
+ let isOnlyTextContent = true;
1637
+ let isOnlyBlockContent = true;
1638
+ const nodes = isFragment(content)
1639
+ ? content
1640
+ : [content];
1641
+ nodes.forEach(node => {
1642
+ // check if added node is valid
1643
+ node.check();
1644
+ isOnlyTextContent = isOnlyTextContent
1645
+ ? node.isText && node.marks.length === 0
1646
+ : false;
1647
+ isOnlyBlockContent = isOnlyBlockContent
1648
+ ? node.isBlock
1649
+ : false;
1650
+ });
1651
+ // check if we can replace the wrapping node by
1652
+ // the newly inserted content
1653
+ // example:
1654
+ // replace an empty paragraph by an inserted image
1655
+ // instead of inserting the image below the paragraph
1656
+ if (from === to && isOnlyBlockContent) {
1657
+ const { parent } = tr.doc.resolve(from);
1658
+ const isEmptyTextBlock = parent.isTextblock
1659
+ && !parent.type.spec.code
1660
+ && !parent.childCount;
1661
+ if (isEmptyTextBlock) {
1662
+ from -= 1;
1663
+ to += 1;
1664
+ }
1665
+ }
1666
+ // if there is only plain text we have to use `insertText`
1667
+ // because this will keep the current marks
1668
+ if (isOnlyTextContent) {
1669
+ tr.insertText(value, from, to);
1670
+ }
1671
+ else {
1672
+ tr.replaceWith(from, to, content);
1673
+ }
1674
+ // set cursor at end of inserted content
1675
+ if (options.updateSelection) {
1676
+ selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
1290
1677
  }
1291
- tr.insert(tr.selection.anchor, content);
1292
- selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
1293
- }
1294
- return true;
1295
- };
1296
-
1297
- var insertContent$1 = /*#__PURE__*/Object.freeze({
1298
- __proto__: null,
1299
- insertContent: insertContent
1300
- });
1301
-
1302
- const insertHTML = value => ({ tr, state, dispatch }) => {
1303
- console.warn('[tiptap warn]: insertHTML() is deprecated. please use insertContent() instead.');
1304
- const { selection } = tr;
1305
- const element = elementFromString(value);
1306
- const slice = DOMParser.fromSchema(state.schema).parseSlice(element);
1307
- if (dispatch) {
1308
- tr.insert(selection.anchor, slice.content);
1309
- selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
1310
- }
1311
- return true;
1312
- };
1313
-
1314
- var insertHTML$1 = /*#__PURE__*/Object.freeze({
1315
- __proto__: null,
1316
- insertHTML: insertHTML
1317
- });
1318
-
1319
- const insertNode = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1320
- console.warn('[tiptap warn]: insertNode() is deprecated. please use insertContent() instead.');
1321
- const { selection } = tr;
1322
- const type = getNodeType(typeOrName, state.schema);
1323
- if (!type) {
1324
- return false;
1325
- }
1326
- const node = type.create(attributes);
1327
- if (dispatch) {
1328
- tr.insert(selection.anchor, node);
1329
- }
1330
- return true;
1331
- };
1332
-
1333
- var insertNode$1 = /*#__PURE__*/Object.freeze({
1334
- __proto__: null,
1335
- insertNode: insertNode
1336
- });
1337
-
1338
- const insertText = value => ({ tr, dispatch }) => {
1339
- console.warn('[tiptap warn]: insertText() is deprecated. please use insertContent() instead.');
1340
- if (dispatch) {
1341
- tr.insertText(value);
1342
1678
  }
1343
1679
  return true;
1344
1680
  };
1345
1681
 
1346
- var insertText$1 = /*#__PURE__*/Object.freeze({
1347
- __proto__: null,
1348
- insertText: insertText
1349
- });
1350
-
1351
1682
  const joinBackward = () => ({ state, dispatch }) => {
1352
- return joinBackward$2(state, dispatch);
1683
+ return joinBackward$1(state, dispatch);
1353
1684
  };
1354
1685
 
1355
- var joinBackward$1 = /*#__PURE__*/Object.freeze({
1356
- __proto__: null,
1357
- joinBackward: joinBackward
1358
- });
1359
-
1360
1686
  const joinForward = () => ({ state, dispatch }) => {
1361
- return joinForward$2(state, dispatch);
1687
+ return joinForward$1(state, dispatch);
1362
1688
  };
1363
1689
 
1364
- var joinForward$1 = /*#__PURE__*/Object.freeze({
1365
- __proto__: null,
1366
- joinForward: joinForward
1367
- });
1690
+ function isMacOS() {
1691
+ return typeof navigator !== 'undefined'
1692
+ ? /Mac/.test(navigator.platform)
1693
+ : false;
1694
+ }
1368
1695
 
1369
- const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
1370
1696
  function normalizeKeyName(name) {
1371
1697
  const parts = name.split(/-(?!$)/);
1372
1698
  let result = parts[parts.length - 1];
@@ -1392,7 +1718,7 @@ function normalizeKeyName(name) {
1392
1718
  shift = true;
1393
1719
  }
1394
1720
  else if (/^mod$/i.test(mod)) {
1395
- if (mac) {
1721
+ if (isiOS() || isMacOS()) {
1396
1722
  meta = true;
1397
1723
  }
1398
1724
  else {
@@ -1443,10 +1769,40 @@ const keyboardShortcut = name => ({ editor, view, tr, dispatch, }) => {
1443
1769
  return true;
1444
1770
  };
1445
1771
 
1446
- var keyboardShortcut$1 = /*#__PURE__*/Object.freeze({
1447
- __proto__: null,
1448
- keyboardShortcut: keyboardShortcut
1449
- });
1772
+ function isNodeActive(state, typeOrName, attributes = {}) {
1773
+ const { from, to, empty } = state.selection;
1774
+ const type = typeOrName
1775
+ ? getNodeType(typeOrName, state.schema)
1776
+ : null;
1777
+ const nodeRanges = [];
1778
+ state.doc.nodesBetween(from, to, (node, pos) => {
1779
+ if (node.isText) {
1780
+ return;
1781
+ }
1782
+ const relativeFrom = Math.max(from, pos);
1783
+ const relativeTo = Math.min(to, pos + node.nodeSize);
1784
+ nodeRanges.push({
1785
+ node,
1786
+ from: relativeFrom,
1787
+ to: relativeTo,
1788
+ });
1789
+ });
1790
+ const selectionRange = to - from;
1791
+ const matchedNodeRanges = nodeRanges
1792
+ .filter(nodeRange => {
1793
+ if (!type) {
1794
+ return true;
1795
+ }
1796
+ return type.name === nodeRange.node.type.name;
1797
+ })
1798
+ .filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
1799
+ if (empty) {
1800
+ return !!matchedNodeRanges.length;
1801
+ }
1802
+ const range = matchedNodeRanges
1803
+ .reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
1804
+ return range >= selectionRange;
1805
+ }
1450
1806
 
1451
1807
  const lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
1452
1808
  const type = getNodeType(typeOrName, state.schema);
@@ -1454,71 +1810,31 @@ const lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
1454
1810
  if (!isActive) {
1455
1811
  return false;
1456
1812
  }
1457
- return lift$2(state, dispatch);
1813
+ return lift$1(state, dispatch);
1458
1814
  };
1459
1815
 
1460
- var lift$1 = /*#__PURE__*/Object.freeze({
1461
- __proto__: null,
1462
- lift: lift
1463
- });
1464
-
1465
1816
  const liftEmptyBlock = () => ({ state, dispatch }) => {
1466
- return liftEmptyBlock$2(state, dispatch);
1817
+ return liftEmptyBlock$1(state, dispatch);
1467
1818
  };
1468
1819
 
1469
- var liftEmptyBlock$1 = /*#__PURE__*/Object.freeze({
1470
- __proto__: null,
1471
- liftEmptyBlock: liftEmptyBlock
1472
- });
1473
-
1474
1820
  const liftListItem = typeOrName => ({ state, dispatch }) => {
1475
1821
  const type = getNodeType(typeOrName, state.schema);
1476
- return liftListItem$2(type)(state, dispatch);
1822
+ return liftListItem$1(type)(state, dispatch);
1477
1823
  };
1478
1824
 
1479
- var liftListItem$1 = /*#__PURE__*/Object.freeze({
1480
- __proto__: null,
1481
- liftListItem: liftListItem
1482
- });
1483
-
1484
1825
  const newlineInCode = () => ({ state, dispatch }) => {
1485
- return newlineInCode$2(state, dispatch);
1826
+ return newlineInCode$1(state, dispatch);
1486
1827
  };
1487
1828
 
1488
- var newlineInCode$1 = /*#__PURE__*/Object.freeze({
1489
- __proto__: null,
1490
- newlineInCode: newlineInCode
1491
- });
1492
-
1493
- const replace = (typeOrName, attributes = {}) => ({ state, commands }) => {
1494
- const { from, to } = state.selection;
1495
- const range = { from, to };
1496
- return commands.replaceRange(range, typeOrName, attributes);
1497
- };
1498
-
1499
- var replace$1 = /*#__PURE__*/Object.freeze({
1500
- __proto__: null,
1501
- replace: replace
1502
- });
1503
-
1504
- const replaceRange = (range, typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1505
- const type = getNodeType(typeOrName, state.schema);
1506
- const { from, to } = range;
1507
- const $from = tr.doc.resolve(from);
1508
- const index = $from.index();
1509
- if (!$from.parent.canReplaceWith(index, index, type)) {
1510
- return false;
1829
+ function getSchemaTypeNameByName(name, schema) {
1830
+ if (schema.nodes[name]) {
1831
+ return 'node';
1511
1832
  }
1512
- if (dispatch) {
1513
- tr.replaceWith(from, to, type.create(attributes));
1833
+ if (schema.marks[name]) {
1834
+ return 'mark';
1514
1835
  }
1515
- return true;
1516
- };
1517
-
1518
- var replaceRange$1 = /*#__PURE__*/Object.freeze({
1519
- __proto__: null,
1520
- replaceRange: replaceRange
1521
- });
1836
+ return null;
1837
+ }
1522
1838
 
1523
1839
  /**
1524
1840
  * Remove a property or an array of properties from an object
@@ -1539,25 +1855,40 @@ function deleteProps(obj, propOrProps) {
1539
1855
  }, {});
1540
1856
  }
1541
1857
 
1542
- const resetNodeAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
1543
- const type = getNodeType(typeOrName, state.schema);
1544
- const { selection } = tr;
1545
- const { ranges } = selection;
1546
- ranges.forEach(range => {
1547
- state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
1548
- if (node.type === type && dispatch) {
1549
- tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes));
1550
- }
1858
+ const resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
1859
+ let nodeType = null;
1860
+ let markType = null;
1861
+ const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
1862
+ ? typeOrName
1863
+ : typeOrName.name, state.schema);
1864
+ if (!schemaType) {
1865
+ return false;
1866
+ }
1867
+ if (schemaType === 'node') {
1868
+ nodeType = getNodeType(typeOrName, state.schema);
1869
+ }
1870
+ if (schemaType === 'mark') {
1871
+ markType = getMarkType(typeOrName, state.schema);
1872
+ }
1873
+ if (dispatch) {
1874
+ tr.selection.ranges.forEach(range => {
1875
+ state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
1876
+ if (nodeType && nodeType === node.type) {
1877
+ tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes));
1878
+ }
1879
+ if (markType && node.marks.length) {
1880
+ node.marks.forEach(mark => {
1881
+ if (markType === mark.type) {
1882
+ tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
1883
+ }
1884
+ });
1885
+ }
1886
+ });
1551
1887
  });
1552
- });
1888
+ }
1553
1889
  return true;
1554
1890
  };
1555
1891
 
1556
- var resetNodeAttributes$1 = /*#__PURE__*/Object.freeze({
1557
- __proto__: null,
1558
- resetNodeAttributes: resetNodeAttributes
1559
- });
1560
-
1561
1892
  const scrollIntoView = () => ({ tr, dispatch }) => {
1562
1893
  if (dispatch) {
1563
1894
  tr.scrollIntoView();
@@ -1565,46 +1896,38 @@ const scrollIntoView = () => ({ tr, dispatch }) => {
1565
1896
  return true;
1566
1897
  };
1567
1898
 
1568
- var scrollIntoView$1 = /*#__PURE__*/Object.freeze({
1569
- __proto__: null,
1570
- scrollIntoView: scrollIntoView
1571
- });
1572
-
1573
- const selectAll = () => ({ state, dispatch }) => {
1574
- return selectAll$2(state, dispatch);
1899
+ const selectAll = () => ({ tr, commands }) => {
1900
+ return commands.setTextSelection({
1901
+ from: 0,
1902
+ to: tr.doc.content.size,
1903
+ });
1575
1904
  };
1576
1905
 
1577
- var selectAll$1 = /*#__PURE__*/Object.freeze({
1578
- __proto__: null,
1579
- selectAll: selectAll
1580
- });
1581
-
1582
1906
  const selectNodeBackward = () => ({ state, dispatch }) => {
1583
- return selectNodeBackward$2(state, dispatch);
1907
+ return selectNodeBackward$1(state, dispatch);
1584
1908
  };
1585
1909
 
1586
- var selectNodeBackward$1 = /*#__PURE__*/Object.freeze({
1587
- __proto__: null,
1588
- selectNodeBackward: selectNodeBackward
1589
- });
1590
-
1591
1910
  const selectNodeForward = () => ({ state, dispatch }) => {
1592
- return selectNodeForward$2(state, dispatch);
1911
+ return selectNodeForward$1(state, dispatch);
1593
1912
  };
1594
1913
 
1595
- var selectNodeForward$1 = /*#__PURE__*/Object.freeze({
1596
- __proto__: null,
1597
- selectNodeForward: selectNodeForward
1598
- });
1599
-
1600
1914
  const selectParentNode = () => ({ state, dispatch }) => {
1601
- return selectParentNode$2(state, dispatch);
1915
+ return selectParentNode$1(state, dispatch);
1602
1916
  };
1603
1917
 
1604
- var selectParentNode$1 = /*#__PURE__*/Object.freeze({
1605
- __proto__: null,
1606
- selectParentNode: selectParentNode
1607
- });
1918
+ // @ts-ignore
1919
+ const selectTextblockEnd = () => ({ state, dispatch }) => {
1920
+ return selectTextblockEnd$1(state, dispatch);
1921
+ };
1922
+
1923
+ // @ts-ignore
1924
+ const selectTextblockStart = () => ({ state, dispatch }) => {
1925
+ return selectTextblockStart$1(state, dispatch);
1926
+ };
1927
+
1928
+ function createDocument(content, schema, parseOptions = {}) {
1929
+ return createNodeFromContent(content, schema, { slice: false, parseOptions });
1930
+ }
1608
1931
 
1609
1932
  const setContent = (content, emitUpdate = false, parseOptions = {}) => ({ tr, editor, dispatch }) => {
1610
1933
  const { doc } = tr;
@@ -1618,58 +1941,131 @@ const setContent = (content, emitUpdate = false, parseOptions = {}) => ({ tr, ed
1618
1941
  return true;
1619
1942
  };
1620
1943
 
1621
- var setContent$1 = /*#__PURE__*/Object.freeze({
1622
- __proto__: null,
1623
- setContent: setContent
1624
- });
1944
+ function getMarkAttributes(state, typeOrName) {
1945
+ const type = getMarkType(typeOrName, state.schema);
1946
+ const { from, to, empty } = state.selection;
1947
+ const marks = [];
1948
+ if (empty) {
1949
+ if (state.storedMarks) {
1950
+ marks.push(...state.storedMarks);
1951
+ }
1952
+ marks.push(...state.selection.$head.marks());
1953
+ }
1954
+ else {
1955
+ state.doc.nodesBetween(from, to, node => {
1956
+ marks.push(...node.marks);
1957
+ });
1958
+ }
1959
+ const mark = marks.find(markItem => markItem.type.name === type.name);
1960
+ if (!mark) {
1961
+ return {};
1962
+ }
1963
+ return { ...mark.attrs };
1964
+ }
1625
1965
 
1626
1966
  const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1627
1967
  const { selection } = tr;
1628
1968
  const { empty, ranges } = selection;
1629
1969
  const type = getMarkType(typeOrName, state.schema);
1630
- const oldAttributes = getMarkAttributes(state, type);
1631
- const newAttributes = {
1632
- ...oldAttributes,
1633
- ...attributes,
1634
- };
1635
1970
  if (dispatch) {
1636
1971
  if (empty) {
1637
- tr.addStoredMark(type.create(newAttributes));
1972
+ const oldAttributes = getMarkAttributes(state, type);
1973
+ tr.addStoredMark(type.create({
1974
+ ...oldAttributes,
1975
+ ...attributes,
1976
+ }));
1638
1977
  }
1639
1978
  else {
1640
1979
  ranges.forEach(range => {
1641
- tr.addMark(range.$from.pos, range.$to.pos, type.create(newAttributes));
1980
+ const from = range.$from.pos;
1981
+ const to = range.$to.pos;
1982
+ state.doc.nodesBetween(from, to, (node, pos) => {
1983
+ const trimmedFrom = Math.max(pos, from);
1984
+ const trimmedTo = Math.min(pos + node.nodeSize, to);
1985
+ const someHasMark = node.marks.find(mark => mark.type === type);
1986
+ // if there is already a mark of this type
1987
+ // we know that we have to merge its attributes
1988
+ // otherwise we add a fresh new mark
1989
+ if (someHasMark) {
1990
+ node.marks.forEach(mark => {
1991
+ if (type === mark.type) {
1992
+ tr.addMark(trimmedFrom, trimmedTo, type.create({
1993
+ ...mark.attrs,
1994
+ ...attributes,
1995
+ }));
1996
+ }
1997
+ });
1998
+ }
1999
+ else {
2000
+ tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
2001
+ }
2002
+ });
1642
2003
  });
1643
2004
  }
1644
2005
  }
1645
2006
  return true;
1646
2007
  };
1647
2008
 
1648
- var setMark$1 = /*#__PURE__*/Object.freeze({
1649
- __proto__: null,
1650
- setMark: setMark
1651
- });
2009
+ const setMeta = (key, value) => ({ tr }) => {
2010
+ tr.setMeta(key, value);
2011
+ return true;
2012
+ };
1652
2013
 
1653
- const setNode = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
2014
+ const setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
1654
2015
  const type = getNodeType(typeOrName, state.schema);
1655
- return setBlockType(type, attributes)(state, dispatch);
2016
+ // TODO: use a fallback like insertContent?
2017
+ if (!type.isTextblock) {
2018
+ console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
2019
+ return false;
2020
+ }
2021
+ return chain()
2022
+ // try to convert node to default node if needed
2023
+ .command(({ commands }) => {
2024
+ const canSetBlock = setBlockType(type, attributes)(state);
2025
+ if (canSetBlock) {
2026
+ return true;
2027
+ }
2028
+ return commands.clearNodes();
2029
+ })
2030
+ .command(({ state: updatedState }) => {
2031
+ return setBlockType(type, attributes)(updatedState, dispatch);
2032
+ })
2033
+ .run();
1656
2034
  };
1657
2035
 
1658
- var setNode$1 = /*#__PURE__*/Object.freeze({
1659
- __proto__: null,
1660
- setNode: setNode
1661
- });
2036
+ const setNodeSelection = position => ({ tr, dispatch }) => {
2037
+ if (dispatch) {
2038
+ const { doc } = tr;
2039
+ const minPos = Selection.atStart(doc).from;
2040
+ const maxPos = Selection.atEnd(doc).to;
2041
+ const resolvedPos = minMax(position, minPos, maxPos);
2042
+ const selection = NodeSelection.create(doc, resolvedPos);
2043
+ tr.setSelection(selection);
2044
+ }
2045
+ return true;
2046
+ };
2047
+
2048
+ const setTextSelection = position => ({ tr, dispatch }) => {
2049
+ if (dispatch) {
2050
+ const { doc } = tr;
2051
+ const { from, to } = typeof position === 'number'
2052
+ ? { from: position, to: position }
2053
+ : position;
2054
+ const minPos = TextSelection.atStart(doc).from;
2055
+ const maxPos = TextSelection.atEnd(doc).to;
2056
+ const resolvedFrom = minMax(from, minPos, maxPos);
2057
+ const resolvedEnd = minMax(to, minPos, maxPos);
2058
+ const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
2059
+ tr.setSelection(selection);
2060
+ }
2061
+ return true;
2062
+ };
1662
2063
 
1663
2064
  const sinkListItem = typeOrName => ({ state, dispatch }) => {
1664
2065
  const type = getNodeType(typeOrName, state.schema);
1665
- return sinkListItem$2(type)(state, dispatch);
2066
+ return sinkListItem$1(type)(state, dispatch);
1666
2067
  };
1667
2068
 
1668
- var sinkListItem$1 = /*#__PURE__*/Object.freeze({
1669
- __proto__: null,
1670
- sinkListItem: sinkListItem
1671
- });
1672
-
1673
2069
  function getSplittedAttributes(extensionAttributes, typeName, attributes) {
1674
2070
  return Object.fromEntries(Object
1675
2071
  .entries(attributes)
@@ -1684,8 +2080,8 @@ function getSplittedAttributes(extensionAttributes, typeName, attributes) {
1684
2080
  }));
1685
2081
  }
1686
2082
 
1687
- function defaultBlockAt(match) {
1688
- for (let i = 0; i < match.edgeCount; i + 1) {
2083
+ function defaultBlockAt$1(match) {
2084
+ for (let i = 0; i < match.edgeCount; i += 1) {
1689
2085
  const { type } = match.edge(i);
1690
2086
  if (type.isTextblock && !type.hasRequiredAttrs()) {
1691
2087
  return type;
@@ -1728,7 +2124,7 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
1728
2124
  }
1729
2125
  const deflt = $from.depth === 0
1730
2126
  ? undefined
1731
- : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
2127
+ : defaultBlockAt$1($from.node(-1).contentMatchAt($from.indexAfter(-1)));
1732
2128
  let types = atEnd && deflt
1733
2129
  ? [{
1734
2130
  type: deflt,
@@ -1749,11 +2145,15 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
1749
2145
  }
1750
2146
  if (can) {
1751
2147
  tr.split(tr.mapping.map($from.pos), 1, types);
1752
- if (!atEnd
2148
+ if (deflt
2149
+ && !atEnd
1753
2150
  && !$from.parentOffset
1754
- && $from.parent.type !== deflt
1755
- && $from.node(-1).canReplace($from.index(-1), $from.indexAfter(-1), Fragment.from(deflt === null || deflt === void 0 ? void 0 : deflt.create()))) {
1756
- tr.setNodeMarkup(tr.mapping.map($from.before()), deflt || undefined);
2151
+ && $from.parent.type !== deflt) {
2152
+ const first = tr.mapping.map($from.before());
2153
+ const $first = tr.doc.resolve(first);
2154
+ if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
2155
+ tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
2156
+ }
1757
2157
  }
1758
2158
  }
1759
2159
  if (keepMarks) {
@@ -1764,11 +2164,6 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
1764
2164
  return true;
1765
2165
  };
1766
2166
 
1767
- var splitBlock$1 = /*#__PURE__*/Object.freeze({
1768
- __proto__: null,
1769
- splitBlock: splitBlock
1770
- });
1771
-
1772
2167
  const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
1773
2168
  var _a;
1774
2169
  const type = getNodeType(typeOrName, state.schema);
@@ -1795,20 +2190,42 @@ const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
1795
2190
  }
1796
2191
  if (dispatch) {
1797
2192
  let wrap = Fragment.empty;
1798
- const keepItem = $from.index(-1) > 0;
2193
+ // eslint-disable-next-line
2194
+ const depthBefore = $from.index(-1)
2195
+ ? 1
2196
+ : $from.index(-2)
2197
+ ? 2
2198
+ : 3;
1799
2199
  // Build a fragment containing empty versions of the structure
1800
2200
  // from the outer list item to the parent node of the cursor
1801
- for (let d = $from.depth - (keepItem ? 1 : 2); d >= $from.depth - 3; d -= 1) {
2201
+ for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
1802
2202
  wrap = Fragment.from($from.node(d).copy(wrap));
1803
2203
  }
2204
+ // eslint-disable-next-line
2205
+ const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount
2206
+ ? 1
2207
+ : $from.indexAfter(-2) < $from.node(-3).childCount
2208
+ ? 2
2209
+ : 3;
1804
2210
  // Add a second list item with an empty default start node
1805
2211
  const newNextTypeAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
1806
2212
  const nextType = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes)) || undefined;
1807
2213
  wrap = wrap.append(Fragment.from(type.createAndFill(null, nextType) || undefined));
1808
- tr
1809
- .replace($from.before(keepItem ? undefined : -1), $from.after(-3), new Slice(wrap, keepItem ? 3 : 2, 2))
1810
- .setSelection(TextSelection.near(tr.doc.resolve($from.pos + (keepItem ? 3 : 2))))
1811
- .scrollIntoView();
2214
+ const start = $from.before($from.depth - (depthBefore - 1));
2215
+ tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
2216
+ let sel = -1;
2217
+ tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
2218
+ if (sel > -1) {
2219
+ return false;
2220
+ }
2221
+ if (n.isTextblock && n.content.size === 0) {
2222
+ sel = pos + 1;
2223
+ }
2224
+ });
2225
+ if (sel > -1) {
2226
+ tr.setSelection(TextSelection.near(tr.doc.resolve(sel)));
2227
+ }
2228
+ tr.scrollIntoView();
1812
2229
  }
1813
2230
  return true;
1814
2231
  }
@@ -1830,11 +2247,6 @@ const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
1830
2247
  return true;
1831
2248
  };
1832
2249
 
1833
- var splitListItem$1 = /*#__PURE__*/Object.freeze({
1834
- __proto__: null,
1835
- splitListItem: splitListItem
1836
- });
1837
-
1838
2250
  function findParentNodeClosestToPos($pos, predicate) {
1839
2251
  for (let i = $pos.depth; i > 0; i -= 1) {
1840
2252
  const node = $pos.node(i);
@@ -1855,19 +2267,60 @@ function findParentNode(predicate) {
1855
2267
 
1856
2268
  function isList(name, extensions) {
1857
2269
  const { nodeExtensions } = splitExtensions(extensions);
1858
- const extension = nodeExtensions.find(item => item.config.name === name);
2270
+ const extension = nodeExtensions.find(item => item.name === name);
1859
2271
  if (!extension) {
1860
2272
  return false;
1861
2273
  }
1862
- const groups = callOrReturn(extension.config.group, { options: extension.options });
1863
- if (typeof groups !== 'string') {
2274
+ const context = {
2275
+ name: extension.name,
2276
+ options: extension.options,
2277
+ storage: extension.storage,
2278
+ };
2279
+ const group = callOrReturn(getExtensionField(extension, 'group', context));
2280
+ if (typeof group !== 'string') {
1864
2281
  return false;
1865
2282
  }
1866
- return groups.split(' ').includes('list');
2283
+ return group.split(' ').includes('list');
1867
2284
  }
1868
2285
 
2286
+ const joinListBackwards = (tr, listType) => {
2287
+ const list = findParentNode(node => node.type === listType)(tr.selection);
2288
+ if (!list) {
2289
+ return true;
2290
+ }
2291
+ const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
2292
+ if (before === undefined) {
2293
+ return true;
2294
+ }
2295
+ const nodeBefore = tr.doc.nodeAt(before);
2296
+ const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type)
2297
+ && canJoin(tr.doc, list.pos);
2298
+ if (!canJoinBackwards) {
2299
+ return true;
2300
+ }
2301
+ tr.join(list.pos);
2302
+ return true;
2303
+ };
2304
+ const joinListForwards = (tr, listType) => {
2305
+ const list = findParentNode(node => node.type === listType)(tr.selection);
2306
+ if (!list) {
2307
+ return true;
2308
+ }
2309
+ const after = tr.doc.resolve(list.start).after(list.depth);
2310
+ if (after === undefined) {
2311
+ return true;
2312
+ }
2313
+ const nodeAfter = tr.doc.nodeAt(after);
2314
+ const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type)
2315
+ && canJoin(tr.doc, after);
2316
+ if (!canJoinForwards) {
2317
+ return true;
2318
+ }
2319
+ tr.join(after);
2320
+ return true;
2321
+ };
1869
2322
  const toggleList = (listTypeOrName, itemTypeOrName) => ({ editor, tr, state, dispatch, chain, commands, can, }) => {
1870
- const { extensions } = editor.options;
2323
+ const { extensions } = editor.extensionManager;
1871
2324
  const listType = getNodeType(listTypeOrName, state.schema);
1872
2325
  const itemType = getNodeType(itemTypeOrName, state.schema);
1873
2326
  const { selection } = state;
@@ -1886,40 +2339,108 @@ const toggleList = (listTypeOrName, itemTypeOrName) => ({ editor, tr, state, dis
1886
2339
  if (isList(parentList.node.type.name, extensions)
1887
2340
  && listType.validContent(parentList.node.content)
1888
2341
  && dispatch) {
1889
- tr.setNodeMarkup(parentList.pos, listType);
1890
- return true;
2342
+ return chain()
2343
+ .command(() => {
2344
+ tr.setNodeMarkup(parentList.pos, listType);
2345
+ return true;
2346
+ })
2347
+ .command(() => joinListBackwards(tr, listType))
2348
+ .command(() => joinListForwards(tr, listType))
2349
+ .run();
1891
2350
  }
1892
2351
  }
1893
- const canWrapInList = can().wrapInList(listType);
1894
- // try to convert node to paragraph if needed
1895
- if (!canWrapInList) {
1896
- return chain()
1897
- .clearNodes()
1898
- .wrapInList(listType)
1899
- .run();
1900
- }
1901
- return commands.wrapInList(listType);
2352
+ return chain()
2353
+ // try to convert node to default node if needed
2354
+ .command(() => {
2355
+ const canWrapInList = can().wrapInList(listType);
2356
+ if (canWrapInList) {
2357
+ return true;
2358
+ }
2359
+ return commands.clearNodes();
2360
+ })
2361
+ .wrapInList(listType)
2362
+ .command(() => joinListBackwards(tr, listType))
2363
+ .command(() => joinListForwards(tr, listType))
2364
+ .run();
1902
2365
  };
1903
2366
 
1904
- var toggleList$1 = /*#__PURE__*/Object.freeze({
1905
- __proto__: null,
1906
- toggleList: toggleList
1907
- });
2367
+ function isMarkActive(state, typeOrName, attributes = {}) {
2368
+ const { empty, ranges } = state.selection;
2369
+ const type = typeOrName
2370
+ ? getMarkType(typeOrName, state.schema)
2371
+ : null;
2372
+ if (empty) {
2373
+ return !!(state.storedMarks || state.selection.$from.marks())
2374
+ .filter(mark => {
2375
+ if (!type) {
2376
+ return true;
2377
+ }
2378
+ return type.name === mark.type.name;
2379
+ })
2380
+ .find(mark => objectIncludes(mark.attrs, attributes, { strict: false }));
2381
+ }
2382
+ let selectionRange = 0;
2383
+ const markRanges = [];
2384
+ ranges.forEach(({ $from, $to }) => {
2385
+ const from = $from.pos;
2386
+ const to = $to.pos;
2387
+ state.doc.nodesBetween(from, to, (node, pos) => {
2388
+ if (!node.isText && !node.marks.length) {
2389
+ return;
2390
+ }
2391
+ const relativeFrom = Math.max(from, pos);
2392
+ const relativeTo = Math.min(to, pos + node.nodeSize);
2393
+ const range = relativeTo - relativeFrom;
2394
+ selectionRange += range;
2395
+ markRanges.push(...node.marks.map(mark => ({
2396
+ mark,
2397
+ from: relativeFrom,
2398
+ to: relativeTo,
2399
+ })));
2400
+ });
2401
+ });
2402
+ if (selectionRange === 0) {
2403
+ return false;
2404
+ }
2405
+ // calculate range of matched mark
2406
+ const matchedRange = markRanges
2407
+ .filter(markRange => {
2408
+ if (!type) {
2409
+ return true;
2410
+ }
2411
+ return type.name === markRange.mark.type.name;
2412
+ })
2413
+ .filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
2414
+ .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
2415
+ // calculate range of marks that excludes the searched mark
2416
+ // for example `code` doesn’t allow any other marks
2417
+ const excludedRange = markRanges
2418
+ .filter(markRange => {
2419
+ if (!type) {
2420
+ return true;
2421
+ }
2422
+ return markRange.mark.type !== type
2423
+ && markRange.mark.type.excludes(type);
2424
+ })
2425
+ .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
2426
+ // we only include the result of `excludedRange`
2427
+ // if there is a match at all
2428
+ const range = matchedRange > 0
2429
+ ? matchedRange + excludedRange
2430
+ : matchedRange;
2431
+ return range >= selectionRange;
2432
+ }
1908
2433
 
1909
- const toggleMark = (typeOrName, attributes = {}) => ({ state, commands }) => {
2434
+ const toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands }) => {
2435
+ const { extendEmptyMarkRange = false } = options;
1910
2436
  const type = getMarkType(typeOrName, state.schema);
1911
2437
  const isActive = isMarkActive(state, type, attributes);
1912
2438
  if (isActive) {
1913
- return commands.unsetMark(type);
2439
+ return commands.unsetMark(type, { extendEmptyMarkRange });
1914
2440
  }
1915
2441
  return commands.setMark(type, attributes);
1916
2442
  };
1917
2443
 
1918
- var toggleMark$1 = /*#__PURE__*/Object.freeze({
1919
- __proto__: null,
1920
- toggleMark: toggleMark
1921
- });
1922
-
1923
2444
  const toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands }) => {
1924
2445
  const type = getNodeType(typeOrName, state.schema);
1925
2446
  const toggleType = getNodeType(toggleTypeOrName, state.schema);
@@ -1930,129 +2451,190 @@ const toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state,
1930
2451
  return commands.setNode(type, attributes);
1931
2452
  };
1932
2453
 
1933
- var toggleNode$1 = /*#__PURE__*/Object.freeze({
1934
- __proto__: null,
1935
- toggleNode: toggleNode
1936
- });
1937
-
1938
- const toggleWrap = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
2454
+ const toggleWrap = (typeOrName, attributes = {}) => ({ state, commands }) => {
1939
2455
  const type = getNodeType(typeOrName, state.schema);
1940
2456
  const isActive = isNodeActive(state, type, attributes);
1941
2457
  if (isActive) {
1942
- return lift$2(state, dispatch);
2458
+ return commands.lift(type);
1943
2459
  }
1944
- return wrapIn$2(type, attributes)(state, dispatch);
2460
+ return commands.wrapIn(type, attributes);
1945
2461
  };
1946
2462
 
1947
- var toggleWrap$1 = /*#__PURE__*/Object.freeze({
1948
- __proto__: null,
1949
- toggleWrap: toggleWrap
1950
- });
1951
-
1952
2463
  const undoInputRule = () => ({ state, dispatch }) => {
1953
- return undoInputRule$2(state, dispatch);
2464
+ const plugins = state.plugins;
2465
+ for (let i = 0; i < plugins.length; i += 1) {
2466
+ const plugin = plugins[i];
2467
+ let undoable;
2468
+ // @ts-ignore
2469
+ // eslint-disable-next-line
2470
+ if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
2471
+ if (dispatch) {
2472
+ const tr = state.tr;
2473
+ const toUndo = undoable.transform;
2474
+ for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
2475
+ tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
2476
+ }
2477
+ if (undoable.text) {
2478
+ const marks = tr.doc.resolve(undoable.from).marks();
2479
+ tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
2480
+ }
2481
+ else {
2482
+ tr.delete(undoable.from, undoable.to);
2483
+ }
2484
+ }
2485
+ return true;
2486
+ }
2487
+ }
2488
+ return false;
1954
2489
  };
1955
2490
 
1956
- var undoInputRule$1 = /*#__PURE__*/Object.freeze({
1957
- __proto__: null,
1958
- undoInputRule: undoInputRule
1959
- });
1960
-
1961
- const unsetAllMarks = () => ({ tr, state, dispatch }) => {
2491
+ const unsetAllMarks = () => ({ tr, dispatch }) => {
1962
2492
  const { selection } = tr;
1963
2493
  const { empty, ranges } = selection;
1964
2494
  if (empty) {
1965
2495
  return true;
1966
2496
  }
1967
2497
  if (dispatch) {
1968
- Object
1969
- .entries(state.schema.marks)
1970
- .forEach(([, mark]) => {
1971
- ranges.forEach(range => {
1972
- tr.removeMark(range.$from.pos, range.$to.pos, mark);
1973
- });
2498
+ ranges.forEach(range => {
2499
+ tr.removeMark(range.$from.pos, range.$to.pos);
1974
2500
  });
1975
2501
  }
1976
2502
  return true;
1977
2503
  };
1978
2504
 
1979
- var unsetAllMarks$1 = /*#__PURE__*/Object.freeze({
1980
- __proto__: null,
1981
- unsetAllMarks: unsetAllMarks
1982
- });
1983
-
1984
- const unsetMark = typeOrName => ({ tr, state, dispatch }) => {
2505
+ const unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
2506
+ var _a;
2507
+ const { extendEmptyMarkRange = false } = options;
1985
2508
  const { selection } = tr;
1986
2509
  const type = getMarkType(typeOrName, state.schema);
1987
2510
  const { $from, empty, ranges } = selection;
1988
- if (dispatch) {
1989
- if (empty) {
1990
- let { from, to } = selection;
1991
- const range = getMarkRange($from, type);
1992
- if (range) {
1993
- from = range.from;
1994
- to = range.to;
1995
- }
1996
- tr.removeMark(from, to, type);
1997
- }
1998
- else {
1999
- ranges.forEach(range => {
2000
- tr.removeMark(range.$from.pos, range.$to.pos, type);
2001
- });
2511
+ if (!dispatch) {
2512
+ return true;
2513
+ }
2514
+ if (empty && extendEmptyMarkRange) {
2515
+ let { from, to } = selection;
2516
+ const attrs = (_a = $from.marks().find(mark => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
2517
+ const range = getMarkRange($from, type, attrs);
2518
+ if (range) {
2519
+ from = range.from;
2520
+ to = range.to;
2002
2521
  }
2003
- tr.removeStoredMark(type);
2522
+ tr.removeMark(from, to, type);
2523
+ }
2524
+ else {
2525
+ ranges.forEach(range => {
2526
+ tr.removeMark(range.$from.pos, range.$to.pos, type);
2527
+ });
2004
2528
  }
2529
+ tr.removeStoredMark(type);
2005
2530
  return true;
2006
2531
  };
2007
2532
 
2008
- var unsetMark$1 = /*#__PURE__*/Object.freeze({
2009
- __proto__: null,
2010
- unsetMark: unsetMark
2011
- });
2012
-
2013
- const updateNodeAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
2014
- const type = getNodeType(typeOrName, state.schema);
2015
- const { selection } = tr;
2016
- const { ranges } = selection;
2017
- ranges.forEach(range => {
2018
- state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
2019
- if (node.type === type && dispatch) {
2020
- tr.setNodeMarkup(pos, undefined, {
2021
- ...node.attrs,
2022
- ...attributes,
2023
- });
2024
- }
2533
+ const updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
2534
+ let nodeType = null;
2535
+ let markType = null;
2536
+ const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
2537
+ ? typeOrName
2538
+ : typeOrName.name, state.schema);
2539
+ if (!schemaType) {
2540
+ return false;
2541
+ }
2542
+ if (schemaType === 'node') {
2543
+ nodeType = getNodeType(typeOrName, state.schema);
2544
+ }
2545
+ if (schemaType === 'mark') {
2546
+ markType = getMarkType(typeOrName, state.schema);
2547
+ }
2548
+ if (dispatch) {
2549
+ tr.selection.ranges.forEach(range => {
2550
+ const from = range.$from.pos;
2551
+ const to = range.$to.pos;
2552
+ state.doc.nodesBetween(from, to, (node, pos) => {
2553
+ if (nodeType && nodeType === node.type) {
2554
+ tr.setNodeMarkup(pos, undefined, {
2555
+ ...node.attrs,
2556
+ ...attributes,
2557
+ });
2558
+ }
2559
+ if (markType && node.marks.length) {
2560
+ node.marks.forEach(mark => {
2561
+ if (markType === mark.type) {
2562
+ const trimmedFrom = Math.max(pos, from);
2563
+ const trimmedTo = Math.min(pos + node.nodeSize, to);
2564
+ tr.addMark(trimmedFrom, trimmedTo, markType.create({
2565
+ ...mark.attrs,
2566
+ ...attributes,
2567
+ }));
2568
+ }
2569
+ });
2570
+ }
2571
+ });
2025
2572
  });
2026
- });
2573
+ }
2027
2574
  return true;
2028
2575
  };
2029
2576
 
2030
- var updateNodeAttributes$1 = /*#__PURE__*/Object.freeze({
2031
- __proto__: null,
2032
- updateNodeAttributes: updateNodeAttributes
2033
- });
2034
-
2035
2577
  const wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
2036
2578
  const type = getNodeType(typeOrName, state.schema);
2037
- const isActive = isNodeActive(state, type, attributes);
2038
- if (isActive) {
2039
- return false;
2040
- }
2041
- return wrapIn$2(type, attributes)(state, dispatch);
2579
+ return wrapIn$1(type, attributes)(state, dispatch);
2042
2580
  };
2043
2581
 
2044
- var wrapIn$1 = /*#__PURE__*/Object.freeze({
2045
- __proto__: null,
2046
- wrapIn: wrapIn
2047
- });
2048
-
2049
2582
  const wrapInList = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
2050
2583
  const type = getNodeType(typeOrName, state.schema);
2051
- return wrapInList$2(type, attributes)(state, dispatch);
2584
+ return wrapInList$1(type, attributes)(state, dispatch);
2052
2585
  };
2053
2586
 
2054
- var wrapInList$1 = /*#__PURE__*/Object.freeze({
2587
+ var commands = /*#__PURE__*/Object.freeze({
2055
2588
  __proto__: null,
2589
+ blur: blur,
2590
+ clearContent: clearContent,
2591
+ clearNodes: clearNodes,
2592
+ command: command,
2593
+ createParagraphNear: createParagraphNear,
2594
+ deleteNode: deleteNode,
2595
+ deleteRange: deleteRange,
2596
+ deleteSelection: deleteSelection,
2597
+ enter: enter,
2598
+ exitCode: exitCode,
2599
+ extendMarkRange: extendMarkRange,
2600
+ first: first,
2601
+ focus: focus,
2602
+ forEach: forEach,
2603
+ insertContent: insertContent,
2604
+ insertContentAt: insertContentAt,
2605
+ joinBackward: joinBackward,
2606
+ joinForward: joinForward,
2607
+ keyboardShortcut: keyboardShortcut,
2608
+ lift: lift,
2609
+ liftEmptyBlock: liftEmptyBlock,
2610
+ liftListItem: liftListItem,
2611
+ newlineInCode: newlineInCode,
2612
+ resetAttributes: resetAttributes,
2613
+ scrollIntoView: scrollIntoView,
2614
+ selectAll: selectAll,
2615
+ selectNodeBackward: selectNodeBackward,
2616
+ selectNodeForward: selectNodeForward,
2617
+ selectParentNode: selectParentNode,
2618
+ selectTextblockEnd: selectTextblockEnd,
2619
+ selectTextblockStart: selectTextblockStart,
2620
+ setContent: setContent,
2621
+ setMark: setMark,
2622
+ setMeta: setMeta,
2623
+ setNode: setNode,
2624
+ setNodeSelection: setNodeSelection,
2625
+ setTextSelection: setTextSelection,
2626
+ sinkListItem: sinkListItem,
2627
+ splitBlock: splitBlock,
2628
+ splitListItem: splitListItem,
2629
+ toggleList: toggleList,
2630
+ toggleMark: toggleMark,
2631
+ toggleNode: toggleNode,
2632
+ toggleWrap: toggleWrap,
2633
+ undoInputRule: undoInputRule,
2634
+ unsetAllMarks: unsetAllMarks,
2635
+ unsetMark: unsetMark,
2636
+ updateAttributes: updateAttributes,
2637
+ wrapIn: wrapIn,
2056
2638
  wrapInList: wrapInList
2057
2639
  });
2058
2640
 
@@ -2060,53 +2642,7 @@ const Commands = Extension.create({
2060
2642
  name: 'commands',
2061
2643
  addCommands() {
2062
2644
  return {
2063
- ...blur$1,
2064
- ...clearContent$1,
2065
- ...clearNodes$1,
2066
- ...command$1,
2067
- ...createParagraphNear$1,
2068
- ...deleteRange$1,
2069
- ...deleteSelection$1,
2070
- ...enter$1,
2071
- ...exitCode$1,
2072
- ...extendMarkRange$1,
2073
- ...first$1,
2074
- ...focus$1,
2075
- ...insertContent$1,
2076
- ...insertHTML$1,
2077
- ...insertNode$1,
2078
- ...insertText$1,
2079
- ...joinBackward$1,
2080
- ...joinForward$1,
2081
- ...keyboardShortcut$1,
2082
- ...lift$1,
2083
- ...liftEmptyBlock$1,
2084
- ...liftListItem$1,
2085
- ...newlineInCode$1,
2086
- ...replace$1,
2087
- ...replaceRange$1,
2088
- ...resetNodeAttributes$1,
2089
- ...scrollIntoView$1,
2090
- ...selectAll$1,
2091
- ...selectNodeBackward$1,
2092
- ...selectNodeForward$1,
2093
- ...selectParentNode$1,
2094
- ...setContent$1,
2095
- ...setMark$1,
2096
- ...setNode$1,
2097
- ...sinkListItem$1,
2098
- ...splitBlock$1,
2099
- ...splitListItem$1,
2100
- ...toggleList$1,
2101
- ...toggleMark$1,
2102
- ...toggleNode$1,
2103
- ...toggleWrap$1,
2104
- ...undoInputRule$1,
2105
- ...unsetAllMarks$1,
2106
- ...unsetMark$1,
2107
- ...updateNodeAttributes$1,
2108
- ...wrapIn$1,
2109
- ...wrapInList$1,
2645
+ ...commands,
2110
2646
  };
2111
2647
  },
2112
2648
  });
@@ -2133,9 +2669,6 @@ const FocusEvents = Extension.create({
2133
2669
  new Plugin({
2134
2670
  key: new PluginKey('focusEvents'),
2135
2671
  props: {
2136
- attributes: {
2137
- tabindex: '0',
2138
- },
2139
2672
  handleDOMEvents: {
2140
2673
  focus: (view, event) => {
2141
2674
  editor.isFocused = true;
@@ -2165,6 +2698,20 @@ const Keymap = Extension.create({
2165
2698
  addKeyboardShortcuts() {
2166
2699
  const handleBackspace = () => this.editor.commands.first(({ commands }) => [
2167
2700
  () => commands.undoInputRule(),
2701
+ // maybe convert first text block node to default node
2702
+ () => commands.command(({ tr }) => {
2703
+ const { selection, doc } = tr;
2704
+ const { empty, $anchor } = selection;
2705
+ const { pos, parent } = $anchor;
2706
+ const isAtStart = Selection.atStart(doc).from === pos;
2707
+ if (!empty
2708
+ || !isAtStart
2709
+ || !parent.type.isTextblock
2710
+ || parent.textContent.length) {
2711
+ return false;
2712
+ }
2713
+ return commands.clearNodes();
2714
+ }),
2168
2715
  () => commands.deleteSelection(),
2169
2716
  () => commands.joinBackward(),
2170
2717
  () => commands.selectNodeBackward(),
@@ -2174,33 +2721,176 @@ const Keymap = Extension.create({
2174
2721
  () => commands.joinForward(),
2175
2722
  () => commands.selectNodeForward(),
2176
2723
  ]);
2177
- return {
2178
- Enter: () => this.editor.commands.first(({ commands }) => [
2179
- () => commands.newlineInCode(),
2180
- () => commands.createParagraphNear(),
2181
- () => commands.liftEmptyBlock(),
2182
- () => commands.splitBlock(),
2183
- ]),
2724
+ const handleEnter = () => this.editor.commands.first(({ commands }) => [
2725
+ () => commands.newlineInCode(),
2726
+ () => commands.createParagraphNear(),
2727
+ () => commands.liftEmptyBlock(),
2728
+ () => commands.splitBlock(),
2729
+ ]);
2730
+ const baseKeymap = {
2731
+ Enter: handleEnter,
2184
2732
  'Mod-Enter': () => this.editor.commands.exitCode(),
2185
- Backspace: () => handleBackspace(),
2186
- 'Mod-Backspace': () => handleBackspace(),
2187
- Delete: () => handleDelete(),
2188
- 'Mod-Delete': () => handleDelete(),
2189
- // we don’t need a custom `selectAll` for now
2190
- // 'Mod-a': () => this.editor.commands.selectAll(),
2733
+ Backspace: handleBackspace,
2734
+ 'Mod-Backspace': handleBackspace,
2735
+ 'Shift-Backspace': handleBackspace,
2736
+ Delete: handleDelete,
2737
+ 'Mod-Delete': handleDelete,
2738
+ 'Mod-a': () => this.editor.commands.selectAll(),
2191
2739
  };
2740
+ const pcKeymap = {
2741
+ ...baseKeymap,
2742
+ };
2743
+ const macKeymap = {
2744
+ ...baseKeymap,
2745
+ 'Ctrl-h': handleBackspace,
2746
+ 'Alt-Backspace': handleBackspace,
2747
+ 'Ctrl-d': handleDelete,
2748
+ 'Ctrl-Alt-Backspace': handleDelete,
2749
+ 'Alt-Delete': handleDelete,
2750
+ 'Alt-d': handleDelete,
2751
+ 'Ctrl-a': () => this.editor.commands.selectTextblockStart(),
2752
+ 'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),
2753
+ };
2754
+ if (isiOS() || isMacOS()) {
2755
+ return macKeymap;
2756
+ }
2757
+ return pcKeymap;
2192
2758
  },
2193
- });
2194
-
2195
- var extensions = /*#__PURE__*/Object.freeze({
2196
- __proto__: null,
2197
- ClipboardTextSerializer: ClipboardTextSerializer,
2759
+ addProseMirrorPlugins() {
2760
+ return [
2761
+ // With this plugin we check if the whole document was selected and deleted.
2762
+ // In this case we will additionally call `clearNodes()` to convert e.g. a heading
2763
+ // to a paragraph if necessary.
2764
+ // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
2765
+ // with many other commands.
2766
+ new Plugin({
2767
+ key: new PluginKey('clearDocument'),
2768
+ appendTransaction: (transactions, oldState, newState) => {
2769
+ const docChanges = transactions.some(transaction => transaction.docChanged)
2770
+ && !oldState.doc.eq(newState.doc);
2771
+ if (!docChanges) {
2772
+ return;
2773
+ }
2774
+ const { empty, from, to } = oldState.selection;
2775
+ const allFrom = Selection.atStart(oldState.doc).from;
2776
+ const allEnd = Selection.atEnd(oldState.doc).to;
2777
+ const allWasSelected = from === allFrom && to === allEnd;
2778
+ const isEmpty = newState.doc.textBetween(0, newState.doc.content.size, ' ', ' ').length === 0;
2779
+ if (empty || !allWasSelected || !isEmpty) {
2780
+ return;
2781
+ }
2782
+ const tr = newState.tr;
2783
+ const state = createChainableState({
2784
+ state: newState,
2785
+ transaction: tr,
2786
+ });
2787
+ const { commands } = new CommandManager({
2788
+ editor: this.editor,
2789
+ state,
2790
+ });
2791
+ commands.clearNodes();
2792
+ if (!tr.steps.length) {
2793
+ return;
2794
+ }
2795
+ return tr;
2796
+ },
2797
+ }),
2798
+ ];
2799
+ },
2800
+ });
2801
+
2802
+ const Tabindex = Extension.create({
2803
+ name: 'tabindex',
2804
+ addProseMirrorPlugins() {
2805
+ return [
2806
+ new Plugin({
2807
+ key: new PluginKey('tabindex'),
2808
+ props: {
2809
+ attributes: this.editor.isEditable ? { tabindex: '0' } : {},
2810
+ },
2811
+ }),
2812
+ ];
2813
+ },
2814
+ });
2815
+
2816
+ var extensions = /*#__PURE__*/Object.freeze({
2817
+ __proto__: null,
2818
+ ClipboardTextSerializer: ClipboardTextSerializer,
2198
2819
  Commands: Commands,
2199
2820
  Editable: Editable,
2200
2821
  FocusEvents: FocusEvents,
2201
- Keymap: Keymap
2822
+ Keymap: Keymap,
2823
+ Tabindex: Tabindex
2202
2824
  });
2203
2825
 
2826
+ function getNodeAttributes(state, typeOrName) {
2827
+ const type = getNodeType(typeOrName, state.schema);
2828
+ const { from, to } = state.selection;
2829
+ const nodes = [];
2830
+ state.doc.nodesBetween(from, to, node => {
2831
+ nodes.push(node);
2832
+ });
2833
+ const node = nodes
2834
+ .reverse()
2835
+ .find(nodeItem => nodeItem.type.name === type.name);
2836
+ if (!node) {
2837
+ return {};
2838
+ }
2839
+ return { ...node.attrs };
2840
+ }
2841
+
2842
+ function getAttributes(state, typeOrName) {
2843
+ const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
2844
+ ? typeOrName
2845
+ : typeOrName.name, state.schema);
2846
+ if (schemaType === 'node') {
2847
+ return getNodeAttributes(state, typeOrName);
2848
+ }
2849
+ if (schemaType === 'mark') {
2850
+ return getMarkAttributes(state, typeOrName);
2851
+ }
2852
+ return {};
2853
+ }
2854
+
2855
+ function getHTMLFromFragment(fragment, schema) {
2856
+ const documentFragment = DOMSerializer
2857
+ .fromSchema(schema)
2858
+ .serializeFragment(fragment);
2859
+ const temporaryDocument = document.implementation.createHTMLDocument();
2860
+ const container = temporaryDocument.createElement('div');
2861
+ container.appendChild(documentFragment);
2862
+ return container.innerHTML;
2863
+ }
2864
+
2865
+ function getText(node, options) {
2866
+ const range = {
2867
+ from: 0,
2868
+ to: node.content.size,
2869
+ };
2870
+ return getTextBetween(node, range, options);
2871
+ }
2872
+
2873
+ function isActive(state, name, attributes = {}) {
2874
+ if (!name) {
2875
+ return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
2876
+ }
2877
+ const schemaType = getSchemaTypeNameByName(name, state.schema);
2878
+ if (schemaType === 'node') {
2879
+ return isNodeActive(state, name, attributes);
2880
+ }
2881
+ if (schemaType === 'mark') {
2882
+ return isMarkActive(state, name, attributes);
2883
+ }
2884
+ return false;
2885
+ }
2886
+
2887
+ function isNodeEmpty(node) {
2888
+ var _a;
2889
+ const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
2890
+ const content = node.toJSON();
2891
+ return JSON.stringify(defaultContent) === JSON.stringify(content);
2892
+ }
2893
+
2204
2894
  const style = `.ProseMirror {
2205
2895
  position: relative;
2206
2896
  }
@@ -2208,8 +2898,10 @@ const style = `.ProseMirror {
2208
2898
  .ProseMirror {
2209
2899
  word-wrap: break-word;
2210
2900
  white-space: pre-wrap;
2901
+ white-space: break-spaces;
2211
2902
  -webkit-font-variant-ligatures: none;
2212
2903
  font-variant-ligatures: none;
2904
+ font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
2213
2905
  }
2214
2906
 
2215
2907
  .ProseMirror [contenteditable="false"] {
@@ -2224,10 +2916,19 @@ const style = `.ProseMirror {
2224
2916
  white-space: pre-wrap;
2225
2917
  }
2226
2918
 
2919
+ img.ProseMirror-separator {
2920
+ display: inline !important;
2921
+ border: none !important;
2922
+ margin: 0 !important;
2923
+ width: 1px !important;
2924
+ height: 1px !important;
2925
+ }
2926
+
2227
2927
  .ProseMirror-gapcursor {
2228
2928
  display: none;
2229
2929
  pointer-events: none;
2230
2930
  position: absolute;
2931
+ margin: 0;
2231
2932
  }
2232
2933
 
2233
2934
  .ProseMirror-gapcursor:after {
@@ -2260,16 +2961,37 @@ const style = `.ProseMirror {
2260
2961
 
2261
2962
  .ProseMirror-focused .ProseMirror-gapcursor {
2262
2963
  display: block;
2964
+ }
2965
+
2966
+ .tippy-box[data-animation=fade][data-state=hidden] {
2967
+ opacity: 0
2263
2968
  }`;
2264
2969
 
2970
+ function createStyleTag(style, nonce) {
2971
+ const tipTapStyleTag = document.querySelector('style[data-tiptap-style]');
2972
+ if (tipTapStyleTag !== null) {
2973
+ return tipTapStyleTag;
2974
+ }
2975
+ const styleNode = document.createElement('style');
2976
+ if (nonce) {
2977
+ styleNode.setAttribute('nonce', nonce);
2978
+ }
2979
+ styleNode.setAttribute('data-tiptap-style', '');
2980
+ styleNode.innerHTML = style;
2981
+ document.getElementsByTagName('head')[0].appendChild(styleNode);
2982
+ return styleNode;
2983
+ }
2984
+
2265
2985
  class Editor extends EventEmitter {
2266
2986
  constructor(options = {}) {
2267
2987
  super();
2268
2988
  this.isFocused = false;
2989
+ this.extensionStorage = {};
2269
2990
  this.options = {
2270
2991
  element: document.createElement('div'),
2271
2992
  content: '',
2272
2993
  injectCSS: true,
2994
+ injectNonce: undefined,
2273
2995
  extensions: [],
2274
2996
  autofocus: false,
2275
2997
  editable: true,
@@ -2277,14 +2999,14 @@ class Editor extends EventEmitter {
2277
2999
  parseOptions: {},
2278
3000
  enableInputRules: true,
2279
3001
  enablePasteRules: true,
3002
+ enableCoreExtensions: true,
3003
+ onBeforeCreate: () => null,
2280
3004
  onCreate: () => null,
2281
3005
  onUpdate: () => null,
2282
3006
  onSelectionUpdate: () => null,
2283
- onViewUpdate: () => null,
2284
3007
  onTransaction: () => null,
2285
3008
  onFocus: () => null,
2286
3009
  onBlur: () => null,
2287
- onResize: () => null,
2288
3010
  onDestroy: () => null,
2289
3011
  };
2290
3012
  this.isCapturingTransaction = false;
@@ -2293,51 +3015,55 @@ class Editor extends EventEmitter {
2293
3015
  this.createExtensionManager();
2294
3016
  this.createCommandManager();
2295
3017
  this.createSchema();
3018
+ this.on('beforeCreate', this.options.onBeforeCreate);
3019
+ this.emit('beforeCreate', { editor: this });
2296
3020
  this.createView();
2297
3021
  this.injectCSS();
2298
3022
  this.on('create', this.options.onCreate);
2299
3023
  this.on('update', this.options.onUpdate);
2300
3024
  this.on('selectionUpdate', this.options.onSelectionUpdate);
2301
- this.on('viewUpdate', this.options.onViewUpdate);
2302
3025
  this.on('transaction', this.options.onTransaction);
2303
3026
  this.on('focus', this.options.onFocus);
2304
3027
  this.on('blur', this.options.onBlur);
2305
3028
  this.on('destroy', this.options.onDestroy);
2306
3029
  window.setTimeout(() => {
3030
+ if (this.isDestroyed) {
3031
+ return;
3032
+ }
2307
3033
  this.commands.focus(this.options.autofocus);
2308
3034
  this.emit('create', { editor: this });
2309
- if (window.ResizeObserver) {
2310
- this.resizeObserver = new ResizeObserver(() => {
2311
- this.emit('resize', { editor: this });
2312
- });
2313
- this.resizeObserver.observe(this.view.dom);
2314
- }
2315
3035
  }, 0);
2316
3036
  }
3037
+ /**
3038
+ * Returns the editor storage.
3039
+ */
3040
+ get storage() {
3041
+ return this.extensionStorage;
3042
+ }
2317
3043
  /**
2318
3044
  * An object of all registered commands.
2319
3045
  */
2320
3046
  get commands() {
2321
- return this.commandManager.createCommands();
3047
+ return this.commandManager.commands;
2322
3048
  }
2323
3049
  /**
2324
3050
  * Create a command chain to call multiple commands at once.
2325
3051
  */
2326
3052
  chain() {
2327
- return this.commandManager.createChain();
3053
+ return this.commandManager.chain();
2328
3054
  }
2329
3055
  /**
2330
3056
  * Check if a command or a command chain can be executed. Without executing it.
2331
3057
  */
2332
3058
  can() {
2333
- return this.commandManager.createCan();
3059
+ return this.commandManager.can();
2334
3060
  }
2335
3061
  /**
2336
3062
  * Inject CSS styles.
2337
3063
  */
2338
3064
  injectCSS() {
2339
3065
  if (this.options.injectCSS && document) {
2340
- this.css = createStyleTag(style);
3066
+ this.css = createStyleTag(style, this.options.injectNonce);
2341
3067
  }
2342
3068
  }
2343
3069
  /**
@@ -2346,22 +3072,34 @@ class Editor extends EventEmitter {
2346
3072
  * @param options A list of options
2347
3073
  */
2348
3074
  setOptions(options = {}) {
2349
- this.options = { ...this.options, ...options };
3075
+ this.options = {
3076
+ ...this.options,
3077
+ ...options,
3078
+ };
3079
+ if (!this.view || !this.state || this.isDestroyed) {
3080
+ return;
3081
+ }
3082
+ if (this.options.editorProps) {
3083
+ this.view.setProps(this.options.editorProps);
3084
+ }
3085
+ this.view.updateState(this.state);
2350
3086
  }
2351
3087
  /**
2352
3088
  * Update editable state of the editor.
2353
3089
  */
2354
3090
  setEditable(editable) {
2355
3091
  this.setOptions({ editable });
2356
- if (this.view && this.state && !this.isDestroyed) {
2357
- this.view.updateState(this.state);
2358
- }
2359
3092
  }
2360
3093
  /**
2361
3094
  * Returns whether the editor is editable.
2362
3095
  */
2363
3096
  get isEditable() {
2364
- return this.view && this.view.editable;
3097
+ // since plugins are applied after creating the view
3098
+ // `editable` is always `true` for one tick.
3099
+ // that’s why we also have to check for `options.editable`
3100
+ return this.options.editable
3101
+ && this.view
3102
+ && this.view.editable;
2365
3103
  }
2366
3104
  /**
2367
3105
  * Returns the editor state.
@@ -2376,8 +3114,8 @@ class Editor extends EventEmitter {
2376
3114
  * @param handlePlugins Control how to merge the plugin into the existing plugins.
2377
3115
  */
2378
3116
  registerPlugin(plugin, handlePlugins) {
2379
- const plugins = typeof handlePlugins === 'function'
2380
- ? handlePlugins(plugin, this.state.plugins)
3117
+ const plugins = isFunction(handlePlugins)
3118
+ ? handlePlugins(plugin, [...this.state.plugins])
2381
3119
  : [...this.state.plugins, plugin];
2382
3120
  const state = this.state.reconfigure({ plugins });
2383
3121
  this.view.updateState(state);
@@ -2385,7 +3123,7 @@ class Editor extends EventEmitter {
2385
3123
  /**
2386
3124
  * Unregister a ProseMirror plugin.
2387
3125
  *
2388
- * @param name The plugins name
3126
+ * @param nameOrPluginKey The plugins name
2389
3127
  */
2390
3128
  unregisterPlugin(nameOrPluginKey) {
2391
3129
  if (this.isDestroyed) {
@@ -2405,7 +3143,9 @@ class Editor extends EventEmitter {
2405
3143
  * Creates an extension manager.
2406
3144
  */
2407
3145
  createExtensionManager() {
2408
- const coreExtensions = Object.entries(extensions).map(([, extension]) => extension);
3146
+ const coreExtensions = this.options.enableCoreExtensions
3147
+ ? Object.values(extensions)
3148
+ : [];
2409
3149
  const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
2410
3150
  return ['extension', 'node', 'mark'].includes(extension === null || extension === void 0 ? void 0 : extension.type);
2411
3151
  });
@@ -2415,7 +3155,9 @@ class Editor extends EventEmitter {
2415
3155
  * Creates an command manager.
2416
3156
  */
2417
3157
  createCommandManager() {
2418
- this.commandManager = new CommandManager(this, this.extensionManager.commands);
3158
+ this.commandManager = new CommandManager({
3159
+ editor: this,
3160
+ });
2419
3161
  }
2420
3162
  /**
2421
3163
  * Creates a ProseMirror schema.
@@ -2427,26 +3169,20 @@ class Editor extends EventEmitter {
2427
3169
  * Creates a ProseMirror view.
2428
3170
  */
2429
3171
  createView() {
3172
+ const doc = createDocument(this.options.content, this.schema, this.options.parseOptions);
3173
+ const selection = resolveFocusPosition(doc, this.options.autofocus);
2430
3174
  this.view = new EditorView(this.options.element, {
2431
3175
  ...this.options.editorProps,
2432
3176
  dispatchTransaction: this.dispatchTransaction.bind(this),
2433
3177
  state: EditorState.create({
2434
- doc: createDocument(this.options.content, this.schema, this.options.parseOptions),
3178
+ doc,
3179
+ selection: selection || undefined,
2435
3180
  }),
2436
3181
  });
2437
3182
  // `editor.view` is not yet available at this time.
2438
3183
  // Therefore we will add all plugins and node views directly afterwards.
2439
3184
  const newState = this.state.reconfigure({
2440
- plugins: [
2441
- new Plugin({
2442
- view: () => ({
2443
- update: () => this.emit('viewUpdate', {
2444
- editor: this,
2445
- }),
2446
- }),
2447
- }),
2448
- ...this.extensionManager.plugins,
2449
- ],
3185
+ plugins: this.extensionManager.plugins,
2450
3186
  });
2451
3187
  this.view.updateState(newState);
2452
3188
  this.createNodeViews();
@@ -2477,9 +3213,6 @@ class Editor extends EventEmitter {
2477
3213
  * @param transaction An editor state transaction
2478
3214
  */
2479
3215
  dispatchTransaction(transaction) {
2480
- if (transaction.docChanged && !this.isEditable) {
2481
- return;
2482
- }
2483
3216
  if (this.isCapturingTransaction) {
2484
3217
  if (!this.capturedTransaction) {
2485
3218
  this.capturedTransaction = transaction;
@@ -2498,6 +3231,7 @@ class Editor extends EventEmitter {
2498
3231
  if (selectionHasChanged) {
2499
3232
  this.emit('selectionUpdate', {
2500
3233
  editor: this,
3234
+ transaction,
2501
3235
  });
2502
3236
  }
2503
3237
  const focus = transaction.getMeta('focus');
@@ -2506,12 +3240,14 @@ class Editor extends EventEmitter {
2506
3240
  this.emit('focus', {
2507
3241
  editor: this,
2508
3242
  event: focus.event,
3243
+ transaction,
2509
3244
  });
2510
3245
  }
2511
3246
  if (blur) {
2512
3247
  this.emit('blur', {
2513
3248
  editor: this,
2514
3249
  event: blur.event,
3250
+ transaction,
2515
3251
  });
2516
3252
  }
2517
3253
  if (!transaction.docChanged || transaction.getMeta('preventUpdate')) {
@@ -2523,20 +3259,10 @@ class Editor extends EventEmitter {
2523
3259
  });
2524
3260
  }
2525
3261
  /**
2526
- * Get attributes of the currently selected node.
2527
- *
2528
- * @param name Name of the node
2529
- */
2530
- getNodeAttributes(name) {
2531
- return getNodeAttributes(this.state, name);
2532
- }
2533
- /**
2534
- * Get attributes of the currently selected mark.
2535
- *
2536
- * @param name Name of the mark
3262
+ * Get attributes of the currently selected node or mark.
2537
3263
  */
2538
- getMarkAttributes(name) {
2539
- return getMarkAttributes(this.state, name);
3264
+ getAttributes(nameOrType) {
3265
+ return getAttributes(this.state, nameOrType);
2540
3266
  }
2541
3267
  isActive(nameOrAttributes, attributesOrUndefined) {
2542
3268
  const name = typeof nameOrAttributes === 'string'
@@ -2557,7 +3283,20 @@ class Editor extends EventEmitter {
2557
3283
  * Get the document as HTML.
2558
3284
  */
2559
3285
  getHTML() {
2560
- return getHTMLFromFragment(this.state.doc, this.schema);
3286
+ return getHTMLFromFragment(this.state.doc.content, this.schema);
3287
+ }
3288
+ /**
3289
+ * Get the document as text.
3290
+ */
3291
+ getText(options) {
3292
+ const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
3293
+ return getText(this.state.doc, {
3294
+ blockSeparator,
3295
+ textSerializers: {
3296
+ ...textSerializers,
3297
+ ...getTextSerializersFromSchema(this.schema),
3298
+ },
3299
+ });
2561
3300
  }
2562
3301
  /**
2563
3302
  * Check if there is no content.
@@ -2567,22 +3306,22 @@ class Editor extends EventEmitter {
2567
3306
  }
2568
3307
  /**
2569
3308
  * Get the number of characters for the current document.
3309
+ *
3310
+ * @deprecated
2570
3311
  */
2571
3312
  getCharacterCount() {
3313
+ console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.');
2572
3314
  return this.state.doc.content.size - 2;
2573
3315
  }
2574
3316
  /**
2575
3317
  * Destroy the editor.
2576
3318
  */
2577
3319
  destroy() {
2578
- var _a;
2579
- (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.unobserve(this.view.dom);
2580
3320
  this.emit('destroy');
2581
3321
  if (this.view) {
2582
3322
  this.view.destroy();
2583
3323
  }
2584
3324
  this.removeAllListeners();
2585
- removeElement(this.css);
2586
3325
  }
2587
3326
  /**
2588
3327
  * Check if the editor is already destroyed.
@@ -2594,88 +3333,584 @@ class Editor extends EventEmitter {
2594
3333
  }
2595
3334
  }
2596
3335
 
2597
- var _configure$1;
2598
- class Node {
2599
- constructor(config) {
2600
- this.type = 'node';
2601
- this.config = {
2602
- name: 'node',
2603
- priority: 100,
2604
- defaultOptions: {},
2605
- };
2606
- _configure$1.set(this, (options) => {
2607
- this.options = mergeDeep(this.config.defaultOptions, options);
2608
- return this;
3336
+ /**
3337
+ * Returns a new `Transform` based on all steps of the passed transactions.
3338
+ */
3339
+ function combineTransactionSteps(oldDoc, transactions) {
3340
+ const transform = new Transform(oldDoc);
3341
+ transactions.forEach(transaction => {
3342
+ transaction.steps.forEach(step => {
3343
+ transform.step(step);
2609
3344
  });
2610
- this.config = {
2611
- ...this.config,
2612
- ...config,
3345
+ });
3346
+ return transform;
3347
+ }
3348
+
3349
+ function defaultBlockAt(match) {
3350
+ for (let i = 0; i < match.edgeCount; i += 1) {
3351
+ const { type } = match.edge(i);
3352
+ if (type.isTextblock && !type.hasRequiredAttrs()) {
3353
+ return type;
3354
+ }
3355
+ }
3356
+ return null;
3357
+ }
3358
+
3359
+ function findChildren(node, predicate) {
3360
+ const nodesWithPos = [];
3361
+ node.descendants((child, pos) => {
3362
+ if (predicate(child)) {
3363
+ nodesWithPos.push({
3364
+ node: child,
3365
+ pos,
3366
+ });
3367
+ }
3368
+ });
3369
+ return nodesWithPos;
3370
+ }
3371
+
3372
+ /**
3373
+ * Same as `findChildren` but searches only within a `range`.
3374
+ */
3375
+ function findChildrenInRange(node, range, predicate) {
3376
+ const nodesWithPos = [];
3377
+ // if (range.from === range.to) {
3378
+ // const nodeAt = node.nodeAt(range.from)
3379
+ // if (nodeAt) {
3380
+ // nodesWithPos.push({
3381
+ // node: nodeAt,
3382
+ // pos: range.from,
3383
+ // })
3384
+ // }
3385
+ // }
3386
+ node.nodesBetween(range.from, range.to, (child, pos) => {
3387
+ if (predicate(child)) {
3388
+ nodesWithPos.push({
3389
+ node: child,
3390
+ pos,
3391
+ });
3392
+ }
3393
+ });
3394
+ return nodesWithPos;
3395
+ }
3396
+
3397
+ function getSchema(extensions) {
3398
+ const resolvedExtensions = ExtensionManager.resolve(extensions);
3399
+ return getSchemaByResolvedExtensions(resolvedExtensions);
3400
+ }
3401
+
3402
+ function generateHTML(doc, extensions) {
3403
+ const schema = getSchema(extensions);
3404
+ const contentNode = Node$1.fromJSON(schema, doc);
3405
+ return getHTMLFromFragment(contentNode.content, schema);
3406
+ }
3407
+
3408
+ function generateJSON(html, extensions) {
3409
+ const schema = getSchema(extensions);
3410
+ const dom = elementFromString(html);
3411
+ return DOMParser.fromSchema(schema)
3412
+ .parse(dom)
3413
+ .toJSON();
3414
+ }
3415
+
3416
+ function generateText(doc, extensions, options) {
3417
+ const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
3418
+ const schema = getSchema(extensions);
3419
+ const contentNode = Node$1.fromJSON(schema, doc);
3420
+ return getText(contentNode, {
3421
+ blockSeparator,
3422
+ textSerializers: {
3423
+ ...textSerializers,
3424
+ ...getTextSerializersFromSchema(schema),
3425
+ },
3426
+ });
3427
+ }
3428
+
3429
+ /**
3430
+ * Removes duplicated values within an array.
3431
+ * Supports numbers, strings and objects.
3432
+ */
3433
+ function removeDuplicates(array, by = JSON.stringify) {
3434
+ const seen = {};
3435
+ return array.filter(item => {
3436
+ const key = by(item);
3437
+ return Object.prototype.hasOwnProperty.call(seen, key)
3438
+ ? false
3439
+ : (seen[key] = true);
3440
+ });
3441
+ }
3442
+
3443
+ /**
3444
+ * Removes duplicated ranges and ranges that are
3445
+ * fully captured by other ranges.
3446
+ */
3447
+ function simplifyChangedRanges(changes) {
3448
+ const uniqueChanges = removeDuplicates(changes);
3449
+ return uniqueChanges.length === 1
3450
+ ? uniqueChanges
3451
+ : uniqueChanges.filter((change, index) => {
3452
+ const rest = uniqueChanges.filter((_, i) => i !== index);
3453
+ return !rest.some(otherChange => {
3454
+ return change.oldRange.from >= otherChange.oldRange.from
3455
+ && change.oldRange.to <= otherChange.oldRange.to
3456
+ && change.newRange.from >= otherChange.newRange.from
3457
+ && change.newRange.to <= otherChange.newRange.to;
3458
+ });
3459
+ });
3460
+ }
3461
+ /**
3462
+ * Returns a list of changed ranges
3463
+ * based on the first and last state of all steps.
3464
+ */
3465
+ function getChangedRanges(transform) {
3466
+ const { mapping, steps } = transform;
3467
+ const changes = [];
3468
+ mapping.maps.forEach((stepMap, index) => {
3469
+ const ranges = [];
3470
+ // This accounts for step changes where no range was actually altered
3471
+ // e.g. when setting a mark, node attribute, etc.
3472
+ // @ts-ignore
3473
+ if (!stepMap.ranges.length) {
3474
+ const { from, to } = steps[index];
3475
+ if (from === undefined || to === undefined) {
3476
+ return;
3477
+ }
3478
+ ranges.push({ from, to });
3479
+ }
3480
+ else {
3481
+ stepMap.forEach((from, to) => {
3482
+ ranges.push({ from, to });
3483
+ });
3484
+ }
3485
+ ranges.forEach(({ from, to }) => {
3486
+ const newStart = mapping.slice(index).map(from, -1);
3487
+ const newEnd = mapping.slice(index).map(to);
3488
+ const oldStart = mapping.invert().map(newStart, -1);
3489
+ const oldEnd = mapping.invert().map(newEnd);
3490
+ changes.push({
3491
+ oldRange: {
3492
+ from: oldStart,
3493
+ to: oldEnd,
3494
+ },
3495
+ newRange: {
3496
+ from: newStart,
3497
+ to: newEnd,
3498
+ },
3499
+ });
3500
+ });
3501
+ });
3502
+ return simplifyChangedRanges(changes);
3503
+ }
3504
+
3505
+ function getDebugJSON(node, startOffset = 0) {
3506
+ const isTopNode = node.type === node.type.schema.topNodeType;
3507
+ const increment = isTopNode ? 0 : 1;
3508
+ const from = startOffset;
3509
+ const to = from + node.nodeSize;
3510
+ const marks = node.marks.map(mark => {
3511
+ const output = {
3512
+ type: mark.type.name,
2613
3513
  };
2614
- this.options = this.config.defaultOptions;
3514
+ if (Object.keys(mark.attrs).length) {
3515
+ output.attrs = { ...mark.attrs };
3516
+ }
3517
+ return output;
3518
+ });
3519
+ const attrs = { ...node.attrs };
3520
+ const output = {
3521
+ type: node.type.name,
3522
+ from,
3523
+ to,
3524
+ };
3525
+ if (Object.keys(attrs).length) {
3526
+ output.attrs = attrs;
2615
3527
  }
2616
- static create(config) {
2617
- return new Node(config);
3528
+ if (marks.length) {
3529
+ output.marks = marks;
2618
3530
  }
2619
- configure(options = {}) {
2620
- var _a;
2621
- return __classPrivateFieldGet((_a = Node
2622
- .create(this.config)), _configure$1).call(_a, options);
3531
+ if (node.content.childCount) {
3532
+ output.content = [];
3533
+ node.forEach((child, offset) => {
3534
+ var _a;
3535
+ (_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
3536
+ });
2623
3537
  }
2624
- extend(extendedConfig) {
2625
- return new Node({
2626
- ...this.config,
2627
- ...extendedConfig,
3538
+ if (node.text) {
3539
+ output.text = node.text;
3540
+ }
3541
+ return output;
3542
+ }
3543
+
3544
+ function getMarksBetween(from, to, doc) {
3545
+ const marks = [];
3546
+ // get all inclusive marks on empty selection
3547
+ if (from === to) {
3548
+ doc
3549
+ .resolve(from)
3550
+ .marks()
3551
+ .forEach(mark => {
3552
+ const $pos = doc.resolve(from - 1);
3553
+ const range = getMarkRange($pos, mark.type);
3554
+ if (!range) {
3555
+ return;
3556
+ }
3557
+ marks.push({
3558
+ mark,
3559
+ ...range,
3560
+ });
2628
3561
  });
2629
3562
  }
2630
- }
2631
- _configure$1 = new WeakMap();
3563
+ else {
3564
+ doc.nodesBetween(from, to, (node, pos) => {
3565
+ marks.push(...node.marks.map(mark => ({
3566
+ from: pos,
3567
+ to: pos + node.nodeSize,
3568
+ mark,
3569
+ })));
3570
+ });
3571
+ }
3572
+ return marks;
3573
+ }
3574
+
3575
+ function isNodeSelection(value) {
3576
+ return isObject(value) && value instanceof NodeSelection;
3577
+ }
3578
+
3579
+ function posToDOMRect(view, from, to) {
3580
+ const minPos = 0;
3581
+ const maxPos = view.state.doc.content.size;
3582
+ const resolvedFrom = minMax(from, minPos, maxPos);
3583
+ const resolvedEnd = minMax(to, minPos, maxPos);
3584
+ const start = view.coordsAtPos(resolvedFrom);
3585
+ const end = view.coordsAtPos(resolvedEnd, -1);
3586
+ const top = Math.min(start.top, end.top);
3587
+ const bottom = Math.max(start.bottom, end.bottom);
3588
+ const left = Math.min(start.left, end.left);
3589
+ const right = Math.max(start.right, end.right);
3590
+ const width = right - left;
3591
+ const height = bottom - top;
3592
+ const x = left;
3593
+ const y = top;
3594
+ const data = {
3595
+ top,
3596
+ bottom,
3597
+ left,
3598
+ right,
3599
+ width,
3600
+ height,
3601
+ x,
3602
+ y,
3603
+ };
3604
+ return {
3605
+ ...data,
3606
+ toJSON: () => data,
3607
+ };
3608
+ }
3609
+
3610
+ /**
3611
+ * Build an input rule that adds a mark when the
3612
+ * matched text is typed into it.
3613
+ */
3614
+ function markInputRule(config) {
3615
+ return new InputRule({
3616
+ find: config.find,
3617
+ handler: ({ state, range, match }) => {
3618
+ const attributes = callOrReturn(config.getAttributes, undefined, match);
3619
+ if (attributes === false || attributes === null) {
3620
+ return null;
3621
+ }
3622
+ const { tr } = state;
3623
+ const captureGroup = match[match.length - 1];
3624
+ const fullMatch = match[0];
3625
+ let markEnd = range.to;
3626
+ if (captureGroup) {
3627
+ const startSpaces = fullMatch.search(/\S/);
3628
+ const textStart = range.from + fullMatch.indexOf(captureGroup);
3629
+ const textEnd = textStart + captureGroup.length;
3630
+ const excludedMarks = getMarksBetween(range.from, range.to, state.doc)
3631
+ .filter(item => {
3632
+ // @ts-ignore
3633
+ const excluded = item.mark.type.excluded;
3634
+ return excluded.find(type => type === config.type && type !== item.mark.type);
3635
+ })
3636
+ .filter(item => item.to > textStart);
3637
+ if (excludedMarks.length) {
3638
+ return null;
3639
+ }
3640
+ if (textEnd < range.to) {
3641
+ tr.delete(textEnd, range.to);
3642
+ }
3643
+ if (textStart > range.from) {
3644
+ tr.delete(range.from + startSpaces, textStart);
3645
+ }
3646
+ markEnd = range.from + startSpaces + captureGroup.length;
3647
+ tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
3648
+ tr.removeStoredMark(config.type);
3649
+ }
3650
+ },
3651
+ });
3652
+ }
3653
+
3654
+ /**
3655
+ * Build an input rule that adds a node when the
3656
+ * matched text is typed into it.
3657
+ */
3658
+ function nodeInputRule(config) {
3659
+ return new InputRule({
3660
+ find: config.find,
3661
+ handler: ({ state, range, match }) => {
3662
+ const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
3663
+ const { tr } = state;
3664
+ const start = range.from;
3665
+ let end = range.to;
3666
+ if (match[1]) {
3667
+ const offset = match[0].lastIndexOf(match[1]);
3668
+ let matchStart = start + offset;
3669
+ if (matchStart > end) {
3670
+ matchStart = end;
3671
+ }
3672
+ else {
3673
+ end = matchStart + match[1].length;
3674
+ }
3675
+ // insert last typed character
3676
+ const lastChar = match[0][match[0].length - 1];
3677
+ tr.insertText(lastChar, start + match[0].length - 1);
3678
+ // insert node from input rule
3679
+ tr.replaceWith(matchStart, end, config.type.create(attributes));
3680
+ }
3681
+ else if (match[0]) {
3682
+ tr.replaceWith(start, end, config.type.create(attributes));
3683
+ }
3684
+ },
3685
+ });
3686
+ }
3687
+
3688
+ /**
3689
+ * Build an input rule that changes the type of a textblock when the
3690
+ * matched text is typed into it. When using a regular expresion you’ll
3691
+ * probably want the regexp to start with `^`, so that the pattern can
3692
+ * only occur at the start of a textblock.
3693
+ */
3694
+ function textblockTypeInputRule(config) {
3695
+ return new InputRule({
3696
+ find: config.find,
3697
+ handler: ({ state, range, match }) => {
3698
+ const $start = state.doc.resolve(range.from);
3699
+ const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
3700
+ if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {
3701
+ return null;
3702
+ }
3703
+ state.tr
3704
+ .delete(range.from, range.to)
3705
+ .setBlockType(range.from, range.from, config.type, attributes);
3706
+ },
3707
+ });
3708
+ }
3709
+
3710
+ /**
3711
+ * Build an input rule that replaces text when the
3712
+ * matched text is typed into it.
3713
+ */
3714
+ function textInputRule(config) {
3715
+ return new InputRule({
3716
+ find: config.find,
3717
+ handler: ({ state, range, match }) => {
3718
+ let insert = config.replace;
3719
+ let start = range.from;
3720
+ const end = range.to;
3721
+ if (match[1]) {
3722
+ const offset = match[0].lastIndexOf(match[1]);
3723
+ insert += match[0].slice(offset + match[1].length);
3724
+ start += offset;
3725
+ const cutOff = start - end;
3726
+ if (cutOff > 0) {
3727
+ insert = match[0].slice(offset - cutOff, offset) + insert;
3728
+ start = end;
3729
+ }
3730
+ }
3731
+ state.tr.insertText(insert, start, end);
3732
+ },
3733
+ });
3734
+ }
3735
+
3736
+ /**
3737
+ * Build an input rule for automatically wrapping a textblock when a
3738
+ * given string is typed. When using a regular expresion you’ll
3739
+ * probably want the regexp to start with `^`, so that the pattern can
3740
+ * only occur at the start of a textblock.
3741
+ *
3742
+ * `type` is the type of node to wrap in.
3743
+ *
3744
+ * By default, if there’s a node with the same type above the newly
3745
+ * wrapped node, the rule will try to join those
3746
+ * two nodes. You can pass a join predicate, which takes a regular
3747
+ * expression match and the node before the wrapped node, and can
3748
+ * return a boolean to indicate whether a join should happen.
3749
+ */
3750
+ function wrappingInputRule(config) {
3751
+ return new InputRule({
3752
+ find: config.find,
3753
+ handler: ({ state, range, match }) => {
3754
+ const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
3755
+ const tr = state.tr.delete(range.from, range.to);
3756
+ const $start = tr.doc.resolve(range.from);
3757
+ const blockRange = $start.blockRange();
3758
+ const wrapping = blockRange && findWrapping(blockRange, config.type, attributes);
3759
+ if (!wrapping) {
3760
+ return null;
3761
+ }
3762
+ tr.wrap(blockRange, wrapping);
3763
+ const before = tr.doc.resolve(range.from - 1).nodeBefore;
3764
+ if (before
3765
+ && before.type === config.type
3766
+ && canJoin(tr.doc, range.from - 1)
3767
+ && (!config.joinPredicate || config.joinPredicate(match, before))) {
3768
+ tr.join(range.from - 1);
3769
+ }
3770
+ },
3771
+ });
3772
+ }
2632
3773
 
2633
- var _configure;
2634
3774
  class Mark {
2635
- constructor(config) {
3775
+ constructor(config = {}) {
2636
3776
  this.type = 'mark';
3777
+ this.name = 'mark';
3778
+ this.parent = null;
3779
+ this.child = null;
2637
3780
  this.config = {
2638
- name: 'mark',
2639
- priority: 100,
3781
+ name: this.name,
2640
3782
  defaultOptions: {},
2641
3783
  };
2642
- _configure.set(this, (options) => {
2643
- this.options = mergeDeep(this.config.defaultOptions, options);
2644
- return this;
2645
- });
2646
3784
  this.config = {
2647
3785
  ...this.config,
2648
3786
  ...config,
2649
3787
  };
3788
+ this.name = this.config.name;
3789
+ if (config.defaultOptions) {
3790
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
3791
+ }
3792
+ // TODO: remove `addOptions` fallback
2650
3793
  this.options = this.config.defaultOptions;
3794
+ if (this.config.addOptions) {
3795
+ this.options = callOrReturn(getExtensionField(this, 'addOptions', {
3796
+ name: this.name,
3797
+ }));
3798
+ }
3799
+ this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
3800
+ name: this.name,
3801
+ options: this.options,
3802
+ })) || {};
2651
3803
  }
2652
- static create(config) {
3804
+ static create(config = {}) {
2653
3805
  return new Mark(config);
2654
3806
  }
2655
3807
  configure(options = {}) {
2656
- var _a;
2657
- return __classPrivateFieldGet((_a = Mark
2658
- .create(this.config)), _configure).call(_a, options);
3808
+ // return a new instance so we can use the same extension
3809
+ // with different calls of `configure`
3810
+ const extension = this.extend();
3811
+ extension.options = mergeDeep(this.options, options);
3812
+ extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
3813
+ name: extension.name,
3814
+ options: extension.options,
3815
+ }));
3816
+ return extension;
3817
+ }
3818
+ extend(extendedConfig = {}) {
3819
+ const extension = new Mark(extendedConfig);
3820
+ extension.parent = this;
3821
+ this.child = extension;
3822
+ extension.name = extendedConfig.name
3823
+ ? extendedConfig.name
3824
+ : extension.parent.name;
3825
+ if (extendedConfig.defaultOptions) {
3826
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
3827
+ }
3828
+ extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
3829
+ name: extension.name,
3830
+ }));
3831
+ extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
3832
+ name: extension.name,
3833
+ options: extension.options,
3834
+ }));
3835
+ return extension;
2659
3836
  }
2660
- extend(extendedConfig) {
2661
- return new Mark({
3837
+ }
3838
+
3839
+ class Node {
3840
+ constructor(config = {}) {
3841
+ this.type = 'node';
3842
+ this.name = 'node';
3843
+ this.parent = null;
3844
+ this.child = null;
3845
+ this.config = {
3846
+ name: this.name,
3847
+ defaultOptions: {},
3848
+ };
3849
+ this.config = {
2662
3850
  ...this.config,
2663
- ...extendedConfig,
2664
- });
3851
+ ...config,
3852
+ };
3853
+ this.name = this.config.name;
3854
+ if (config.defaultOptions) {
3855
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
3856
+ }
3857
+ // TODO: remove `addOptions` fallback
3858
+ this.options = this.config.defaultOptions;
3859
+ if (this.config.addOptions) {
3860
+ this.options = callOrReturn(getExtensionField(this, 'addOptions', {
3861
+ name: this.name,
3862
+ }));
3863
+ }
3864
+ this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
3865
+ name: this.name,
3866
+ options: this.options,
3867
+ })) || {};
2665
3868
  }
2666
- }
2667
- _configure = new WeakMap();
3869
+ static create(config = {}) {
3870
+ return new Node(config);
3871
+ }
3872
+ configure(options = {}) {
3873
+ // return a new instance so we can use the same extension
3874
+ // with different calls of `configure`
3875
+ const extension = this.extend();
3876
+ extension.options = mergeDeep(this.options, options);
3877
+ extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
3878
+ name: extension.name,
3879
+ options: extension.options,
3880
+ }));
3881
+ return extension;
3882
+ }
3883
+ extend(extendedConfig = {}) {
3884
+ const extension = new Node(extendedConfig);
3885
+ extension.parent = this;
3886
+ this.child = extension;
3887
+ extension.name = extendedConfig.name
3888
+ ? extendedConfig.name
3889
+ : extension.parent.name;
3890
+ if (extendedConfig.defaultOptions) {
3891
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
3892
+ }
3893
+ extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
3894
+ name: extension.name,
3895
+ }));
3896
+ extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
3897
+ name: extension.name,
3898
+ options: extension.options,
3899
+ }));
3900
+ return extension;
3901
+ }
3902
+ }
2668
3903
 
2669
3904
  class NodeView {
2670
3905
  constructor(component, props, options) {
2671
3906
  this.isDragging = false;
3907
+ this.component = component;
3908
+ this.editor = props.editor;
2672
3909
  this.options = {
2673
3910
  stopEvent: null,
2674
- update: null,
3911
+ ignoreMutation: null,
3912
+ ...options,
2675
3913
  };
2676
- this.component = component;
2677
- this.options = { ...this.options, ...options };
2678
- this.editor = props.editor;
2679
3914
  this.extension = props.extension;
2680
3915
  this.node = props.node;
2681
3916
  this.decorations = props.decorations;
@@ -2687,23 +3922,40 @@ class NodeView {
2687
3922
  return;
2688
3923
  }
2689
3924
  get dom() {
2690
- return null;
3925
+ return this.editor.view.dom;
2691
3926
  }
2692
3927
  get contentDOM() {
2693
3928
  return null;
2694
3929
  }
2695
3930
  onDragStart(event) {
2696
- var _a, _b;
2697
- if (!this.dom) {
2698
- return;
2699
- }
3931
+ var _a, _b, _c, _d, _e, _f, _g;
2700
3932
  const { view } = this.editor;
2701
3933
  const target = event.target;
2702
- if ((_a = this.contentDOM) === null || _a === void 0 ? void 0 : _a.contains(target)) {
3934
+ // get the drag handle element
3935
+ // `closest` is not available for text nodes so we may have to use its parent
3936
+ const dragHandle = target.nodeType === 3
3937
+ ? (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.closest('[data-drag-handle]')
3938
+ : target.closest('[data-drag-handle]');
3939
+ if (!this.dom
3940
+ || ((_b = this.contentDOM) === null || _b === void 0 ? void 0 : _b.contains(target))
3941
+ || !dragHandle) {
2703
3942
  return;
2704
3943
  }
2705
- // sometimes `event.target` is not the `dom` element
2706
- (_b = event.dataTransfer) === null || _b === void 0 ? void 0 : _b.setDragImage(this.dom, 0, 0);
3944
+ let x = 0;
3945
+ let y = 0;
3946
+ // calculate offset for drag element if we use a different drag handle element
3947
+ if (this.dom !== dragHandle) {
3948
+ const domBox = this.dom.getBoundingClientRect();
3949
+ const handleBox = dragHandle.getBoundingClientRect();
3950
+ // In React, we have to go through nativeEvent to reach offsetX/offsetY.
3951
+ const offsetX = (_c = event.offsetX) !== null && _c !== void 0 ? _c : (_d = event.nativeEvent) === null || _d === void 0 ? void 0 : _d.offsetX;
3952
+ const offsetY = (_e = event.offsetY) !== null && _e !== void 0 ? _e : (_f = event.nativeEvent) === null || _f === void 0 ? void 0 : _f.offsetY;
3953
+ x = handleBox.x - domBox.x + offsetX;
3954
+ y = handleBox.y - domBox.y + offsetY;
3955
+ }
3956
+ (_g = event.dataTransfer) === null || _g === void 0 ? void 0 : _g.setDragImage(this.dom, x, y);
3957
+ // we need to tell ProseMirror that we want to move the whole node
3958
+ // so we create a NodeSelection
2707
3959
  const selection = NodeSelection.create(view.state.doc, this.getPos());
2708
3960
  const transaction = view.state.tr.setSelection(selection);
2709
3961
  view.dispatch(transaction);
@@ -2714,7 +3966,7 @@ class NodeView {
2714
3966
  return false;
2715
3967
  }
2716
3968
  if (typeof this.options.stopEvent === 'function') {
2717
- return this.options.stopEvent(event);
3969
+ return this.options.stopEvent({ event });
2718
3970
  }
2719
3971
  const target = event.target;
2720
3972
  const isInElement = this.dom.contains(target) && !((_a = this.contentDOM) === null || _a === void 0 ? void 0 : _a.contains(target));
@@ -2722,10 +3974,11 @@ class NodeView {
2722
3974
  if (!isInElement) {
2723
3975
  return false;
2724
3976
  }
3977
+ const isDropEvent = event.type === 'drop';
2725
3978
  const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName)
2726
3979
  || target.isContentEditable;
2727
3980
  // any input event within node views should be ignored by ProseMirror
2728
- if (isInput) {
3981
+ if (isInput && !isDropEvent) {
2729
3982
  return true;
2730
3983
  }
2731
3984
  const { isEditable } = this.editor;
@@ -2736,7 +3989,7 @@ class NodeView {
2736
3989
  const isPasteEvent = event.type === 'paste';
2737
3990
  const isCutEvent = event.type === 'cut';
2738
3991
  const isClickEvent = event.type === 'mousedown';
2739
- const isDragEvent = event.type.startsWith('drag') || event.type === 'drop';
3992
+ const isDragEvent = event.type.startsWith('drag');
2740
3993
  // ProseMirror tries to drag selectable nodes
2741
3994
  // even if `draggable` is set to `false`
2742
3995
  // this fix prevents that
@@ -2764,6 +4017,7 @@ class NodeView {
2764
4017
  }
2765
4018
  // these events are handled by prosemirror
2766
4019
  if (isDragging
4020
+ || isDropEvent
2767
4021
  || isCopyEvent
2768
4022
  || isPasteEvent
2769
4023
  || isCutEvent
@@ -2773,156 +4027,166 @@ class NodeView {
2773
4027
  return true;
2774
4028
  }
2775
4029
  ignoreMutation(mutation) {
4030
+ if (!this.dom || !this.contentDOM) {
4031
+ return true;
4032
+ }
4033
+ if (typeof this.options.ignoreMutation === 'function') {
4034
+ return this.options.ignoreMutation({ mutation });
4035
+ }
4036
+ // a leaf/atom node is like a black box for ProseMirror
4037
+ // and should be fully handled by the node view
4038
+ if (this.node.isLeaf || this.node.isAtom) {
4039
+ return true;
4040
+ }
4041
+ // ProseMirror should handle any selections
2776
4042
  if (mutation.type === 'selection') {
2777
- if (this.node.isLeaf) {
2778
- return true;
2779
- }
2780
4043
  return false;
2781
4044
  }
2782
- if (!this.contentDOM) {
4045
+ // try to prevent a bug on iOS that will break node views on enter
4046
+ // this is because ProseMirror can’t preventDispatch on enter
4047
+ // this will lead to a re-render of the node view on enter
4048
+ // see: https://github.com/ueberdosis/tiptap/issues/1214
4049
+ if (this.dom.contains(mutation.target)
4050
+ && mutation.type === 'childList'
4051
+ && isiOS()
4052
+ && this.editor.isFocused) {
4053
+ const changedNodes = [
4054
+ ...Array.from(mutation.addedNodes),
4055
+ ...Array.from(mutation.removedNodes),
4056
+ ];
4057
+ // we’ll check if every changed node is contentEditable
4058
+ // to make sure it’s probably mutated by ProseMirror
4059
+ if (changedNodes.every(node => node.isContentEditable)) {
4060
+ return false;
4061
+ }
4062
+ }
4063
+ // we will allow mutation contentDOM with attributes
4064
+ // so we can for example adding classes within our node view
4065
+ if (this.contentDOM === mutation.target && mutation.type === 'attributes') {
2783
4066
  return true;
2784
4067
  }
2785
- const contentDOMHasChanged = !this.contentDOM.contains(mutation.target)
2786
- || this.contentDOM === mutation.target;
2787
- return contentDOMHasChanged;
4068
+ // ProseMirror should handle any changes within contentDOM
4069
+ if (this.contentDOM.contains(mutation.target)) {
4070
+ return false;
4071
+ }
4072
+ return true;
2788
4073
  }
2789
4074
  updateAttributes(attributes) {
2790
- if (!this.editor.view.editable) {
2791
- return;
2792
- }
2793
- const { state } = this.editor.view;
2794
- const pos = this.getPos();
2795
- const transaction = state.tr.setNodeMarkup(pos, undefined, {
2796
- ...this.node.attrs,
2797
- ...attributes,
4075
+ this.editor.commands.command(({ tr }) => {
4076
+ const pos = this.getPos();
4077
+ tr.setNodeMarkup(pos, undefined, {
4078
+ ...this.node.attrs,
4079
+ ...attributes,
4080
+ });
4081
+ return true;
2798
4082
  });
2799
- this.editor.view.dispatch(transaction);
4083
+ }
4084
+ deleteNode() {
4085
+ const from = this.getPos();
4086
+ const to = from + this.node.nodeSize;
4087
+ this.editor.commands.deleteRange({ from, to });
2800
4088
  }
2801
4089
  }
2802
4090
 
2803
- function nodeInputRule (regexp, type, getAttributes) {
2804
- return new InputRule(regexp, (state, match, start, end) => {
2805
- const attributes = getAttributes instanceof Function
2806
- ? getAttributes(match)
2807
- : getAttributes;
2808
- const { tr } = state;
2809
- if (match[0]) {
2810
- tr.replaceWith(start - 1, end, type.create(attributes));
2811
- }
2812
- return tr;
2813
- });
2814
- }
2815
-
2816
- function getMarksBetween(from, to, state) {
2817
- let marks = [];
2818
- state.doc.nodesBetween(from, to, (node, pos) => {
2819
- marks = [...marks, ...node.marks.map(mark => ({
2820
- from: pos,
2821
- to: pos + node.nodeSize,
2822
- mark,
2823
- }))];
2824
- });
2825
- return marks;
2826
- }
2827
-
2828
- function markInputRule (regexp, markType, getAttributes) {
2829
- return new InputRule(regexp, (state, match, start, end) => {
2830
- const attributes = getAttributes instanceof Function
2831
- ? getAttributes(match)
2832
- : getAttributes;
2833
- const { tr } = state;
2834
- const captureGroup = match[match.length - 1];
2835
- const fullMatch = match[0];
2836
- let markEnd = end;
2837
- if (captureGroup) {
2838
- const startSpaces = fullMatch.search(/\S/);
2839
- const textStart = start + fullMatch.indexOf(captureGroup);
2840
- const textEnd = textStart + captureGroup.length;
2841
- const excludedMarks = getMarksBetween(start, end, state)
2842
- .filter(item => {
2843
- // TODO: PR to add excluded to MarkType
2844
- // @ts-ignore
2845
- const { excluded } = item.mark.type;
2846
- return excluded.find((type) => type.name === markType.name);
2847
- })
2848
- .filter(item => item.to > textStart);
2849
- if (excludedMarks.length) {
4091
+ /**
4092
+ * Build an paste rule that adds a mark when the
4093
+ * matched text is pasted into it.
4094
+ */
4095
+ function markPasteRule(config) {
4096
+ return new PasteRule({
4097
+ find: config.find,
4098
+ handler: ({ state, range, match }) => {
4099
+ const attributes = callOrReturn(config.getAttributes, undefined, match);
4100
+ if (attributes === false || attributes === null) {
2850
4101
  return null;
2851
4102
  }
2852
- if (textEnd < end) {
2853
- tr.delete(textEnd, end);
2854
- }
2855
- if (textStart > start) {
2856
- tr.delete(start + startSpaces, textStart);
4103
+ const { tr } = state;
4104
+ const captureGroup = match[match.length - 1];
4105
+ const fullMatch = match[0];
4106
+ let markEnd = range.to;
4107
+ if (captureGroup) {
4108
+ const startSpaces = fullMatch.search(/\S/);
4109
+ const textStart = range.from + fullMatch.indexOf(captureGroup);
4110
+ const textEnd = textStart + captureGroup.length;
4111
+ const excludedMarks = getMarksBetween(range.from, range.to, state.doc)
4112
+ .filter(item => {
4113
+ // @ts-ignore
4114
+ const excluded = item.mark.type.excluded;
4115
+ return excluded.find(type => type === config.type && type !== item.mark.type);
4116
+ })
4117
+ .filter(item => item.to > textStart);
4118
+ if (excludedMarks.length) {
4119
+ return null;
4120
+ }
4121
+ if (textEnd < range.to) {
4122
+ tr.delete(textEnd, range.to);
4123
+ }
4124
+ if (textStart > range.from) {
4125
+ tr.delete(range.from + startSpaces, textStart);
4126
+ }
4127
+ markEnd = range.from + startSpaces + captureGroup.length;
4128
+ tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
4129
+ tr.removeStoredMark(config.type);
2857
4130
  }
2858
- markEnd = start + startSpaces + captureGroup.length;
2859
- tr.addMark(start + startSpaces, markEnd, markType.create(attributes));
2860
- tr.removeStoredMark(markType);
2861
- }
2862
- return tr;
4131
+ },
2863
4132
  });
2864
4133
  }
2865
4134
 
2866
- function markPasteRule (regexp, type, getAttrs) {
2867
- const handler = (fragment, parent) => {
2868
- const nodes = [];
2869
- fragment.forEach(child => {
2870
- if (child.isText && child.text) {
2871
- const { text } = child;
2872
- let pos = 0;
2873
- let match;
2874
- // eslint-disable-next-line
2875
- while ((match = regexp.exec(text)) !== null) {
2876
- const outerMatch = Math.max(match.length - 2, 0);
2877
- const innerMatch = Math.max(match.length - 1, 0);
2878
- if (parent === null || parent === void 0 ? void 0 : parent.type.allowsMarkType(type)) {
2879
- const start = match.index;
2880
- const matchStart = start + match[0].indexOf(match[outerMatch]);
2881
- const matchEnd = matchStart + match[outerMatch].length;
2882
- const textStart = matchStart + match[outerMatch].lastIndexOf(match[innerMatch]);
2883
- const textEnd = textStart + match[innerMatch].length;
2884
- const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
2885
- // adding text before markdown to nodes
2886
- if (matchStart > 0) {
2887
- nodes.push(child.cut(pos, matchStart));
2888
- }
2889
- // adding the markdown part to nodes
2890
- nodes.push(child
2891
- .cut(textStart, textEnd)
2892
- .mark(type.create(attrs).addToSet(child.marks)));
2893
- pos = matchEnd;
2894
- }
2895
- }
2896
- // adding rest of text to nodes
2897
- if (pos < text.length) {
2898
- nodes.push(child.cut(pos));
4135
+ /**
4136
+ * Build an paste rule that replaces text when the
4137
+ * matched text is pasted into it.
4138
+ */
4139
+ function textPasteRule(config) {
4140
+ return new PasteRule({
4141
+ find: config.find,
4142
+ handler: ({ state, range, match }) => {
4143
+ let insert = config.replace;
4144
+ let start = range.from;
4145
+ const end = range.to;
4146
+ if (match[1]) {
4147
+ const offset = match[0].lastIndexOf(match[1]);
4148
+ insert += match[0].slice(offset + match[1].length);
4149
+ start += offset;
4150
+ const cutOff = start - end;
4151
+ if (cutOff > 0) {
4152
+ insert = match[0].slice(offset - cutOff, offset) + insert;
4153
+ start = end;
2899
4154
  }
2900
4155
  }
2901
- else {
2902
- nodes.push(child.copy(handler(child.content, child)));
2903
- }
2904
- });
2905
- return Fragment.fromArray(nodes);
2906
- };
2907
- return new Plugin({
2908
- key: new PluginKey('markPasteRule'),
2909
- props: {
2910
- transformPasted: slice => {
2911
- return new Slice(handler(slice.content), slice.openStart, slice.openEnd);
2912
- },
4156
+ state.tr.insertText(insert, start, end);
2913
4157
  },
2914
4158
  });
2915
4159
  }
2916
4160
 
2917
- function generateHTML(doc, extensions) {
2918
- const schema = getSchema(extensions);
2919
- const contentNode = Node$1.fromJSON(schema, doc);
2920
- return getHTMLFromFragment(contentNode, schema);
4161
+ class Tracker {
4162
+ constructor(transaction) {
4163
+ this.transaction = transaction;
4164
+ this.currentStep = this.transaction.steps.length;
4165
+ }
4166
+ map(position) {
4167
+ let deleted = false;
4168
+ const mappedPosition = this.transaction.steps
4169
+ .slice(this.currentStep)
4170
+ .reduce((newPosition, step) => {
4171
+ const mapResult = step
4172
+ .getMap()
4173
+ .mapResult(newPosition);
4174
+ if (mapResult.deleted) {
4175
+ deleted = true;
4176
+ }
4177
+ return mapResult.pos;
4178
+ }, position);
4179
+ return {
4180
+ position: mappedPosition,
4181
+ deleted,
4182
+ };
4183
+ }
2921
4184
  }
2922
4185
 
2923
- function isNodeSelection(value) {
2924
- return isObject(value) && value instanceof NodeSelection;
4186
+ // source: https://stackoverflow.com/a/6969486
4187
+ function escapeForRegEx(string) {
4188
+ return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
2925
4189
  }
2926
4190
 
2927
- export { Editor, Extension, Mark, Node, NodeView, callOrReturn, extensions, findParentNodeClosestToPos, generateHTML, getHTMLFromFragment, getMarkAttributes, getSchema, isActive, isMarkActive, isNodeActive, isNodeEmpty, isNodeSelection, isTextSelection, markInputRule, markPasteRule, mergeAttributes, nodeInputRule };
4191
+ export { CommandManager, Editor, Extension, InputRule, Mark, Node, NodeView, PasteRule, Tracker, callOrReturn, combineTransactionSteps, defaultBlockAt, escapeForRegEx, extensions, findChildren, findChildrenInRange, findParentNode, findParentNodeClosestToPos, generateHTML, generateJSON, generateText, getAttributes, getChangedRanges, getDebugJSON, getExtensionField, getHTMLFromFragment, getMarkAttributes, getMarkRange, getMarkType, getMarksBetween, getNodeAttributes, getNodeType, getSchema, getText, getTextBetween, getTextContentFromNodes, getTextSerializersFromSchema, inputRulesPlugin, isActive, isList, isMarkActive, isNodeActive, isNodeEmpty, isNodeSelection, isTextSelection, markInputRule, markPasteRule, mergeAttributes, nodeInputRule, pasteRulesPlugin, posToDOMRect, textInputRule, textPasteRule, textblockTypeInputRule, wrappingInputRule };
2928
4192
  //# sourceMappingURL=tiptap-core.esm.js.map