@tiptap/core 2.0.0-beta.98 → 2.0.0-rc.1

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