@prosekit/extensions 0.13.0 → 0.14.0

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 (311) hide show
  1. package/dist/{drop-indicator-B1QHFb5m.js → drop-indicator-B_oMfeVP.js} +11 -10
  2. package/dist/drop-indicator-B_oMfeVP.js.map +1 -0
  3. package/dist/{enter-rule-CzWOZF_Z.js → enter-rule-D-p4ykfv.js} +1 -1
  4. package/dist/enter-rule-D-p4ykfv.js.map +1 -0
  5. package/dist/{file-DrfcSid-.js → file-iLVR0eM0.js} +3 -3
  6. package/dist/file-iLVR0eM0.js.map +1 -0
  7. package/dist/{index-oIc1a2f2.d.ts → index-cp1u4e0e.d.ts} +1 -1
  8. package/dist/index-cp1u4e0e.d.ts.map +1 -0
  9. package/dist/{input-rule-dmsb3j6w.js → input-rule-COGr_GBb.js} +1 -1
  10. package/dist/input-rule-COGr_GBb.js.map +1 -0
  11. package/dist/{mark-rule-BcLB4Uv2.js → mark-rule-CYe8zk4q.js} +6 -6
  12. package/dist/mark-rule-CYe8zk4q.js.map +1 -0
  13. package/dist/{paste-rule-pVb4sqvJ.js → paste-rule-BaDghcaU.js} +7 -7
  14. package/dist/paste-rule-BaDghcaU.js.map +1 -0
  15. package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -1
  16. package/dist/prosekit-extensions-autocomplete.js.map +1 -1
  17. package/dist/prosekit-extensions-background-color.d.ts +62 -0
  18. package/dist/prosekit-extensions-background-color.d.ts.map +1 -0
  19. package/dist/prosekit-extensions-background-color.js +76 -0
  20. package/dist/prosekit-extensions-background-color.js.map +1 -0
  21. package/dist/prosekit-extensions-blockquote.d.ts.map +1 -1
  22. package/dist/prosekit-extensions-blockquote.js +1 -1
  23. package/dist/prosekit-extensions-blockquote.js.map +1 -1
  24. package/dist/prosekit-extensions-bold.d.ts.map +1 -1
  25. package/dist/prosekit-extensions-bold.js +1 -1
  26. package/dist/prosekit-extensions-bold.js.map +1 -1
  27. package/dist/prosekit-extensions-code-block.d.ts +1 -1
  28. package/dist/prosekit-extensions-code-block.d.ts.map +1 -1
  29. package/dist/prosekit-extensions-code-block.js +4 -4
  30. package/dist/prosekit-extensions-code-block.js.map +1 -1
  31. package/dist/prosekit-extensions-code.d.ts.map +1 -1
  32. package/dist/prosekit-extensions-code.js +1 -1
  33. package/dist/prosekit-extensions-code.js.map +1 -1
  34. package/dist/prosekit-extensions-commit.d.ts +0 -1
  35. package/dist/prosekit-extensions-commit.d.ts.map +1 -1
  36. package/dist/prosekit-extensions-commit.js.map +1 -1
  37. package/dist/prosekit-extensions-doc.d.ts +0 -1
  38. package/dist/prosekit-extensions-doc.d.ts.map +1 -1
  39. package/dist/prosekit-extensions-doc.js.map +1 -1
  40. package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -1
  41. package/dist/prosekit-extensions-drop-cursor.js.map +1 -1
  42. package/dist/prosekit-extensions-drop-indicator.d.ts +0 -1
  43. package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -1
  44. package/dist/prosekit-extensions-drop-indicator.js +1 -1
  45. package/dist/prosekit-extensions-enter-rule.d.ts +0 -1
  46. package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -1
  47. package/dist/prosekit-extensions-enter-rule.js +1 -1
  48. package/dist/prosekit-extensions-file.d.ts +1 -1
  49. package/dist/prosekit-extensions-file.js +1 -1
  50. package/dist/prosekit-extensions-gap-cursor.d.ts +0 -1
  51. package/dist/prosekit-extensions-gap-cursor.d.ts.map +1 -1
  52. package/dist/prosekit-extensions-gap-cursor.js.map +1 -1
  53. package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
  54. package/dist/prosekit-extensions-hard-break.js.map +1 -1
  55. package/dist/prosekit-extensions-heading.d.ts.map +1 -1
  56. package/dist/prosekit-extensions-heading.js +1 -1
  57. package/dist/prosekit-extensions-heading.js.map +1 -1
  58. package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -1
  59. package/dist/prosekit-extensions-horizontal-rule.js +1 -1
  60. package/dist/prosekit-extensions-horizontal-rule.js.map +1 -1
  61. package/dist/prosekit-extensions-image.d.ts +9 -2
  62. package/dist/prosekit-extensions-image.d.ts.map +1 -1
  63. package/dist/prosekit-extensions-image.js +17 -4
  64. package/dist/prosekit-extensions-image.js.map +1 -1
  65. package/dist/prosekit-extensions-input-rule.d.ts +0 -1
  66. package/dist/prosekit-extensions-input-rule.d.ts.map +1 -1
  67. package/dist/prosekit-extensions-input-rule.js +1 -1
  68. package/dist/prosekit-extensions-italic.d.ts.map +1 -1
  69. package/dist/prosekit-extensions-italic.js +1 -1
  70. package/dist/prosekit-extensions-italic.js.map +1 -1
  71. package/dist/prosekit-extensions-link.d.ts +0 -1
  72. package/dist/prosekit-extensions-link.d.ts.map +1 -1
  73. package/dist/prosekit-extensions-link.js +4 -4
  74. package/dist/prosekit-extensions-link.js.map +1 -1
  75. package/dist/prosekit-extensions-list.d.ts +0 -1
  76. package/dist/prosekit-extensions-list.d.ts.map +1 -1
  77. package/dist/prosekit-extensions-list.js +3 -3
  78. package/dist/prosekit-extensions-list.js.map +1 -1
  79. package/dist/prosekit-extensions-loro.d.ts.map +1 -1
  80. package/dist/prosekit-extensions-loro.js +2 -2
  81. package/dist/prosekit-extensions-loro.js.map +1 -1
  82. package/dist/prosekit-extensions-mark-rule.d.ts +0 -1
  83. package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -1
  84. package/dist/prosekit-extensions-mark-rule.js +1 -1
  85. package/dist/prosekit-extensions-mention.d.ts.map +1 -1
  86. package/dist/prosekit-extensions-mention.js.map +1 -1
  87. package/dist/prosekit-extensions-mod-click-prevention.d.ts +0 -1
  88. package/dist/prosekit-extensions-mod-click-prevention.d.ts.map +1 -1
  89. package/dist/prosekit-extensions-mod-click-prevention.js.map +1 -1
  90. package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
  91. package/dist/prosekit-extensions-paragraph.js.map +1 -1
  92. package/dist/prosekit-extensions-paste-rule.d.ts +0 -1
  93. package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -1
  94. package/dist/prosekit-extensions-paste-rule.js +1 -1
  95. package/dist/prosekit-extensions-placeholder.d.ts.map +1 -1
  96. package/dist/prosekit-extensions-placeholder.js +1 -1
  97. package/dist/prosekit-extensions-placeholder.js.map +1 -1
  98. package/dist/prosekit-extensions-readonly.d.ts +0 -1
  99. package/dist/prosekit-extensions-readonly.d.ts.map +1 -1
  100. package/dist/prosekit-extensions-readonly.js.map +1 -1
  101. package/dist/prosekit-extensions-search.d.ts +0 -1
  102. package/dist/prosekit-extensions-search.d.ts.map +1 -1
  103. package/dist/prosekit-extensions-search.js.map +1 -1
  104. package/dist/prosekit-extensions-strike.d.ts +0 -1
  105. package/dist/prosekit-extensions-strike.d.ts.map +1 -1
  106. package/dist/prosekit-extensions-strike.js +1 -1
  107. package/dist/prosekit-extensions-table.d.ts.map +1 -1
  108. package/dist/prosekit-extensions-table.js +1 -1
  109. package/dist/prosekit-extensions-text-align.d.ts +0 -1
  110. package/dist/prosekit-extensions-text-align.d.ts.map +1 -1
  111. package/dist/prosekit-extensions-text-align.js +2 -2
  112. package/dist/prosekit-extensions-text-align.js.map +1 -1
  113. package/dist/prosekit-extensions-text-color.d.ts +62 -0
  114. package/dist/prosekit-extensions-text-color.d.ts.map +1 -0
  115. package/dist/prosekit-extensions-text-color.js +76 -0
  116. package/dist/prosekit-extensions-text-color.js.map +1 -0
  117. package/dist/prosekit-extensions-text.d.ts +0 -1
  118. package/dist/prosekit-extensions-text.d.ts.map +1 -1
  119. package/dist/prosekit-extensions-text.js.map +1 -1
  120. package/dist/prosekit-extensions-underline.d.ts +0 -1
  121. package/dist/prosekit-extensions-underline.d.ts.map +1 -1
  122. package/dist/prosekit-extensions-virtual-selection.d.ts +0 -1
  123. package/dist/prosekit-extensions-virtual-selection.d.ts.map +1 -1
  124. package/dist/prosekit-extensions-virtual-selection.js.map +1 -1
  125. package/dist/prosekit-extensions-yjs.d.ts +9 -2
  126. package/dist/prosekit-extensions-yjs.d.ts.map +1 -1
  127. package/dist/prosekit-extensions-yjs.js.map +1 -1
  128. package/dist/{shiki-highlighter-chunk-rkzofy4z.d.ts → shiki-highlighter-chunk-DNNm2Vow.d.ts} +1 -1
  129. package/dist/shiki-highlighter-chunk-DNNm2Vow.d.ts.map +1 -0
  130. package/dist/shiki-highlighter-chunk.d.ts +1 -1
  131. package/dist/shiki-highlighter-chunk.js.map +1 -1
  132. package/dist/{table-BRDh_9mG.js → table-4oHfV-Ql.js} +2 -2
  133. package/dist/table-4oHfV-Ql.js.map +1 -0
  134. package/package.json +28 -14
  135. package/src/autocomplete/autocomplete-helpers.ts +1 -5
  136. package/src/autocomplete/autocomplete-plugin.ts +3 -13
  137. package/src/autocomplete/autocomplete.spec.ts +4 -19
  138. package/src/autocomplete/autocomplete.ts +1 -7
  139. package/src/background-color/background-color-commands.spec.ts +71 -0
  140. package/src/background-color/background-color-commands.ts +35 -0
  141. package/src/background-color/background-color-spec.spec.ts +286 -0
  142. package/src/background-color/background-color-spec.ts +58 -0
  143. package/src/background-color/background-color.ts +21 -0
  144. package/src/background-color/index.ts +8 -0
  145. package/src/blockquote/blockquote-commands.ts +1 -7
  146. package/src/blockquote/blockquote-keymap.spec.ts +1 -5
  147. package/src/blockquote/blockquote-keymap.ts +1 -6
  148. package/src/blockquote/blockquote-spec.ts +1 -4
  149. package/src/blockquote/blockquote.ts +3 -12
  150. package/src/blockquote/index.ts +3 -12
  151. package/src/bold/bold-commands.ts +1 -5
  152. package/src/bold/bold-input-rule.spec.ts +1 -5
  153. package/src/bold/bold-input-rule.ts +1 -4
  154. package/src/bold/bold-keymap.ts +1 -5
  155. package/src/bold/bold-spec.ts +1 -4
  156. package/src/bold/bold.ts +3 -12
  157. package/src/bold/index.ts +3 -12
  158. package/src/code/code-commands.ts +1 -5
  159. package/src/code/code-input-rule.ts +1 -4
  160. package/src/code/code-keymap.ts +1 -5
  161. package/src/code/code-spec.ts +1 -4
  162. package/src/code/code.ts +3 -12
  163. package/src/code/index.ts +3 -12
  164. package/src/code-block/code-block-commands.ts +1 -8
  165. package/src/code-block/code-block-highlight.ts +2 -8
  166. package/src/code-block/code-block-keymap.ts +2 -9
  167. package/src/code-block/code-block-shiki.ts +1 -4
  168. package/src/code-block/code-block-spec.spec.ts +4 -11
  169. package/src/code-block/code-block-spec.ts +1 -4
  170. package/src/code-block/code-block.ts +4 -16
  171. package/src/code-block/index.ts +5 -21
  172. package/src/code-block/shiki-highlighter-chunk.ts +1 -7
  173. package/src/code-block/shiki-highlighter.ts +1 -4
  174. package/src/code-block/shiki-parser.ts +1 -4
  175. package/src/commit/index.ts +6 -35
  176. package/src/doc/index.ts +1 -4
  177. package/src/drop-cursor/drop-cursor.ts +1 -4
  178. package/src/drop-cursor/index.ts +1 -5
  179. package/src/drop-indicator/drop-indicator-facet.ts +12 -21
  180. package/src/drop-indicator/index.ts +1 -5
  181. package/src/enter-rule/index.ts +2 -11
  182. package/src/file/file-paste-handler.spec.ts +3 -16
  183. package/src/file/index.ts +3 -16
  184. package/src/gap-cursor/gap-cursor.ts +1 -4
  185. package/src/gap-cursor/index.ts +1 -4
  186. package/src/hard-break/hard-break-commands.ts +1 -5
  187. package/src/hard-break/hard-break-keymap.spec.ts +1 -5
  188. package/src/hard-break/hard-break-keymap.ts +1 -4
  189. package/src/hard-break/hard-break-spec.ts +1 -4
  190. package/src/hard-break/hard-break.ts +3 -12
  191. package/src/hard-break/index.ts +3 -12
  192. package/src/heading/heading-commands.ts +1 -7
  193. package/src/heading/heading-keymap.spec.ts +1 -5
  194. package/src/heading/heading-keymap.ts +1 -8
  195. package/src/heading/heading-spec.ts +1 -4
  196. package/src/heading/heading.ts +3 -12
  197. package/src/heading/index.ts +3 -12
  198. package/src/horizontal-rule/horizontal-rule-commands.spec.ts +1 -5
  199. package/src/horizontal-rule/horizontal-rule-commands.ts +2 -9
  200. package/src/horizontal-rule/horizontal-rule-input-rule.spec.ts +5 -9
  201. package/src/horizontal-rule/horizontal-rule-input-rule.ts +1 -5
  202. package/src/horizontal-rule/horizontal-rule-spec.ts +1 -4
  203. package/src/horizontal-rule/horizontal-rule.ts +3 -12
  204. package/src/horizontal-rule/index.ts +3 -13
  205. package/src/image/image-commands/upload-image.spec.ts +245 -0
  206. package/src/image/image-commands/upload-image.ts +46 -11
  207. package/src/image/image-commands.ts +2 -8
  208. package/src/image/image-spec.ts +1 -4
  209. package/src/image/image-upload-handler.ts +2 -8
  210. package/src/image/image.ts +3 -12
  211. package/src/image/index.ts +3 -13
  212. package/src/input-rule/index.ts +2 -13
  213. package/src/italic/index.ts +3 -12
  214. package/src/italic/italic-commands.spec.ts +2 -10
  215. package/src/italic/italic-commands.ts +1 -5
  216. package/src/italic/italic-input-rule.spec.ts +1 -5
  217. package/src/italic/italic-input-rule.ts +1 -4
  218. package/src/italic/italic-keymap.ts +1 -5
  219. package/src/italic/italic-spec.ts +1 -4
  220. package/src/italic/italic.ts +3 -12
  221. package/src/link/index.spec.ts +1 -5
  222. package/src/link/index.ts +1 -5
  223. package/src/link/link-paste-rule.spec.ts +2 -9
  224. package/src/link/link-regex.spec.ts +1 -5
  225. package/src/list/index.ts +3 -12
  226. package/src/list/list-commands.ts +1 -5
  227. package/src/list/list-input-rules.ts +1 -4
  228. package/src/list/list-keymap.spec.ts +1 -5
  229. package/src/list/list-keymap.ts +2 -8
  230. package/src/list/list-plugins.ts +1 -4
  231. package/src/list/list-serializer.ts +2 -9
  232. package/src/list/list-spec.ts +3 -13
  233. package/src/list/list.spec.ts +10 -21
  234. package/src/list/list.ts +3 -12
  235. package/src/loro/index.ts +3 -13
  236. package/src/loro/loro-commands.ts +2 -8
  237. package/src/loro/loro-cursor-plugin.ts +3 -13
  238. package/src/loro/loro-keymap.ts +2 -10
  239. package/src/loro/loro-sync-plugin.ts +2 -8
  240. package/src/loro/loro-undo-plugin.ts +2 -8
  241. package/src/loro/loro.ts +2 -11
  242. package/src/mark-rule/apply.ts +3 -13
  243. package/src/mark-rule/mark-rule.spec.ts +2 -13
  244. package/src/mark-rule/mark-rule.ts +2 -13
  245. package/src/mark-rule/range.ts +2 -8
  246. package/src/mark-rule/types.ts +1 -4
  247. package/src/mention/index.ts +1 -8
  248. package/src/mod-click-prevention/index.ts +2 -9
  249. package/src/paragraph/paragraph-commands.ts +1 -5
  250. package/src/paragraph/paragraph-keymap.ts +1 -4
  251. package/src/paragraph/paragraph-spec.ts +1 -4
  252. package/src/paragraph/paragraph.ts +3 -14
  253. package/src/paste-rule/index.ts +2 -10
  254. package/src/paste-rule/mark-paste-rule.spec.ts +3 -13
  255. package/src/paste-rule/mark-paste-rule.ts +4 -14
  256. package/src/paste-rule/paste-rule-plugin.ts +2 -11
  257. package/src/paste-rule/paste-rule.spec.ts +4 -19
  258. package/src/paste-rule/split-text-by-regex.spec.ts +1 -5
  259. package/src/placeholder/index.ts +3 -16
  260. package/src/readonly/index.ts +2 -8
  261. package/src/search/index.ts +1 -6
  262. package/src/table/index.ts +10 -40
  263. package/src/table/table-commands/delete-cell-selection.spec.ts +1 -5
  264. package/src/table/table-commands/exit-table.spec.ts +1 -5
  265. package/src/table/table-commands/insert-table.spec.ts +1 -5
  266. package/src/table/table-commands/insert-table.ts +1 -4
  267. package/src/table/table-commands/move-table-column.spec.ts +1 -5
  268. package/src/table/table-commands/move-table-column.ts +1 -4
  269. package/src/table/table-commands/move-table-row.spec.ts +1 -5
  270. package/src/table/table-commands/move-table-row.ts +1 -4
  271. package/src/table/table-commands/select-table-cell.spec.ts +1 -5
  272. package/src/table/table-commands/select-table-column.spec.ts +1 -5
  273. package/src/table/table-commands/select-table-row.spec.ts +1 -5
  274. package/src/table/table-commands/select-table.spec.ts +1 -5
  275. package/src/table/table-commands/select-table.ts +1 -4
  276. package/src/table/table-commands.ts +8 -32
  277. package/src/table/table-plugins.ts +2 -8
  278. package/src/table/table-spec.spec.ts +2 -11
  279. package/src/table/table-spec.ts +2 -8
  280. package/src/table/table-utils.ts +2 -6
  281. package/src/table/table.ts +2 -8
  282. package/src/table/test-utils.ts +1 -4
  283. package/src/testing/clipboard.ts +1 -2
  284. package/src/testing/index.ts +7 -12
  285. package/src/text/index.ts +1 -4
  286. package/src/text-align/index.ts +2 -2
  287. package/src/text-color/index.ts +3 -0
  288. package/src/text-color/text-color-commands.spec.ts +71 -0
  289. package/src/text-color/text-color-commands.ts +35 -0
  290. package/src/text-color/text-color-spec.spec.ts +297 -0
  291. package/src/text-color/text-color-spec.ts +58 -0
  292. package/src/text-color/text-color.ts +21 -0
  293. package/src/virtual-selection/index.ts +3 -14
  294. package/src/yjs/index.ts +5 -20
  295. package/src/yjs/yjs-commands.ts +2 -8
  296. package/src/yjs/yjs-cursor-plugin.ts +3 -5
  297. package/src/yjs/yjs-keymap.ts +2 -10
  298. package/src/yjs/yjs-sync-plugin.ts +1 -4
  299. package/src/yjs/yjs-types.ts +10 -0
  300. package/src/yjs/yjs-undo-plugin.ts +2 -8
  301. package/src/yjs/yjs.ts +6 -24
  302. package/dist/drop-indicator-B1QHFb5m.js.map +0 -1
  303. package/dist/enter-rule-CzWOZF_Z.js.map +0 -1
  304. package/dist/file-DrfcSid-.js.map +0 -1
  305. package/dist/index-oIc1a2f2.d.ts.map +0 -1
  306. package/dist/input-rule-dmsb3j6w.js.map +0 -1
  307. package/dist/mark-rule-BcLB4Uv2.js.map +0 -1
  308. package/dist/paste-rule-pVb4sqvJ.js.map +0 -1
  309. package/dist/shiki-highlighter-chunk-rkzofy4z.d.ts.map +0 -1
  310. package/dist/table-BRDh_9mG.js.map +0 -1
  311. package/src/testing/format-html.ts +0 -5
@@ -0,0 +1,297 @@
1
+ import { formatHTML } from 'diffable-html-snapshot'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { setupTest } from '../testing'
5
+
6
+ describe('defineTextColorSpec', () => {
7
+ it('should render color as inline span with style attribute', () => {
8
+ const { editor, n, m } = setupTest()
9
+
10
+ const doc = n.doc(
11
+ n.p(
12
+ 'Default',
13
+ m.textColor({ color: '#0000ff' }, 'hex'),
14
+ m.textColor({ color: 'blue' }, 'named'),
15
+ m.textColor({ color: 'rgb(0, 0, 255)' }, 'rgb'),
16
+ m.textColor({ color: 'rgba(0 0 255 / 0.5)' }, 'rgba'),
17
+ m.textColor({ color: 'hsl(240 100% 50% / 0.5)' }, 'hsl'),
18
+ m.textColor({ color: 'var(--color-variable)' }, 'variable'),
19
+ ),
20
+ )
21
+ editor.set(doc)
22
+ expect(formatHTML(editor.view.dom.innerHTML)).toMatchInlineSnapshot(
23
+ `
24
+ "
25
+ <p>
26
+ Default
27
+ <span
28
+ data-text-color="#0000ff"
29
+ style="color: rgb(0, 0, 255);"
30
+ >
31
+ hex
32
+ </span>
33
+ <span
34
+ data-text-color="blue"
35
+ style="color: blue;"
36
+ >
37
+ named
38
+ </span>
39
+ <span
40
+ data-text-color="rgb(0, 0, 255)"
41
+ style="color: rgb(0, 0, 255);"
42
+ >
43
+ rgb
44
+ </span>
45
+ <span
46
+ data-text-color="rgba(0 0 255 / 0.5)"
47
+ style="color: rgba(0, 0, 255, 0.5);"
48
+ >
49
+ rgba
50
+ </span>
51
+ <span
52
+ data-text-color="hsl(240 100% 50% / 0.5)"
53
+ style="color: rgba(0, 0, 255, 0.5);"
54
+ >
55
+ hsl
56
+ </span>
57
+ <span
58
+ data-text-color="var(--color-variable)"
59
+ style="color: var(--color-variable);"
60
+ >
61
+ variable
62
+ </span>
63
+ </p>
64
+ "
65
+ `,
66
+ )
67
+ })
68
+
69
+ it('should parse color from style attribute', () => {
70
+ const { editor } = setupTest()
71
+
72
+ const html = `<p><span style="color: #0000ff;">text</span></p>`
73
+ editor.setContent(html)
74
+ expect(editor.state.doc.firstChild?.firstChild?.toJSON()).toMatchInlineSnapshot(`
75
+ {
76
+ "marks": [
77
+ {
78
+ "attrs": {
79
+ "color": "rgb(0, 0, 255)",
80
+ },
81
+ "type": "textColor",
82
+ },
83
+ ],
84
+ "text": "text",
85
+ "type": "text",
86
+ }
87
+ `)
88
+ })
89
+
90
+ it('should parse color from data-text-color attribute', () => {
91
+ const { editor } = setupTest()
92
+
93
+ const html = `<p><span data-text-color="rgb(0 0 255 / 0.5)">text</span></p>`
94
+ editor.setContent(html)
95
+ expect(editor.state.doc.firstChild?.firstChild?.toJSON()).toMatchInlineSnapshot(`
96
+ {
97
+ "marks": [
98
+ {
99
+ "attrs": {
100
+ "color": "rgb(0 0 255 / 0.5)",
101
+ },
102
+ "type": "textColor",
103
+ },
104
+ ],
105
+ "text": "text",
106
+ "type": "text",
107
+ }
108
+ `)
109
+ })
110
+
111
+ it('should prioritize data-text-color attribute over style attribute', () => {
112
+ const { editor } = setupTest()
113
+
114
+ const html = `<p><span style="color: blue;" data-text-color="red">This should be red</span></p>`
115
+ editor.setContent(html)
116
+ expect(editor.state.doc.firstChild?.firstChild?.toJSON()).toMatchInlineSnapshot(`
117
+ {
118
+ "marks": [
119
+ {
120
+ "attrs": {
121
+ "color": "red",
122
+ },
123
+ "type": "textColor",
124
+ },
125
+ ],
126
+ "text": "This should be red",
127
+ "type": "text",
128
+ }
129
+ `)
130
+ })
131
+
132
+ it('can handle non-span inline elements', () => {
133
+ const { editor } = setupTest()
134
+
135
+ const html = `<p>`
136
+ + `<i><span style="color: red;">This should be red and italic</span></i>`
137
+ + `<span style="color: green;"><i>This should be green and italic</i></span>`
138
+ + `<b style="color: blue;">This should be blue and bold</b>`
139
+ + `</p>`
140
+ editor.setContent(html)
141
+ expect(editor.state.doc.firstChild?.toJSON()).toMatchInlineSnapshot(`
142
+ {
143
+ "content": [
144
+ {
145
+ "marks": [
146
+ {
147
+ "attrs": {
148
+ "color": "red",
149
+ },
150
+ "type": "textColor",
151
+ },
152
+ {
153
+ "type": "italic",
154
+ },
155
+ ],
156
+ "text": "This should be red and italic",
157
+ "type": "text",
158
+ },
159
+ {
160
+ "marks": [
161
+ {
162
+ "attrs": {
163
+ "color": "green",
164
+ },
165
+ "type": "textColor",
166
+ },
167
+ {
168
+ "type": "italic",
169
+ },
170
+ ],
171
+ "text": "This should be green and italic",
172
+ "type": "text",
173
+ },
174
+ {
175
+ "marks": [
176
+ {
177
+ "attrs": {
178
+ "color": "blue",
179
+ },
180
+ "type": "textColor",
181
+ },
182
+ {
183
+ "type": "bold",
184
+ },
185
+ ],
186
+ "text": "This should be blue and bold",
187
+ "type": "text",
188
+ },
189
+ ],
190
+ "type": "paragraph",
191
+ }
192
+ `)
193
+ })
194
+
195
+ it('should ignore empty attributes', () => {
196
+ const { editor } = setupTest()
197
+
198
+ const html = `<p><span data-text-color="" style="color: ;">This should be plain text</span></p>`
199
+ editor.setContent(html)
200
+ expect(editor.state.doc.firstChild?.firstChild?.toJSON()).toMatchInlineSnapshot(`
201
+ {
202
+ "text": "This should be plain text",
203
+ "type": "text",
204
+ }
205
+ `)
206
+ })
207
+
208
+ it('should ignore inherit attributes', () => {
209
+ const { editor } = setupTest()
210
+
211
+ const html = `<p><span data-text-color="inherit" style="color: inherit;">This should be plain text</span></p>`
212
+ editor.setContent(html)
213
+ expect(editor.state.doc.firstChild?.firstChild?.toJSON()).toMatchInlineSnapshot(`
214
+ {
215
+ "text": "This should be plain text",
216
+ "type": "text",
217
+ }
218
+ `)
219
+ })
220
+
221
+ it('can handle block elements', () => {
222
+ const { editor } = setupTest()
223
+
224
+ const html = `<blockquote style="color: red;">`
225
+ + `<p>This should be red</p>`
226
+ + `<blockquote style="color: blue;">`
227
+ + `<p>This should be blue</p>`
228
+ + `</blockquote>`
229
+ + `<p>This should be red again</p>`
230
+ + `</blockquote>`
231
+
232
+ editor.setContent(html)
233
+ expect(editor.state.doc.firstChild?.toJSON()).toMatchInlineSnapshot(`
234
+ {
235
+ "content": [
236
+ {
237
+ "content": [
238
+ {
239
+ "marks": [
240
+ {
241
+ "attrs": {
242
+ "color": "red",
243
+ },
244
+ "type": "textColor",
245
+ },
246
+ ],
247
+ "text": "This should be red",
248
+ "type": "text",
249
+ },
250
+ ],
251
+ "type": "paragraph",
252
+ },
253
+ {
254
+ "content": [
255
+ {
256
+ "content": [
257
+ {
258
+ "marks": [
259
+ {
260
+ "attrs": {
261
+ "color": "blue",
262
+ },
263
+ "type": "textColor",
264
+ },
265
+ ],
266
+ "text": "This should be blue",
267
+ "type": "text",
268
+ },
269
+ ],
270
+ "type": "paragraph",
271
+ },
272
+ ],
273
+ "type": "blockquote",
274
+ },
275
+ {
276
+ "content": [
277
+ {
278
+ "marks": [
279
+ {
280
+ "attrs": {
281
+ "color": "red",
282
+ },
283
+ "type": "textColor",
284
+ },
285
+ ],
286
+ "text": "This should be red again",
287
+ "type": "text",
288
+ },
289
+ ],
290
+ "type": "paragraph",
291
+ },
292
+ ],
293
+ "type": "blockquote",
294
+ }
295
+ `)
296
+ })
297
+ })
@@ -0,0 +1,58 @@
1
+ import { defineMarkSpec, type Extension } from '@prosekit/core'
2
+
3
+ /**
4
+ * Attributes for the `textColor` mark.
5
+ *
6
+ * @public
7
+ */
8
+ export interface TextColorAttrs {
9
+ color: string
10
+ }
11
+
12
+ /**
13
+ * @internal
14
+ */
15
+ export type TextColorSpecExtension = Extension<{
16
+ Marks: {
17
+ textColor: TextColorAttrs
18
+ }
19
+ }>
20
+
21
+ /**
22
+ * @internal
23
+ */
24
+ export function defineTextColorSpec(): TextColorSpecExtension {
25
+ return defineMarkSpec<'textColor', TextColorAttrs>({
26
+ name: 'textColor',
27
+ attrs: {
28
+ color: {
29
+ validate: 'string',
30
+ },
31
+ },
32
+ parseDOM: [
33
+ {
34
+ tag: ':where([style*="color:"], [data-text-color])',
35
+ getAttrs: (node): TextColorAttrs | false => {
36
+ // When both `data-text-color` and `style="color"` are present, we
37
+ // prioritize the `data-text-color` attribute. This avoids the
38
+ // browser's default behavior of changing hex colors to rgba in style
39
+ // attribute.
40
+ const value = node.getAttribute('data-text-color')
41
+ if (value && value !== 'inherit') {
42
+ return { color: value }
43
+ }
44
+ const color = node.style.color
45
+ if (color && color !== 'inherit') {
46
+ return { color }
47
+ }
48
+ return false
49
+ },
50
+ consuming: false,
51
+ },
52
+ ],
53
+ toDOM(mark) {
54
+ const color = (mark.attrs as TextColorAttrs).color
55
+ return ['span', { 'style': `color: ${color};`, 'data-text-color': color }, 0]
56
+ },
57
+ })
58
+ }
@@ -0,0 +1,21 @@
1
+ import { union, type Union } from '@prosekit/core'
2
+
3
+ import { defineTextColorCommands, type TextColorCommandsExtension } from './text-color-commands'
4
+ import { defineTextColorSpec, type TextColorSpecExtension } from './text-color-spec'
5
+
6
+ /**
7
+ * @internal
8
+ */
9
+ export type TextColorExtension = Union<[TextColorSpecExtension, TextColorCommandsExtension]>
10
+
11
+ /**
12
+ * Defines the `textColor` mark and some commands for it.
13
+ *
14
+ * @public
15
+ */
16
+ export function defineTextColor(): TextColorExtension {
17
+ return union(
18
+ defineTextColorSpec(),
19
+ defineTextColorCommands(),
20
+ )
21
+ }
@@ -1,17 +1,6 @@
1
- import {
2
- definePlugin,
3
- type PlainExtension,
4
- } from '@prosekit/core'
5
- import {
6
- PluginKey,
7
- ProseMirrorPlugin,
8
- type EditorState,
9
- type Transaction,
10
- } from '@prosekit/pm/state'
11
- import {
12
- Decoration,
13
- DecorationSet,
14
- } from '@prosekit/pm/view'
1
+ import { definePlugin, type PlainExtension } from '@prosekit/core'
2
+ import { PluginKey, ProseMirrorPlugin, type EditorState, type Transaction } from '@prosekit/pm/state'
3
+ import { Decoration, DecorationSet } from '@prosekit/pm/view'
15
4
 
16
5
  /**
17
6
  * @internal
package/src/yjs/index.ts CHANGED
@@ -1,22 +1,7 @@
1
- export {
2
- defineYjs,
3
- type YjsExtension,
4
- type YjsOptions,
5
- } from './yjs'
1
+ export { defineYjs, type YjsExtension, type YjsOptions } from './yjs'
6
2
  export { defineYjsCommands } from './yjs-commands'
7
- export {
8
- defineYjsCursorPlugin,
9
- type YjsCursorOptions,
10
- type YjsCursorPluginOptions,
11
- } from './yjs-cursor-plugin'
3
+ export { defineYjsCursorPlugin, type YjsCursorOptions, type YjsCursorPluginOptions } from './yjs-cursor-plugin'
12
4
  export { defineYjsKeymap } from './yjs-keymap'
13
- export {
14
- defineYjsSyncPlugin,
15
- type YjsSyncOptions,
16
- type YjsSyncPluginOptions,
17
- } from './yjs-sync-plugin'
18
- export {
19
- defineYjsUndoPlugin,
20
- type YjsUndoOptions,
21
- type YjsUndoPluginOptions,
22
- } from './yjs-undo-plugin'
5
+ export { defineYjsSyncPlugin, type YjsSyncOptions, type YjsSyncPluginOptions } from './yjs-sync-plugin'
6
+ export type { Awareness } from './yjs-types'
7
+ export { defineYjsUndoPlugin, type YjsUndoOptions, type YjsUndoPluginOptions } from './yjs-undo-plugin'
@@ -1,11 +1,5 @@
1
- import {
2
- defineCommands,
3
- type Extension,
4
- } from '@prosekit/core'
5
- import {
6
- redoCommand,
7
- undoCommand,
8
- } from 'y-prosemirror'
1
+ import { defineCommands, type Extension } from '@prosekit/core'
2
+ import { redoCommand, undoCommand } from 'y-prosemirror'
9
3
 
10
4
  const commands = {
11
5
  undo: () => undoCommand,
@@ -1,10 +1,8 @@
1
- import {
2
- definePlugin,
3
- type PlainExtension,
4
- } from '@prosekit/core'
1
+ import { definePlugin, type PlainExtension } from '@prosekit/core'
5
2
  import type { Plugin } from '@prosekit/pm/state'
6
3
  import { yCursorPlugin } from 'y-prosemirror'
7
- import type { Awareness } from 'y-protocols/awareness'
4
+
5
+ import type { Awareness } from './yjs-types'
8
6
 
9
7
  /**
10
8
  * Options for `y-prosemirror`'s `yCursorPlugin`.
@@ -1,13 +1,5 @@
1
- import {
2
- defineKeymap,
3
- isApple,
4
- type Keymap,
5
- type PlainExtension,
6
- } from '@prosekit/core'
7
- import {
8
- redoCommand,
9
- undoCommand,
10
- } from 'y-prosemirror'
1
+ import { defineKeymap, isApple, type Keymap, type PlainExtension } from '@prosekit/core'
2
+ import { redoCommand, undoCommand } from 'y-prosemirror'
11
3
 
12
4
  const keymap: Keymap = {
13
5
  'Mod-z': undoCommand,
@@ -1,7 +1,4 @@
1
- import {
2
- definePlugin,
3
- type PlainExtension,
4
- } from '@prosekit/core'
1
+ import { definePlugin, type PlainExtension } from '@prosekit/core'
5
2
  import type { Plugin } from '@prosekit/pm/state'
6
3
  import { ySyncPlugin } from 'y-prosemirror'
7
4
  import type * as Y from 'yjs'
@@ -0,0 +1,10 @@
1
+ import type { yCursorPlugin } from 'y-prosemirror'
2
+
3
+ // TODO: Remove `import type { Awareness } from 'y-protocols/awareness'` from examples.
4
+
5
+ /**
6
+ * @internal
7
+ *
8
+ * The type `Awareness` is not exported by `y-prosemirror` so we need to define it like this.
9
+ */
10
+ export type Awareness = Parameters<typeof yCursorPlugin>[0]
@@ -1,13 +1,7 @@
1
- import {
2
- definePlugin,
3
- type PlainExtension,
4
- } from '@prosekit/core'
1
+ import { definePlugin, type PlainExtension } from '@prosekit/core'
5
2
  import type { ProseMirrorPlugin } from '@prosekit/pm/state'
6
3
  import type { EditorView } from '@prosekit/pm/view'
7
- import {
8
- yUndoPlugin as originalYUndoPlugin,
9
- yUndoPluginKey,
10
- } from 'y-prosemirror'
4
+ import { yUndoPlugin as originalYUndoPlugin, yUndoPluginKey } from 'y-prosemirror'
11
5
  import type { UndoManager as YjsUndoManager } from 'yjs'
12
6
 
13
7
  type UndoManager = YjsUndoManager & { restore: () => void }
package/src/yjs/yjs.ts CHANGED
@@ -1,30 +1,12 @@
1
- import {
2
- Priority,
3
- union,
4
- withPriority,
5
- type PlainExtension,
6
- type Union,
7
- } from '@prosekit/core'
8
- import type { Awareness } from 'y-protocols/awareness'
1
+ import { Priority, union, withPriority, type PlainExtension, type Union } from '@prosekit/core'
9
2
  import type * as Y from 'yjs'
10
3
 
11
- import {
12
- defineYjsCommands,
13
- type YjsCommandsExtension,
14
- } from './yjs-commands'
15
- import {
16
- defineYjsCursorPlugin,
17
- type YjsCursorPluginOptions,
18
- } from './yjs-cursor-plugin'
4
+ import { defineYjsCommands, type YjsCommandsExtension } from './yjs-commands'
5
+ import { defineYjsCursorPlugin, type YjsCursorPluginOptions } from './yjs-cursor-plugin'
19
6
  import { defineYjsKeymap } from './yjs-keymap'
20
- import {
21
- defineYjsSyncPlugin,
22
- type YjsSyncPluginOptions,
23
- } from './yjs-sync-plugin'
24
- import {
25
- defineYjsUndoPlugin,
26
- type YjsUndoPluginOptions,
27
- } from './yjs-undo-plugin'
7
+ import { defineYjsSyncPlugin, type YjsSyncPluginOptions } from './yjs-sync-plugin'
8
+ import type { Awareness } from './yjs-types'
9
+ import { defineYjsUndoPlugin, type YjsUndoPluginOptions } from './yjs-undo-plugin'
28
10
 
29
11
  export interface YjsOptions {
30
12
  /**
@@ -1 +0,0 @@
1
- {"version":3,"file":"drop-indicator-B1QHFb5m.js","names":["showHandler: ShowHandler","hideHandler: VoidFunction","dragHandler: DragEventHandler"],"sources":["../src/drop-indicator/drop-indicator-facet.ts","../src/drop-indicator/drop-indicator.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport type {\n DragEventHandler,\n DropIndicatorPluginOptions,\n ShowHandler,\n} from 'prosemirror-drop-indicator'\nimport { createDropIndicatorPlugin } from 'prosemirror-drop-indicator'\n\n/**\n * @internal\n */\nexport function defineDropIndicatorPayload(\n payload: DropIndicatorPluginOptions,\n): PlainExtension {\n return defineFacetPayload(dropIndicatorFacet, [payload]) as PlainExtension\n}\n\nconst dropIndicatorFacet = defineFacet<DropIndicatorPluginOptions, PluginPayload>({\n parent: pluginFacet,\n singleton: true,\n reducer: (payloads: DropIndicatorPluginOptions[]): PluginPayload => {\n let showHandlers = payloads.map(p => p.onShow).filter(x => !!x)\n let hideHandlers = payloads.map(p => p.onHide).filter(x => !!x)\n let dragHandlers = payloads.map(p => p.onDrag).filter(x => !!x)\n\n let showHandler: ShowHandler = (options) => {\n for (let fn of showHandlers) {\n fn(options)\n }\n }\n\n let hideHandler: VoidFunction = () => {\n for (let fn of hideHandlers) {\n fn()\n }\n }\n\n let dragHandler: DragEventHandler = (options): boolean => {\n for (let fn of dragHandlers) {\n if (fn(options) === false) return false\n }\n return true\n }\n\n if (showHandlers.length === 0) {\n // No `onShow` event handler, so we don't need to create a plugin.\n return []\n }\n\n return createDropIndicatorPlugin({\n onDrag: dragHandler,\n onShow: showHandler,\n onHide: hideHandler,\n })\n },\n})\n","import type { PlainExtension } from '@prosekit/core'\nimport type { DropIndicatorPluginOptions } from 'prosemirror-drop-indicator'\n\nimport { defineDropIndicatorPayload } from './drop-indicator-facet'\n\n/**\n * @internal\n */\nexport type DropIndicatorExtension = PlainExtension\n\n/**\n * Defines an extension that controls the behavior of the drop indicator.\n *\n * This extension itself doesn't draw the drop indicator, but it provides the\n * necessary callbacks to do so. You probably don't want to use this extension\n * directly, but rather use the `<DropIndicator>` component.\n *\n * You can add this extension multiple times. If any extension has `onDrag`\n * callback defined, and it returns `false`, then the drop point will be\n * discarded.\n *\n * @public\n */\nexport function defineDropIndicator(\n options?: DropIndicatorOptions,\n): DropIndicatorExtension {\n return defineDropIndicatorPayload(options ?? {})\n}\n\n/**\n * Options for {@link defineDropIndicator}.\n *\n * @public\n */\nexport interface DropIndicatorOptions extends DropIndicatorPluginOptions {}\n"],"mappings":";;;;;;;AAiBA,SAAgB,2BACd,SACgB;AAChB,QAAO,mBAAmB,oBAAoB,CAAC,QAAQ,CAAC;;AAG1D,MAAM,qBAAqB,YAAuD;CAChF,QAAQ;CACR,WAAW;CACX,UAAU,aAA0D;EAClE,IAAI,eAAe,SAAS,KAAI,MAAK,EAAE,OAAO,CAAC,QAAO,MAAK,CAAC,CAAC,EAAE;EAC/D,IAAI,eAAe,SAAS,KAAI,MAAK,EAAE,OAAO,CAAC,QAAO,MAAK,CAAC,CAAC,EAAE;EAC/D,IAAI,eAAe,SAAS,KAAI,MAAK,EAAE,OAAO,CAAC,QAAO,MAAK,CAAC,CAAC,EAAE;EAE/D,IAAIA,eAA4B,YAAY;AAC1C,QAAK,IAAI,MAAM,aACb,IAAG,QAAQ;;EAIf,IAAIC,oBAAkC;AACpC,QAAK,IAAI,MAAM,aACb,KAAI;;EAIR,IAAIC,eAAiC,YAAqB;AACxD,QAAK,IAAI,MAAM,aACb,KAAI,GAAG,QAAQ,KAAK,MAAO,QAAO;AAEpC,UAAO;;AAGT,MAAI,aAAa,WAAW,EAE1B,QAAO,EAAE;AAGX,SAAO,0BAA0B;GAC/B,QAAQ;GACR,QAAQ;GACR,QAAQ;GACT,CAAC;;CAEL,CAAC;;;;;;;;;;;;;;;;;ACtCF,SAAgB,oBACd,SACwB;AACxB,QAAO,2BAA2B,WAAW,EAAE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"enter-rule-CzWOZF_Z.js","names":["regex: RegExp","handler: EnterRuleHandler","stop: boolean","rules: EnterRule[]","command: Command"],"sources":["../src/enter-rule/index.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n getNodeType,\n isTextSelection,\n maybeRun,\n OBJECT_REPLACEMENT_CHARACTER,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport { keydownHandler } from '@prosekit/pm/keymap'\nimport type {\n Attrs,\n NodeType,\n} from '@prosekit/pm/model'\nimport {\n PluginKey,\n ProseMirrorPlugin,\n type Command,\n type EditorState,\n type Transaction,\n} from '@prosekit/pm/state'\nimport type { EditorView } from '@prosekit/pm/view'\n\n/**\n * @public\n *\n * Options for {@link EnterRuleHandler}.\n */\nexport interface EnterRuleHandlerOptions {\n /**\n * The current editor state.\n */\n state: EditorState\n\n /**\n * The start position of the matched text.\n */\n from: number\n\n /**\n * The end position of the matched text.\n */\n to: number\n\n /**\n * The matched result from the regular expression.\n */\n match: RegExpExecArray\n}\n\n/**\n * @public\n */\nexport type EnterRuleHandler = (options: EnterRuleHandlerOptions) => Transaction | null\n\n/**\n * Options for {@link defineEnterRule}.\n *\n * @public\n */\nexport type EnterRuleOptions = {\n /**\n * The regular expression to match against. It should end with `$`.\n */\n regex: RegExp\n\n /**\n * A function to be called when an enter rule is triggered.\n */\n handler: EnterRuleHandler\n\n /**\n * Whether to stop further handlers from being called if this rule is triggered.\n *\n * @default false\n */\n stop?: boolean\n}\n\n/**\n * Options for {@link defineTextBlockEnterRule}.\n *\n * @public\n */\nexport interface TextBlockEnterRuleOptions {\n /**\n * The regular expression to match against. It should end with `$`.\n */\n regex: RegExp\n\n /**\n * The node type to replace the matched text with.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node. If a function is provided, it will be called\n * with the matched result from the regular expression.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * Whether to stop further handlers from being called if this rule is triggered.\n *\n * @default true\n */\n stop?: boolean\n}\n\n/**\n * Defines an enter rule. An enter rule applies when the text directly in front of\n * the cursor matches `regex` and user presses Enter. The `regex` should end\n * with `$`.\n *\n * @param options\n *\n * @public\n */\nexport function defineEnterRule({\n regex,\n handler,\n stop = false,\n}: EnterRuleOptions): PlainExtension {\n const rule: EnterRule = new EnterRule(regex, handler, stop)\n return defineFacetPayload(enterRule, [rule]) as PlainExtension\n}\n\n/**\n * Defines an enter rule that replaces the matched text with a block node.\n *\n * See also {@link defineEnterRule}.\n *\n * @param options\n *\n * @public\n */\nexport function defineTextBlockEnterRule({\n regex,\n type,\n attrs,\n stop = true,\n}: TextBlockEnterRuleOptions): PlainExtension {\n return defineEnterRule({\n regex,\n handler: ({ state, from, to, match }) => {\n const nodeType = getNodeType(state.schema, type)\n const $start = state.doc.resolve(from)\n\n if (\n !$start\n .node(-1)\n .canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)\n ) {\n return null\n }\n\n const nodeAttrs = maybeRun(attrs, match)\n return state.tr\n .delete(from, to)\n .setBlockType(from, from, nodeType, nodeAttrs)\n },\n stop,\n })\n}\n\n/**\n * @internal\n */\nclass EnterRule {\n constructor(\n readonly regex: RegExp,\n readonly handler: EnterRuleHandler,\n readonly stop: boolean,\n ) {}\n}\n\nconst enterRule = defineFacet<EnterRule, PluginPayload>({\n reduce: () => {\n let rules: EnterRule[] = []\n\n const command: Command = (state, dispatch, view) => {\n if (!view) return false\n return execRules(view, rules, dispatch)\n }\n const handler = keydownHandler({ Enter: command })\n const plugin = new ProseMirrorPlugin({\n key: new PluginKey('prosekit-enter-rule'),\n props: { handleKeyDown: handler },\n })\n\n return function reducer(inputs) {\n rules = inputs\n return plugin\n }\n },\n\n parent: pluginFacet,\n})\n\nfunction execRules(\n view: EditorView,\n rules: readonly EnterRule[],\n dispatch?: (tr: Transaction) => void,\n): boolean {\n if (view.composing) return false\n const state = view.state\n const selection = state.selection\n if (!isTextSelection(selection)) return false\n const $cursor = selection.$cursor\n if (!$cursor || $cursor.parent.type.spec.code) return false\n\n const textBefore = $cursor.parent.textBetween(\n Math.max(0, $cursor.parentOffset - MAX_MATCH),\n $cursor.parentOffset,\n null,\n OBJECT_REPLACEMENT_CHARACTER,\n )\n\n for (const rule of rules) {\n rule.regex.lastIndex = 0\n const match = rule.regex.exec(textBefore)\n const tr = match\n && rule.handler({\n state,\n from: $cursor.pos - match[0].length,\n to: $cursor.pos,\n match,\n })\n if (!tr) continue\n dispatch?.(tr)\n\n if (rule.stop) {\n return true\n }\n }\n return false\n}\n\nconst MAX_MATCH = 200\n"],"mappings":";;;;;;;;;;;;;;AAwHA,SAAgB,gBAAgB,EAC9B,OACA,SACA,OAAO,SAC4B;AAEnC,QAAO,mBAAmB,WAAW,CADb,IAAI,UAAU,OAAO,SAAS,KAAK,CAChB,CAAC;;;;;;;;;;;AAY9C,SAAgB,yBAAyB,EACvC,OACA,MACA,OACA,OAAO,QACqC;AAC5C,QAAO,gBAAgB;EACrB;EACA,UAAU,EAAE,OAAO,MAAM,IAAI,YAAY;GACvC,MAAM,WAAW,YAAY,MAAM,QAAQ,KAAK;GAChD,MAAM,SAAS,MAAM,IAAI,QAAQ,KAAK;AAEtC,OACE,CAAC,OACE,KAAK,GAAG,CACR,eAAe,OAAO,MAAM,GAAG,EAAE,OAAO,WAAW,GAAG,EAAE,SAAS,CAEpE,QAAO;GAGT,MAAM,YAAY,SAAS,OAAO,MAAM;AACxC,UAAO,MAAM,GACV,OAAO,MAAM,GAAG,CAChB,aAAa,MAAM,MAAM,UAAU,UAAU;;EAElD;EACD,CAAC;;;;;AAMJ,IAAM,YAAN,MAAgB;CACd,YACE,AAASA,OACT,AAASC,SACT,AAASC,MACT;EAHS;EACA;EACA;;;AAIb,MAAM,YAAY,YAAsC;CACtD,cAAc;EACZ,IAAIC,QAAqB,EAAE;EAE3B,MAAMC,WAAoB,OAAO,UAAU,SAAS;AAClD,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,UAAU,MAAM,OAAO,SAAS;;EAEzC,MAAM,UAAU,eAAe,EAAE,OAAO,SAAS,CAAC;EAClD,MAAM,SAAS,IAAI,kBAAkB;GACnC,KAAK,IAAI,UAAU,sBAAsB;GACzC,OAAO,EAAE,eAAe,SAAS;GAClC,CAAC;AAEF,SAAO,SAAS,QAAQ,QAAQ;AAC9B,WAAQ;AACR,UAAO;;;CAIX,QAAQ;CACT,CAAC;AAEF,SAAS,UACP,MACA,OACA,UACS;AACT,KAAI,KAAK,UAAW,QAAO;CAC3B,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,MAAM;AACxB,KAAI,CAAC,gBAAgB,UAAU,CAAE,QAAO;CACxC,MAAM,UAAU,UAAU;AAC1B,KAAI,CAAC,WAAW,QAAQ,OAAO,KAAK,KAAK,KAAM,QAAO;CAEtD,MAAM,aAAa,QAAQ,OAAO,YAChC,KAAK,IAAI,GAAG,QAAQ,eAAe,UAAU,EAC7C,QAAQ,cACR,MACA,6BACD;AAED,MAAK,MAAM,QAAQ,OAAO;AACxB,OAAK,MAAM,YAAY;EACvB,MAAM,QAAQ,KAAK,MAAM,KAAK,WAAW;EACzC,MAAM,KAAK,SACN,KAAK,QAAQ;GACd;GACA,MAAM,QAAQ,MAAM,MAAM,GAAG;GAC7B,IAAI,QAAQ;GACZ;GACD,CAAC;AACJ,MAAI,CAAC,GAAI;AACT,aAAW,GAAG;AAEd,MAAI,KAAK,KACP,QAAO;;AAGX,QAAO;;AAGT,MAAM,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-DrfcSid-.js","names":["getFiles","facet","getFiles","dropHandler: DropHandler","pasteHandler: PasteHandler"],"sources":["../src/file/helpers.ts","../src/file/file-drop-handler.ts","../src/file/file-paste-handler.ts","../src/file/file-upload.ts"],"sourcesContent":["import type { EditorView } from '@prosekit/pm/view'\n\ntype FileHandler<E extends Event> = (options: {\n view: EditorView\n event: E\n file: File\n}) => boolean | void\n\nfunction handleFile<E extends Event>(\n view: EditorView,\n event: E,\n file: File,\n handlers: FileHandler<E>[],\n) {\n // The last item in `handlers` should has the highest priority.\n for (let i = handlers.length - 1; i >= 0; i--) {\n const handler = handlers[i]\n if (handler({ view, event, file })) {\n return true\n }\n }\n return false\n}\n\nexport function handleEvent<E extends Event>(\n view: EditorView,\n event: E,\n handlers: FileHandler<E>[],\n getFiles: (event: E) => File[],\n): boolean {\n const files = getFiles(event)\n let handled = false\n for (const file of files) {\n if (handleFile(view, event, file, handlers)) {\n handled = true\n }\n }\n return handled\n}\n","import {\n defineFacet,\n defineFacetPayload,\n editorEventFacet,\n type DropHandler,\n type EditorEventPayload,\n type PlainExtension,\n} from '@prosekit/core'\nimport type { EditorView } from '@prosekit/pm/view'\n\nimport { handleEvent } from './helpers'\n\nexport interface FileDropHandlerOptions {\n /**\n * The editor view.\n */\n view: EditorView\n\n /**\n * The event that triggered the drop.\n */\n event: DragEvent\n\n /**\n * The file that was dropped.\n */\n file: File\n\n /**\n * The position of the document where the file was dropped.\n */\n pos: number\n}\n\n/**\n * A function that handles one of the files in a drop event.\n *\n * Returns `true` if the file was handled and thus should not be handled by\n * other handlers.\n */\nexport type FileDropHandler = (\n options: FileDropHandlerOptions,\n) => boolean | void\n\nexport function defineFileDropHandler(\n handler: FileDropHandler,\n): PlainExtension {\n return defineFacetPayload(facet, [handler]) as PlainExtension\n}\n\nfunction getFiles(event: DragEvent) {\n return Array.from(event.dataTransfer?.files ?? [])\n}\n\nconst facet = defineFacet<FileDropHandler, EditorEventPayload>({\n parent: editorEventFacet,\n singleton: true,\n reducer: (handlers: FileDropHandler[]): EditorEventPayload => {\n const dropHandler: DropHandler = (view, event): boolean => {\n const position = view.posAtCoords({ left: event.x, top: event.y })\n if (!position) {\n return false\n }\n const pos = position.inside > 0 ? position.inside : position.pos\n\n return handleEvent<DragEvent>(\n view,\n event,\n handlers.map((handler) => (options) => handler({ ...options, pos })),\n getFiles,\n )\n }\n return ['drop', dropHandler]\n },\n})\n","import {\n defineFacet,\n defineFacetPayload,\n editorEventFacet,\n type EditorEventPayload,\n type PasteHandler,\n type PlainExtension,\n} from '@prosekit/core'\nimport type { EditorView } from '@prosekit/pm/view'\n\nimport { handleEvent } from './helpers'\n\nexport interface FilePasteHandlerOptions {\n /**\n * The editor view.\n */\n view: EditorView\n\n /**\n * The event that triggered the paste.\n */\n event: ClipboardEvent\n\n /**\n * The file that was pasted.\n */\n file: File\n}\n\n/**\n * A function that handles one of the files in a paste event.\n *\n * Returns `true` if the file was handled and thus should not be handled by\n * other handlers.\n */\nexport type FilePasteHandler = (\n options: FilePasteHandlerOptions,\n) => boolean | void\n\nexport function defineFilePasteHandler(\n handler: FilePasteHandler,\n): PlainExtension {\n return defineFacetPayload(facet, [handler]) as PlainExtension\n}\n\nfunction getFiles(event: ClipboardEvent) {\n return Array.from(event.clipboardData?.files ?? [])\n}\n\nconst facet = defineFacet<FilePasteHandler, EditorEventPayload>({\n parent: editorEventFacet,\n singleton: true,\n reducer: (handlers: FilePasteHandler[]): EditorEventPayload => {\n const pasteHandler: PasteHandler = (view, event): boolean => {\n return handleEvent<ClipboardEvent>(view, event, handlers, getFiles)\n }\n return ['paste', pasteHandler]\n },\n})\n","import { ProseKitError } from '@prosekit/core'\n\n/**\n * An interface representing the upload progress.\n */\nexport interface UploadProgress {\n // A number representing the amount of work already performed by the\n // underlying process.\n loaded: number\n // A number representing the total amount of work that the underlying\n // process is in the progress of performing.\n total: number\n}\n\nexport interface UploaderOptions {\n /**\n * The file to be uploaded.\n */\n file: File\n\n /**\n * A callback function that should be called with the upload progress updates.\n */\n onProgress: (progress: UploadProgress) => void\n}\n\n/**\n * The implementation of the actual upload function. You need to implement this\n * function to upload files to your desired destination.\n */\nexport type Uploader<Result> = (options: UploaderOptions) => Promise<Result>\n\n/**\n * A class that represents a upload task.\n */\nexport class UploadTask<Result> {\n /**\n * An [object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL)\n * representing the file to be uploaded. This URL will be revoked once the\n * upload is complete successfully.\n */\n readonly objectURL: string\n\n /**\n * A boolean indicating whether the upload is complete (either successfully or with an error).\n */\n protected done = false\n\n /**\n * If the upload is complete successfully, this will be the result of the upload.\n */\n protected result: Result | undefined\n\n /**\n * If the upload is complete with an error, this will be the error that occurred.\n */\n protected error: Error | undefined\n\n /**\n * A promise that fulfills once the upload is complete, or rejects if an error occurs.\n */\n readonly finished: Promise<Result>\n\n private subscribers: ((progress: UploadProgress) => void)[] = []\n\n /**\n * Creates a new upload task. You can find the upload task by its object URL\n * later using `UploadTask.get()`.\n *\n * @param options - The options for the upload task.\n */\n constructor({ file, uploader }: { file: File; uploader: Uploader<Result> }) {\n this.objectURL = URL.createObjectURL(file)\n this.finished = new Promise((resolve, reject) => {\n const maybePromise = uploader({\n file,\n onProgress: (progress) => {\n for (const subscriber of this.subscribers) {\n subscriber(progress)\n }\n },\n })\n Promise.resolve(maybePromise).then(\n (result) => {\n this.done = true\n URL.revokeObjectURL(this.objectURL)\n this.result = result\n resolve(result)\n },\n (err) => {\n this.done = true\n const error = new ProseKitError('[prosekit] Failed to upload file', { cause: err })\n this.error = error\n reject(error)\n },\n )\n })\n store.set(this.objectURL, this)\n }\n\n /**\n * Subscribes to progress updates. Returns a function to unsubscribe.\n */\n public subscribeProgress(\n callback: (progress: UploadProgress) => void,\n ): VoidFunction {\n this.subscribers.push(callback)\n return () => {\n this.subscribers = this.subscribers.filter(\n (subscriber) => subscriber !== callback,\n )\n }\n }\n\n /**\n * Finds an upload task from the global store by its object URL.\n */\n static get<Result = unknown>(\n objectURL: string,\n ): UploadTask<Result> | undefined {\n return store.get(objectURL) as UploadTask<Result> | undefined\n }\n\n /**\n * Deletes an upload task from the global store by its object URL.\n */\n static delete(objectURL: string): void {\n store.delete(objectURL)\n }\n}\n\nconst store = new Map<string, UploadTask<unknown>>()\n"],"mappings":";;;AAQA,SAAS,WACP,MACA,OACA,MACA,UACA;AAEA,MAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAC7C,MAAM,UAAU,SAAS;AACzB,MAAI,QAAQ;GAAE;GAAM;GAAO;GAAM,CAAC,CAChC,QAAO;;AAGX,QAAO;;AAGT,SAAgB,YACd,MACA,OACA,UACA,YACS;CACT,MAAM,QAAQA,WAAS,MAAM;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,QAAQ,MACjB,KAAI,WAAW,MAAM,OAAO,MAAM,SAAS,CACzC,WAAU;AAGd,QAAO;;;;;ACOT,SAAgB,sBACd,SACgB;AAChB,QAAO,mBAAmBC,SAAO,CAAC,QAAQ,CAAC;;AAG7C,SAASC,WAAS,OAAkB;AAClC,QAAO,MAAM,KAAK,MAAM,cAAc,SAAS,EAAE,CAAC;;AAGpD,MAAMD,UAAQ,YAAiD;CAC7D,QAAQ;CACR,WAAW;CACX,UAAU,aAAoD;EAC5D,MAAME,eAA4B,MAAM,UAAmB;GACzD,MAAM,WAAW,KAAK,YAAY;IAAE,MAAM,MAAM;IAAG,KAAK,MAAM;IAAG,CAAC;AAClE,OAAI,CAAC,SACH,QAAO;GAET,MAAM,MAAM,SAAS,SAAS,IAAI,SAAS,SAAS,SAAS;AAE7D,UAAO,YACL,MACA,OACA,SAAS,KAAK,aAAa,YAAY,QAAQ;IAAE,GAAG;IAAS;IAAK,CAAC,CAAC,EACpED,WACD;;AAEH,SAAO,CAAC,QAAQ,YAAY;;CAE/B,CAAC;;;;ACnCF,SAAgB,uBACd,SACgB;AAChB,QAAO,mBAAmB,OAAO,CAAC,QAAQ,CAAC;;AAG7C,SAAS,SAAS,OAAuB;AACvC,QAAO,MAAM,KAAK,MAAM,eAAe,SAAS,EAAE,CAAC;;AAGrD,MAAM,QAAQ,YAAkD;CAC9D,QAAQ;CACR,WAAW;CACX,UAAU,aAAqD;EAC7D,MAAME,gBAA8B,MAAM,UAAmB;AAC3D,UAAO,YAA4B,MAAM,OAAO,UAAU,SAAS;;AAErE,SAAO,CAAC,SAAS,aAAa;;CAEjC,CAAC;;;;;;;ACvBF,IAAa,aAAb,MAAgC;;;;;;;CAoC9B,YAAY,EAAE,MAAM,YAAwD;cAzB3D;qBAiB6C,EAAE;AAS9D,OAAK,YAAY,IAAI,gBAAgB,KAAK;AAC1C,OAAK,WAAW,IAAI,SAAS,SAAS,WAAW;GAC/C,MAAM,eAAe,SAAS;IAC5B;IACA,aAAa,aAAa;AACxB,UAAK,MAAM,cAAc,KAAK,YAC5B,YAAW,SAAS;;IAGzB,CAAC;AACF,WAAQ,QAAQ,aAAa,CAAC,MAC3B,WAAW;AACV,SAAK,OAAO;AACZ,QAAI,gBAAgB,KAAK,UAAU;AACnC,SAAK,SAAS;AACd,YAAQ,OAAO;OAEhB,QAAQ;AACP,SAAK,OAAO;IACZ,MAAM,QAAQ,IAAI,cAAc,oCAAoC,EAAE,OAAO,KAAK,CAAC;AACnF,SAAK,QAAQ;AACb,WAAO,MAAM;KAEhB;IACD;AACF,QAAM,IAAI,KAAK,WAAW,KAAK;;;;;CAMjC,AAAO,kBACL,UACc;AACd,OAAK,YAAY,KAAK,SAAS;AAC/B,eAAa;AACX,QAAK,cAAc,KAAK,YAAY,QACjC,eAAe,eAAe,SAChC;;;;;;CAOL,OAAO,IACL,WACgC;AAChC,SAAO,MAAM,IAAI,UAAU;;;;;CAM7B,OAAO,OAAO,WAAyB;AACrC,QAAM,OAAO,UAAU;;;AAI3B,MAAM,wBAAQ,IAAI,KAAkC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-oIc1a2f2.d.ts","names":[],"sources":["../src/file/file-drop-handler.ts","../src/file/file-paste-handler.ts","../src/file/file-upload.ts"],"sourcesContent":[],"mappings":";;;;UAYiB,sBAAA;;AAAjB;;EASS,IAAA,EALD,UAKC;EAKD;;AAcR;EAIgB,KAAA,EAvBP,SAuBO;;;;EChCC,IAAA,EDcT,ICdS;EAIT;;;EAUI,GAAA,EAAA,MAAA;AASZ;AAIA;;;;AClCA;AASA;AAgBY,KFUA,eAAA,GEVQ,CAAA,OAAA,EFWT,sBEXS,EAAA,GAAA,OAAA,GAAA,IAAA;AAAqB,iBFczB,qBAAA,CEdyB,OAAA,EFe9B,eEf8B,CAAA,EFgBtC,cEhBsC;;;UDlBxB,uBAAA;;ADAjB;;EASS,IAAA,ECLD,UDKC;EAKD;;AAcR;EAIgB,KAAA,ECvBP,cDuBO;;;;EChCC,IAAA,EAcT,IAdS;;;;;AAuBjB;AAIA;;KAJY,gBAAA,aACD;iBAGK,sBAAA,UACL,mBACR;;;;;;UCpCc,cAAA;EFOA,MAAA,EAAA,MAAA;EAIT,KAAA,EAAA,MAAA;;AAUA,UEZS,eAAA,CFYT;EAAI;AAcZ;AAIA;QE1BQ;;;ADNR;EAIQ,UAAA,EAAA,CAAA,QAAA,ECOiB,cDPjB,EAAA,GAAA,IAAA;;;;AAmBR;AAIA;KCTY,6BAA6B,oBAAoB,QAAQ;;;AAzBrE;AASiB,cAqBJ,UArBmB,CAAA,MAIxB,CAAA,CAAA;EAYI;;;;;EAKC,SAAA,SAAU,EAAA,MAAA;EAgBH;;;EAUC,UAAA,IAAA,EAAA,OAAA;EAUL;;;EAAmD,UAAA,MAAA,EApB/C,MAoB+C,GAAA,SAAA;EAAT;;;EAgD1C,UAAA,KAAA,EA/DG,KA+DH,GAAA,SAAA;EAAX;;;qBA1DgB,QAAQ;;;;;;;;;;;;UAUa;cAAgB,SAAS;;;;;yCAiC1C,0BACpB;;;;mDAcA,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"input-rule-dmsb3j6w.js","names":[],"sources":["../src/input-rule/index.ts"],"sourcesContent":["import {\n defineFacet,\n defineFacetPayload,\n getMarkType,\n getNodeType,\n isMarkAbsent,\n maybeRun,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport {\n InputRule,\n inputRules,\n textblockTypeInputRule,\n wrappingInputRule,\n} from '@prosekit/pm/inputrules'\nimport type {\n Attrs,\n MarkType,\n NodeType,\n ProseMirrorNode,\n Schema,\n} from '@prosekit/pm/model'\nimport type { Plugin } from '@prosekit/pm/state'\n\n/**\n * Defines an input rule extension.\n *\n * @param rule - The ProseMirror input rule to add.\n *\n * @public\n */\nexport function defineInputRule(rule: InputRule): PlainExtension {\n return defineInputRuleFacetPayload(() => rule)\n}\n\n/**\n * Options for {@link defineMarkInputRule}.\n *\n * @public\n */\nexport interface MarkInputRuleOptions {\n /**\n * The regular expression to match against, which should end with `$` and has\n * exactly one capture group. All other matched text outside the capture group\n * will be deleted.\n */\n regex: RegExp\n\n /**\n * The type of mark to set.\n */\n type: string | MarkType\n\n /**\n * Attributes to set on the mark.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * Whether this rule should fire inside marks marked as [code](https://prosemirror.net/docs/ref/#model.MarkSpec.code).\n *\n * @default `false`\n */\n inCodeMark?: boolean\n}\n\n/**\n * @internal\n */\nexport function createMarkInputRule({\n regex,\n type,\n attrs = null,\n inCodeMark = false,\n}: MarkInputRuleOptions): InputRule {\n const rule = new InputRule(regex, (state, match, start, end) => {\n const { tr, schema } = state\n const [fullText, markText] = match\n\n if (!markText) {\n return null\n }\n\n const markStart = start + fullText.indexOf(markText)\n const markEnd = markStart + markText.length\n\n if (!(start <= markStart && markStart < markEnd && markEnd <= end)) {\n // Incorrect regex.\n return null\n }\n\n const markType = getMarkType(schema, type)\n const mark = markType.create(maybeRun(attrs, match))\n\n if (!isMarkAbsent(tr.doc, markStart, markEnd, markType, attrs)) {\n // The mark is already active.\n return null\n }\n\n const initialStoredMarks = tr.storedMarks ?? []\n\n tr.addMark(markStart, markEnd, mark)\n\n if (markEnd < end) {\n tr.delete(markEnd, end)\n }\n if (start < markStart) {\n tr.delete(start, markStart)\n }\n\n // Make sure not to reactivate any marks which had previously been\n // deactivated. By keeping track of the initial stored marks we are able to\n // discard any unintended consequences of deleting text and adding it again.\n tr.setStoredMarks(initialStoredMarks)\n\n return tr\n }, { inCodeMark })\n\n return rule\n}\n\n/**\n * Defines an input rule for automatically adding inline marks when a given\n * pattern is typed.\n *\n * @public\n */\nexport function defineMarkInputRule(\n options: MarkInputRuleOptions,\n): PlainExtension {\n return defineInputRule(createMarkInputRule(options))\n}\n\n/**\n * Defines an input rule that changes the type of a textblock when the matched\n * text is typed into it.\n *\n * See also [textblockTypeInputRule](https://prosemirror.net/docs/ref/#inputrules.textblockTypeInputRule)\n *\n * @param options\n *\n * @public\n */\nexport function defineTextBlockInputRule({\n regex,\n type,\n attrs,\n}: {\n /**\n * The regular expression to match against, which should end with `$`. It\n * usually also starts with `^` to that it is only matched at the start of a\n * textblock.\n */\n regex: RegExp\n\n /**\n * The node type to replace the matched text with.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n}): PlainExtension {\n return defineInputRuleFacetPayload(({ schema }): InputRule => {\n const nodeType = getNodeType(schema, type)\n return textblockTypeInputRule(regex, nodeType, attrs)\n })\n}\n\n/**\n * Defines an input rule for automatically wrapping a textblock when a given\n * string is typed.\n *\n * See also [wrappingInputRule](https://prosemirror.net/docs/ref/#inputrules.wrappingInputRule)\n *\n * @param options\n *\n * @public\n */\nexport function defineWrappingInputRule({\n regex,\n type,\n attrs,\n join,\n}: {\n /**\n * The regular expression to match against, which should end with `$`. It\n * usually also starts with `^` to that it is only matched at the start of a\n * textblock.\n */\n regex: RegExp\n\n /**\n * The type of node to wrap in.\n */\n type: string | NodeType\n\n /**\n * Attributes to set on the node.\n */\n attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null)\n\n /**\n * By default, if there's a node with the same type above the newly wrapped\n * node, the rule will try to\n * [join](https://prosemirror.net/docs/ref/#transform.Transform.join) those\n * two nodes. You can pass a join predicate, which takes a regular expression\n * match and the node before the wrapped node, and can return a boolean to\n * indicate whether a join should happen.\n */\n join?: (match: RegExpMatchArray, node: ProseMirrorNode) => boolean\n}): PlainExtension {\n return defineInputRuleFacetPayload(({ schema }): InputRule => {\n const nodeType = getNodeType(schema, type)\n return wrappingInputRule(regex, nodeType, attrs, join)\n })\n}\n\nfunction defineInputRuleFacetPayload(input: InputRulePayload): PlainExtension {\n return defineFacetPayload(inputRuleFacet, [input]) as PlainExtension\n}\n\ntype InputRulePayload = (context: { schema: Schema }) => InputRule\n\nconst inputRuleFacet = defineFacet<InputRulePayload, PluginPayload>({\n reducer: (inputs: InputRulePayload[]): PluginPayload => {\n return (context): Plugin[] => {\n const rules: InputRule[] = inputs.flatMap((callback) => callback(context))\n return [inputRules({ rules })]\n }\n },\n parent: pluginFacet,\n})\n"],"mappings":";;;;;;;;;;;AAiCA,SAAgB,gBAAgB,MAAiC;AAC/D,QAAO,kCAAkC,KAAK;;;;;AAqChD,SAAgB,oBAAoB,EAClC,OACA,MACA,QAAQ,MACR,aAAa,SACqB;AA4ClC,QA3Ca,IAAI,UAAU,QAAQ,OAAO,OAAO,OAAO,QAAQ;EAC9D,MAAM,EAAE,IAAI,WAAW;EACvB,MAAM,CAAC,UAAU,YAAY;AAE7B,MAAI,CAAC,SACH,QAAO;EAGT,MAAM,YAAY,QAAQ,SAAS,QAAQ,SAAS;EACpD,MAAM,UAAU,YAAY,SAAS;AAErC,MAAI,EAAE,SAAS,aAAa,YAAY,WAAW,WAAW,KAE5D,QAAO;EAGT,MAAM,WAAW,YAAY,QAAQ,KAAK;EAC1C,MAAM,OAAO,SAAS,OAAO,SAAS,OAAO,MAAM,CAAC;AAEpD,MAAI,CAAC,aAAa,GAAG,KAAK,WAAW,SAAS,UAAU,MAAM,CAE5D,QAAO;EAGT,MAAM,qBAAqB,GAAG,eAAe,EAAE;AAE/C,KAAG,QAAQ,WAAW,SAAS,KAAK;AAEpC,MAAI,UAAU,IACZ,IAAG,OAAO,SAAS,IAAI;AAEzB,MAAI,QAAQ,UACV,IAAG,OAAO,OAAO,UAAU;AAM7B,KAAG,eAAe,mBAAmB;AAErC,SAAO;IACN,EAAE,YAAY,CAAC;;;;;;;;AAWpB,SAAgB,oBACd,SACgB;AAChB,QAAO,gBAAgB,oBAAoB,QAAQ,CAAC;;;;;;;;;;;;AAatD,SAAgB,yBAAyB,EACvC,OACA,MACA,SAkBiB;AACjB,QAAO,6BAA6B,EAAE,aAAwB;AAE5D,SAAO,uBAAuB,OADb,YAAY,QAAQ,KAAK,EACK,MAAM;GACrD;;;;;;;;;;;;AAaJ,SAAgB,wBAAwB,EACtC,OACA,MACA,OACA,QA4BiB;AACjB,QAAO,6BAA6B,EAAE,aAAwB;AAE5D,SAAO,kBAAkB,OADR,YAAY,QAAQ,KAAK,EACA,OAAO,KAAK;GACtD;;AAGJ,SAAS,4BAA4B,OAAyC;AAC5E,QAAO,mBAAmB,gBAAgB,CAAC,MAAM,CAAC;;AAKpD,MAAM,iBAAiB,YAA6C;CAClE,UAAU,WAA8C;AACtD,UAAQ,YAAsB;AAE5B,UAAO,CAAC,WAAW,EAAE,OADM,OAAO,SAAS,aAAa,SAAS,QAAQ,CAAC,EAC9C,CAAC,CAAC;;;CAGlC,QAAQ;CACT,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mark-rule-BcLB4Uv2.js","names":["stack: Array<[start: number, node: ProseMirrorNode]>","ranges: Array<[number, number]>","start","ranges: MarkRange[]","result: MarkRange[]","mark","tr","toRemove: MarkRange[]","toCreate: MarkRange[]","rules: MarkRuleOptions[]"],"sources":["../src/mark-rule/range.ts","../src/mark-rule/apply.ts","../src/mark-rule/mark-rule.ts"],"sourcesContent":["import type {\n ProseMirrorNode,\n ResolvedPos,\n} from '@prosekit/pm/model'\nimport type {\n EditorState,\n Transaction,\n} from '@prosekit/pm/state'\nimport type { ProsemirrorNode } from 'prosemirror-flat-list'\n\nfunction getSpanTextRanges($from: ResolvedPos, $to: ResolvedPos) {\n const nodeRange = $from.blockRange($to)\n if (!nodeRange) {\n return []\n }\n\n const stack: Array<[start: number, node: ProseMirrorNode]> = []\n let start = nodeRange.start\n\n for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {\n const child = nodeRange.parent.child(i)\n stack.push([start, child])\n start += child.nodeSize\n }\n\n const ranges: Array<[number, number]> = []\n\n while (stack.length > 0) {\n const [start, node] = stack.pop()!\n if (node.type.spec.code) {\n continue\n }\n\n if (node.type.isTextblock) {\n ranges.push([start + 1, start + 1 + node.content.size])\n continue\n }\n\n node.forEach((child, offset) => {\n stack.push([start + offset + 1, child])\n })\n }\n\n return ranges\n}\n\nfunction getInlineTextRange(\n $from: ResolvedPos,\n $to: ResolvedPos,\n): [number, number] {\n return [$from.start(), $to.end()]\n}\n\nfunction getTextRanges(\n doc: ProsemirrorNode,\n from: number,\n to: number,\n): Array<[number, number]> {\n const $from = doc.resolve(from)\n const $to = doc.resolve(to)\n\n if ($from.sameParent($to) && $from.parent.isTextblock) {\n return [getInlineTextRange($from, $to)]\n } else {\n const nodeRange = $from.blockRange($to)\n if (!nodeRange) {\n return []\n }\n\n return getSpanTextRanges($from, $to)\n }\n}\n\nfunction getMapRange(\n transactions: readonly Transaction[],\n oldState: EditorState,\n newState: EditorState,\n) {\n let lo = oldState.selection.from\n let hi = oldState.selection.to\n\n for (const tr of transactions) {\n for (const map of tr.mapping.maps) {\n lo = map.map(lo)\n hi = map.map(hi)\n\n map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {\n lo = Math.min(lo, hi, newStart)\n hi = Math.max(lo, hi, newEnd)\n })\n }\n }\n\n lo = Math.min(lo, hi, newState.selection.from)\n hi = Math.min(lo, hi, newState.selection.to)\n\n return [lo, hi] as const\n}\n\nexport function getCheckRanges(\n transactions: readonly Transaction[],\n oldState: EditorState,\n newState: EditorState,\n): Array<[number, number]> {\n const [from, to] = getMapRange(transactions, oldState, newState)\n return getTextRanges(newState.doc, from, to)\n}\n","import {\n getMarkType,\n maybeRun,\n OBJECT_REPLACEMENT_CHARACTER,\n} from '@prosekit/core'\nimport type {\n Mark,\n ProseMirrorNode,\n} from '@prosekit/pm/model'\nimport type {\n EditorState,\n Transaction,\n} from '@prosekit/pm/state'\n\nimport { getCheckRanges } from './range'\nimport type { MarkRuleOptions } from './types'\n\ntype MarkRange = [from: number, to: number, mark: Mark]\n\nfunction getExpectedMarkings(\n rules: MarkRuleOptions[],\n doc: ProseMirrorNode,\n from: number,\n to: number,\n): MarkRange[] {\n const text = doc.textBetween(from, to, null, OBJECT_REPLACEMENT_CHARACTER)\n const ranges: MarkRange[] = []\n\n for (const rule of rules) {\n rule.regex.lastIndex = 0\n const matches = text.matchAll(rule.regex)\n const markType = getMarkType(doc.type.schema, rule.type)\n\n for (const match of matches) {\n const index = match.index\n if (index == null) continue\n const attrs = maybeRun(rule.attrs, match)\n const mark = markType.create(attrs)\n ranges.push([from + index, from + index + match[0].length, mark])\n }\n }\n\n // Sort by start position. If start positions are equal, the longer match\n // should be prioritized.\n ranges.sort((a, b) => a[0] - b[0] || b[1] - a[1])\n\n // Remove overlapped marks.\n const result: MarkRange[] = []\n let freeIndex = 0\n\n for (const range of ranges) {\n if (range[0] >= freeIndex) {\n result.push(range)\n freeIndex = range[1]\n }\n }\n\n return result\n}\n\nfunction getReceivedMarkings(\n rules: MarkRuleOptions[],\n doc: ProseMirrorNode,\n from: number,\n to: number,\n): MarkRange[] {\n const result: MarkRange[] = []\n const schema = doc.type.schema\n const markTypes = rules.map((rule) => getMarkType(schema, rule.type))\n\n doc.nodesBetween(from, to, (node, pos) => {\n if (!node.isInline) {\n return\n }\n\n for (const markType of markTypes) {\n const mark = node.marks.find((mark) => mark.type === markType)\n if (mark) {\n result.push([pos, pos + node.nodeSize, mark])\n }\n }\n })\n return result\n}\n\nfunction markRangeEquals(a: MarkRange, b: MarkRange): boolean {\n return a[0] === b[0] && a[1] === b[1] && a[2].eq(b[2])\n}\n\nfunction markRangeDiffs(a: MarkRange[], b: MarkRange[]): MarkRange[] {\n return a.filter((x) => !b.some((y) => markRangeEquals(x, y)))\n}\n\nexport function applyMarkRules(\n rules: MarkRuleOptions[],\n transactions: readonly Transaction[],\n oldState: EditorState,\n newState: EditorState,\n): Transaction | null {\n if (transactions.length === 0 || transactions.every((tr) => !tr.docChanged)) {\n return null\n }\n\n const ranges = getCheckRanges(transactions, oldState, newState)\n\n const toRemove: MarkRange[] = []\n const toCreate: MarkRange[] = []\n\n for (const [from, to] of ranges) {\n const expected = getExpectedMarkings(rules, newState.doc, from, to)\n const received = getReceivedMarkings(rules, newState.doc, from, to)\n\n toRemove.push(...markRangeDiffs(received, expected))\n toCreate.push(...markRangeDiffs(expected, received))\n }\n\n if (toCreate.length === 0 && toRemove.length === 0) {\n return null\n }\n\n const tr = newState.tr\n for (const [from, to, mark] of toRemove) {\n tr.removeMark(from, to, mark)\n }\n for (const [from, to, mark] of toCreate) {\n tr.addMark(from, to, mark)\n }\n return tr\n}\n","import {\n defineFacet,\n defineFacetPayload,\n pluginFacet,\n type PlainExtension,\n type PluginPayload,\n} from '@prosekit/core'\nimport {\n PluginKey,\n ProseMirrorPlugin,\n type EditorState,\n type Transaction,\n} from '@prosekit/pm/state'\n\nimport { applyMarkRules } from './apply'\nimport type { MarkRuleOptions } from './types'\n\n/**\n * A mark rule is something that can automatically apply marks to text if it\n * matches a certain pattern, and remove them if it doesn't match anymore.\n */\nexport function defineMarkRule(options: MarkRuleOptions): PlainExtension {\n return defineFacetPayload(markRuleFacet, [options]) as PlainExtension\n}\n\nconst markRuleFacet = defineFacet<MarkRuleOptions, PluginPayload>({\n reduce: () => {\n let rules: MarkRuleOptions[] = []\n\n const plugin = new ProseMirrorPlugin({\n key: new PluginKey('prosekit-mark-rule'),\n appendTransaction: (\n transactions: readonly Transaction[],\n oldState: EditorState,\n newState: EditorState,\n ) => {\n return applyMarkRules(rules, transactions, oldState, newState)\n },\n })\n\n return function reducer(input) {\n rules = input\n return plugin\n }\n },\n\n parent: pluginFacet,\n})\n"],"mappings":"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"}