@prosekit/extensions 0.12.2 → 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 (324) 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/list/style.css +5 -5
  12. package/dist/list/style.css.map +1 -1
  13. package/dist/{mark-rule-BcLB4Uv2.js → mark-rule-CYe8zk4q.js} +6 -6
  14. package/dist/mark-rule-CYe8zk4q.js.map +1 -0
  15. package/dist/{paste-rule-pVb4sqvJ.js → paste-rule-BaDghcaU.js} +7 -7
  16. package/dist/paste-rule-BaDghcaU.js.map +1 -0
  17. package/dist/prosekit-extensions-autocomplete.d.ts +11 -3
  18. package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -1
  19. package/dist/prosekit-extensions-autocomplete.js +171 -60
  20. package/dist/prosekit-extensions-autocomplete.js.map +1 -1
  21. package/dist/prosekit-extensions-background-color.d.ts +62 -0
  22. package/dist/prosekit-extensions-background-color.d.ts.map +1 -0
  23. package/dist/prosekit-extensions-background-color.js +76 -0
  24. package/dist/prosekit-extensions-background-color.js.map +1 -0
  25. package/dist/prosekit-extensions-blockquote.d.ts.map +1 -1
  26. package/dist/prosekit-extensions-blockquote.js +2 -2
  27. package/dist/prosekit-extensions-blockquote.js.map +1 -1
  28. package/dist/prosekit-extensions-bold.d.ts.map +1 -1
  29. package/dist/prosekit-extensions-bold.js +1 -1
  30. package/dist/prosekit-extensions-bold.js.map +1 -1
  31. package/dist/prosekit-extensions-code-block.d.ts +1 -1
  32. package/dist/prosekit-extensions-code-block.d.ts.map +1 -1
  33. package/dist/prosekit-extensions-code-block.js +4 -4
  34. package/dist/prosekit-extensions-code-block.js.map +1 -1
  35. package/dist/prosekit-extensions-code.d.ts.map +1 -1
  36. package/dist/prosekit-extensions-code.js +1 -1
  37. package/dist/prosekit-extensions-code.js.map +1 -1
  38. package/dist/prosekit-extensions-commit.d.ts +0 -1
  39. package/dist/prosekit-extensions-commit.d.ts.map +1 -1
  40. package/dist/prosekit-extensions-commit.js +1 -1
  41. package/dist/prosekit-extensions-commit.js.map +1 -1
  42. package/dist/prosekit-extensions-doc.d.ts +0 -1
  43. package/dist/prosekit-extensions-doc.d.ts.map +1 -1
  44. package/dist/prosekit-extensions-doc.js.map +1 -1
  45. package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -1
  46. package/dist/prosekit-extensions-drop-cursor.js.map +1 -1
  47. package/dist/prosekit-extensions-drop-indicator.d.ts +0 -1
  48. package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -1
  49. package/dist/prosekit-extensions-drop-indicator.js +1 -1
  50. package/dist/prosekit-extensions-enter-rule.d.ts +0 -1
  51. package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -1
  52. package/dist/prosekit-extensions-enter-rule.js +1 -1
  53. package/dist/prosekit-extensions-file.d.ts +1 -1
  54. package/dist/prosekit-extensions-file.js +1 -1
  55. package/dist/prosekit-extensions-gap-cursor.d.ts +0 -1
  56. package/dist/prosekit-extensions-gap-cursor.d.ts.map +1 -1
  57. package/dist/prosekit-extensions-gap-cursor.js.map +1 -1
  58. package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
  59. package/dist/prosekit-extensions-hard-break.js.map +1 -1
  60. package/dist/prosekit-extensions-heading.d.ts.map +1 -1
  61. package/dist/prosekit-extensions-heading.js +7 -7
  62. package/dist/prosekit-extensions-heading.js.map +1 -1
  63. package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -1
  64. package/dist/prosekit-extensions-horizontal-rule.js +1 -1
  65. package/dist/prosekit-extensions-horizontal-rule.js.map +1 -1
  66. package/dist/prosekit-extensions-image.d.ts +9 -2
  67. package/dist/prosekit-extensions-image.d.ts.map +1 -1
  68. package/dist/prosekit-extensions-image.js +17 -4
  69. package/dist/prosekit-extensions-image.js.map +1 -1
  70. package/dist/prosekit-extensions-input-rule.d.ts +0 -1
  71. package/dist/prosekit-extensions-input-rule.d.ts.map +1 -1
  72. package/dist/prosekit-extensions-input-rule.js +1 -1
  73. package/dist/prosekit-extensions-italic.d.ts.map +1 -1
  74. package/dist/prosekit-extensions-italic.js +1 -1
  75. package/dist/prosekit-extensions-italic.js.map +1 -1
  76. package/dist/prosekit-extensions-link.d.ts +0 -1
  77. package/dist/prosekit-extensions-link.d.ts.map +1 -1
  78. package/dist/prosekit-extensions-link.js +4 -4
  79. package/dist/prosekit-extensions-link.js.map +1 -1
  80. package/dist/prosekit-extensions-list.d.ts +0 -1
  81. package/dist/prosekit-extensions-list.d.ts.map +1 -1
  82. package/dist/prosekit-extensions-list.js +3 -3
  83. package/dist/prosekit-extensions-list.js.map +1 -1
  84. package/dist/prosekit-extensions-loro.d.ts +16 -17
  85. package/dist/prosekit-extensions-loro.d.ts.map +1 -1
  86. package/dist/prosekit-extensions-loro.js +14 -7
  87. package/dist/prosekit-extensions-loro.js.map +1 -1
  88. package/dist/prosekit-extensions-mark-rule.d.ts +0 -1
  89. package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -1
  90. package/dist/prosekit-extensions-mark-rule.js +1 -1
  91. package/dist/prosekit-extensions-mention.d.ts.map +1 -1
  92. package/dist/prosekit-extensions-mention.js.map +1 -1
  93. package/dist/prosekit-extensions-mod-click-prevention.d.ts +0 -1
  94. package/dist/prosekit-extensions-mod-click-prevention.d.ts.map +1 -1
  95. package/dist/prosekit-extensions-mod-click-prevention.js.map +1 -1
  96. package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
  97. package/dist/prosekit-extensions-paragraph.js +1 -1
  98. package/dist/prosekit-extensions-paragraph.js.map +1 -1
  99. package/dist/prosekit-extensions-paste-rule.d.ts +0 -1
  100. package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -1
  101. package/dist/prosekit-extensions-paste-rule.js +1 -1
  102. package/dist/prosekit-extensions-placeholder.d.ts.map +1 -1
  103. package/dist/prosekit-extensions-placeholder.js +3 -4
  104. package/dist/prosekit-extensions-placeholder.js.map +1 -1
  105. package/dist/prosekit-extensions-readonly.d.ts +0 -1
  106. package/dist/prosekit-extensions-readonly.d.ts.map +1 -1
  107. package/dist/prosekit-extensions-readonly.js.map +1 -1
  108. package/dist/prosekit-extensions-search.d.ts +0 -1
  109. package/dist/prosekit-extensions-search.d.ts.map +1 -1
  110. package/dist/prosekit-extensions-search.js.map +1 -1
  111. package/dist/prosekit-extensions-strike.d.ts +0 -1
  112. package/dist/prosekit-extensions-strike.d.ts.map +1 -1
  113. package/dist/prosekit-extensions-strike.js +3 -3
  114. package/dist/prosekit-extensions-strike.js.map +1 -1
  115. package/dist/prosekit-extensions-table.d.ts.map +1 -1
  116. package/dist/prosekit-extensions-table.js +1 -2
  117. package/dist/prosekit-extensions-text-align.d.ts +0 -1
  118. package/dist/prosekit-extensions-text-align.d.ts.map +1 -1
  119. package/dist/prosekit-extensions-text-align.js +6 -6
  120. package/dist/prosekit-extensions-text-align.js.map +1 -1
  121. package/dist/prosekit-extensions-text-color.d.ts +62 -0
  122. package/dist/prosekit-extensions-text-color.d.ts.map +1 -0
  123. package/dist/prosekit-extensions-text-color.js +76 -0
  124. package/dist/prosekit-extensions-text-color.js.map +1 -0
  125. package/dist/prosekit-extensions-text.d.ts +0 -1
  126. package/dist/prosekit-extensions-text.d.ts.map +1 -1
  127. package/dist/prosekit-extensions-text.js.map +1 -1
  128. package/dist/prosekit-extensions-underline.d.ts +0 -1
  129. package/dist/prosekit-extensions-underline.d.ts.map +1 -1
  130. package/dist/prosekit-extensions-virtual-selection.d.ts +0 -1
  131. package/dist/prosekit-extensions-virtual-selection.d.ts.map +1 -1
  132. package/dist/prosekit-extensions-virtual-selection.js.map +1 -1
  133. package/dist/prosekit-extensions-yjs.d.ts +9 -2
  134. package/dist/prosekit-extensions-yjs.d.ts.map +1 -1
  135. package/dist/prosekit-extensions-yjs.js +1 -1
  136. package/dist/prosekit-extensions-yjs.js.map +1 -1
  137. package/dist/{shiki-highlighter-chunk-rkzofy4z.d.ts → shiki-highlighter-chunk-DNNm2Vow.d.ts} +1 -1
  138. package/dist/shiki-highlighter-chunk-DNNm2Vow.d.ts.map +1 -0
  139. package/dist/shiki-highlighter-chunk.d.ts +1 -1
  140. package/dist/shiki-highlighter-chunk.js.map +1 -1
  141. package/dist/{table-BRDh_9mG.js → table-4oHfV-Ql.js} +2 -2
  142. package/dist/table-4oHfV-Ql.js.map +1 -0
  143. package/package.json +33 -18
  144. package/src/autocomplete/autocomplete-helpers.ts +19 -14
  145. package/src/autocomplete/autocomplete-plugin.ts +260 -126
  146. package/src/autocomplete/autocomplete-rule.ts +3 -3
  147. package/src/autocomplete/autocomplete.spec.ts +244 -40
  148. package/src/autocomplete/autocomplete.ts +9 -7
  149. package/src/background-color/background-color-commands.spec.ts +71 -0
  150. package/src/background-color/background-color-commands.ts +35 -0
  151. package/src/background-color/background-color-spec.spec.ts +286 -0
  152. package/src/background-color/background-color-spec.ts +58 -0
  153. package/src/background-color/background-color.ts +21 -0
  154. package/src/background-color/index.ts +8 -0
  155. package/src/blockquote/blockquote-commands.ts +1 -7
  156. package/src/blockquote/blockquote-keymap.spec.ts +5 -9
  157. package/src/blockquote/blockquote-keymap.ts +2 -7
  158. package/src/blockquote/blockquote-spec.ts +1 -4
  159. package/src/blockquote/blockquote.ts +3 -12
  160. package/src/blockquote/index.ts +3 -12
  161. package/src/bold/bold-commands.ts +1 -5
  162. package/src/bold/bold-input-rule.spec.ts +1 -5
  163. package/src/bold/bold-input-rule.ts +1 -4
  164. package/src/bold/bold-keymap.ts +1 -5
  165. package/src/bold/bold-spec.ts +1 -4
  166. package/src/bold/bold.ts +3 -12
  167. package/src/bold/index.ts +3 -12
  168. package/src/code/code-commands.ts +1 -5
  169. package/src/code/code-input-rule.ts +1 -4
  170. package/src/code/code-keymap.ts +1 -5
  171. package/src/code/code-spec.ts +1 -4
  172. package/src/code/code.ts +3 -12
  173. package/src/code/index.ts +3 -12
  174. package/src/code-block/code-block-commands.ts +1 -8
  175. package/src/code-block/code-block-highlight.ts +2 -8
  176. package/src/code-block/code-block-keymap.ts +2 -9
  177. package/src/code-block/code-block-shiki.ts +1 -4
  178. package/src/code-block/code-block-spec.spec.ts +4 -11
  179. package/src/code-block/code-block-spec.ts +1 -4
  180. package/src/code-block/code-block.ts +4 -16
  181. package/src/code-block/index.ts +5 -21
  182. package/src/code-block/shiki-highlighter-chunk.ts +1 -7
  183. package/src/code-block/shiki-highlighter.ts +1 -4
  184. package/src/code-block/shiki-parser.ts +1 -4
  185. package/src/commit/index.ts +7 -36
  186. package/src/doc/index.ts +1 -4
  187. package/src/drop-cursor/drop-cursor.ts +1 -4
  188. package/src/drop-cursor/index.ts +1 -5
  189. package/src/drop-indicator/drop-indicator-facet.ts +12 -21
  190. package/src/drop-indicator/index.ts +1 -5
  191. package/src/enter-rule/index.ts +2 -11
  192. package/src/file/file-paste-handler.spec.ts +3 -16
  193. package/src/file/index.ts +3 -16
  194. package/src/gap-cursor/gap-cursor.ts +1 -4
  195. package/src/gap-cursor/index.ts +1 -4
  196. package/src/hard-break/hard-break-commands.ts +1 -5
  197. package/src/hard-break/hard-break-keymap.spec.ts +6 -12
  198. package/src/hard-break/hard-break-keymap.ts +1 -4
  199. package/src/hard-break/hard-break-spec.ts +1 -4
  200. package/src/hard-break/hard-break.ts +3 -12
  201. package/src/hard-break/index.ts +3 -12
  202. package/src/heading/heading-commands.ts +1 -7
  203. package/src/heading/heading-keymap.spec.ts +8 -12
  204. package/src/heading/heading-keymap.ts +7 -14
  205. package/src/heading/heading-spec.ts +1 -4
  206. package/src/heading/heading.ts +3 -12
  207. package/src/heading/index.ts +3 -12
  208. package/src/horizontal-rule/horizontal-rule-commands.spec.ts +1 -5
  209. package/src/horizontal-rule/horizontal-rule-commands.ts +2 -9
  210. package/src/horizontal-rule/horizontal-rule-input-rule.spec.ts +5 -9
  211. package/src/horizontal-rule/horizontal-rule-input-rule.ts +1 -5
  212. package/src/horizontal-rule/horizontal-rule-spec.ts +1 -4
  213. package/src/horizontal-rule/horizontal-rule.ts +3 -12
  214. package/src/horizontal-rule/index.ts +3 -13
  215. package/src/image/image-commands/upload-image.spec.ts +245 -0
  216. package/src/image/image-commands/upload-image.ts +46 -11
  217. package/src/image/image-commands.ts +2 -8
  218. package/src/image/image-spec.ts +1 -4
  219. package/src/image/image-upload-handler.ts +2 -8
  220. package/src/image/image.ts +3 -12
  221. package/src/image/index.ts +3 -13
  222. package/src/input-rule/index.ts +2 -13
  223. package/src/italic/index.ts +3 -12
  224. package/src/italic/italic-commands.spec.ts +2 -10
  225. package/src/italic/italic-commands.ts +1 -5
  226. package/src/italic/italic-input-rule.spec.ts +1 -5
  227. package/src/italic/italic-input-rule.ts +1 -4
  228. package/src/italic/italic-keymap.ts +1 -5
  229. package/src/italic/italic-spec.ts +1 -4
  230. package/src/italic/italic.ts +3 -12
  231. package/src/link/index.spec.ts +10 -13
  232. package/src/link/index.ts +1 -5
  233. package/src/link/link-paste-rule.spec.ts +2 -9
  234. package/src/link/link-regex.spec.ts +1 -5
  235. package/src/list/index.ts +3 -12
  236. package/src/list/list-commands.ts +1 -5
  237. package/src/list/list-input-rules.ts +1 -4
  238. package/src/list/list-keymap.spec.ts +6 -10
  239. package/src/list/list-keymap.ts +2 -8
  240. package/src/list/list-plugins.ts +1 -4
  241. package/src/list/list-serializer.ts +2 -9
  242. package/src/list/list-spec.ts +3 -13
  243. package/src/list/list.spec.ts +10 -21
  244. package/src/list/list.ts +3 -12
  245. package/src/list/style.css +5 -5
  246. package/src/loro/index.ts +3 -13
  247. package/src/loro/loro-commands.ts +2 -8
  248. package/src/loro/loro-cursor-plugin.ts +21 -21
  249. package/src/loro/loro-keymap.ts +3 -11
  250. package/src/loro/loro-sync-plugin.ts +2 -8
  251. package/src/loro/loro-undo-plugin.ts +2 -8
  252. package/src/loro/loro.ts +16 -21
  253. package/src/mark-rule/apply.ts +3 -13
  254. package/src/mark-rule/mark-rule.spec.ts +2 -13
  255. package/src/mark-rule/mark-rule.ts +2 -13
  256. package/src/mark-rule/range.ts +2 -8
  257. package/src/mark-rule/types.ts +1 -4
  258. package/src/mention/index.ts +1 -8
  259. package/src/mod-click-prevention/index.ts +2 -9
  260. package/src/paragraph/paragraph-commands.ts +1 -5
  261. package/src/paragraph/paragraph-keymap.ts +2 -5
  262. package/src/paragraph/paragraph-spec.ts +1 -4
  263. package/src/paragraph/paragraph.ts +3 -14
  264. package/src/paste-rule/index.ts +2 -10
  265. package/src/paste-rule/mark-paste-rule.spec.ts +3 -13
  266. package/src/paste-rule/mark-paste-rule.ts +4 -14
  267. package/src/paste-rule/paste-rule-plugin.ts +2 -11
  268. package/src/paste-rule/paste-rule.spec.ts +4 -19
  269. package/src/paste-rule/split-text-by-regex.spec.ts +1 -5
  270. package/src/placeholder/index.ts +4 -16
  271. package/src/readonly/index.ts +2 -8
  272. package/src/search/index.ts +1 -6
  273. package/src/strike/index.ts +2 -2
  274. package/src/table/index.ts +10 -40
  275. package/src/table/table-commands/delete-cell-selection.spec.ts +1 -5
  276. package/src/table/table-commands/exit-table.spec.ts +1 -5
  277. package/src/table/table-commands/insert-table.spec.ts +1 -5
  278. package/src/table/table-commands/insert-table.ts +1 -4
  279. package/src/table/table-commands/move-table-column.spec.ts +1 -5
  280. package/src/table/table-commands/move-table-column.ts +1 -4
  281. package/src/table/table-commands/move-table-row.spec.ts +1 -5
  282. package/src/table/table-commands/move-table-row.ts +1 -4
  283. package/src/table/table-commands/select-table-cell.spec.ts +1 -5
  284. package/src/table/table-commands/select-table-column.spec.ts +1 -5
  285. package/src/table/table-commands/select-table-row.spec.ts +1 -5
  286. package/src/table/table-commands/select-table.spec.ts +1 -5
  287. package/src/table/table-commands/select-table.ts +1 -4
  288. package/src/table/table-commands.ts +8 -32
  289. package/src/table/table-plugins.ts +2 -8
  290. package/src/table/table-spec.spec.ts +2 -11
  291. package/src/table/table-spec.ts +2 -8
  292. package/src/table/table-utils.ts +2 -6
  293. package/src/table/table.ts +2 -8
  294. package/src/table/test-utils.ts +1 -4
  295. package/src/testing/clipboard.ts +1 -2
  296. package/src/testing/index.ts +9 -14
  297. package/src/testing/keyboard.ts +0 -30
  298. package/src/text/index.ts +1 -4
  299. package/src/text-align/index.ts +6 -6
  300. package/src/text-color/index.ts +3 -0
  301. package/src/text-color/text-color-commands.spec.ts +71 -0
  302. package/src/text-color/text-color-commands.ts +35 -0
  303. package/src/text-color/text-color-spec.spec.ts +297 -0
  304. package/src/text-color/text-color-spec.ts +58 -0
  305. package/src/text-color/text-color.ts +21 -0
  306. package/src/virtual-selection/index.ts +3 -14
  307. package/src/yjs/index.ts +5 -20
  308. package/src/yjs/yjs-commands.ts +2 -8
  309. package/src/yjs/yjs-cursor-plugin.ts +3 -5
  310. package/src/yjs/yjs-keymap.ts +3 -11
  311. package/src/yjs/yjs-sync-plugin.ts +1 -4
  312. package/src/yjs/yjs-types.ts +10 -0
  313. package/src/yjs/yjs-undo-plugin.ts +2 -8
  314. package/src/yjs/yjs.ts +6 -24
  315. package/dist/drop-indicator-B1QHFb5m.js.map +0 -1
  316. package/dist/enter-rule-CzWOZF_Z.js.map +0 -1
  317. package/dist/file-DrfcSid-.js.map +0 -1
  318. package/dist/index-oIc1a2f2.d.ts.map +0 -1
  319. package/dist/input-rule-dmsb3j6w.js.map +0 -1
  320. package/dist/mark-rule-BcLB4Uv2.js.map +0 -1
  321. package/dist/paste-rule-pVb4sqvJ.js.map +0 -1
  322. package/dist/shiki-highlighter-chunk-rkzofy4z.d.ts.map +0 -1
  323. package/dist/table-BRDh_9mG.js.map +0 -1
  324. package/src/testing/format-html.ts +0 -5
@@ -1,35 +1,28 @@
1
- import {
2
- canUseRegexLookbehind,
3
- union,
4
- } from '@prosekit/core'
5
- import {
6
- describe,
7
- expect,
8
- it,
9
- vi,
10
- } from 'vitest'
11
-
12
- import {
13
- defineTestExtension,
14
- setupTestFromExtension,
15
- } from '../testing'
16
- import {
17
- inputText,
18
- pressKey,
19
- } from '../testing/keyboard'
1
+ import { canUseRegexLookbehind, union } from '@prosekit/core'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import { keyboard } from 'vitest-browser-commands/playwright'
4
+
5
+ import { defineTestExtension, setupTestFromExtension } from '../testing'
6
+ import { inputText } from '../testing/keyboard'
20
7
 
21
8
  import { defineAutocomplete } from './autocomplete'
22
- import {
23
- AutocompleteRule,
24
- type MatchHandler,
25
- type MatchHandlerOptions,
26
- } from './autocomplete-rule'
9
+ import { AutocompleteRule, type MatchHandler, type MatchHandlerOptions } from './autocomplete-rule'
27
10
 
28
11
  function setupSlashMenu() {
29
- const regex = canUseRegexLookbehind() ? /(?<!\S)\/(|\S.*)$/u : /\/(|\S.*)$/u
12
+ const regex = canUseRegexLookbehind() ? /(?<!\S)\/(\S.*)?$/u : /\/(\S.*)?$/u
13
+
14
+ let matching: MatchHandlerOptions | null = null
30
15
 
31
- const onEnter = vi.fn<MatchHandler>()
32
- const onLeave = vi.fn<VoidFunction>()
16
+ const onEnter = vi.fn<MatchHandler>((options) => {
17
+ matching = options
18
+ })
19
+ const onLeave = vi.fn<VoidFunction>(() => {
20
+ if (matching) {
21
+ matching = null
22
+ } else {
23
+ throw new Error('onLeave should not be called when there is no matching')
24
+ }
25
+ })
33
26
 
34
27
  const rule = new AutocompleteRule({ regex, onEnter, onLeave })
35
28
  const extension = union(defineTestExtension(), defineAutocomplete(rule))
@@ -38,16 +31,34 @@ function setupSlashMenu() {
38
31
  const doc = n.doc(n.paragraph('<a>'))
39
32
  editor.set(doc)
40
33
 
41
- const getOnEnterOptions = (): MatchHandlerOptions => {
42
- const parameters = onEnter.mock.calls.at(-1)
43
- const options = parameters?.[0]
44
- if (!options) {
45
- throw new Error('No onEnter options found')
34
+ const isMatching = (): boolean => {
35
+ return !!matching
36
+ }
37
+
38
+ const getMatching = (): MatchHandlerOptions => {
39
+ if (!matching) {
40
+ throw new Error('No matching found')
46
41
  }
47
- return options
42
+ return matching
48
43
  }
49
44
 
50
- return { editor, n, m, onEnter, onLeave, getOnEnterOptions }
45
+ const getMatchingText = (): string => {
46
+ return getMatching().match[0]
47
+ }
48
+
49
+ const showSelection = (): string => {
50
+ const { selection, doc } = editor.state
51
+ const textBackward = doc.textBetween(0, selection.from, '\n')
52
+ const textSelected = doc.textBetween(selection.from, selection.to, '\n')
53
+ const textForward = doc.textBetween(selection.to, doc.content.size, '\n')
54
+ if (selection.empty) {
55
+ return textBackward + '<cursor>' + textForward
56
+ } else {
57
+ return textBackward + '<selection>' + textSelected + '<selection>' + textForward
58
+ }
59
+ }
60
+
61
+ return { editor, n, m, onEnter, onLeave, getMatching, isMatching, getMatchingText, showSelection }
51
62
  }
52
63
 
53
64
  describe('defineAutocomplete', () => {
@@ -82,27 +93,27 @@ describe('defineAutocomplete', () => {
82
93
  expect(onLeave).toHaveBeenCalledTimes(0)
83
94
 
84
95
  // Slash menu should not be triggered when typing "/ "
85
- await pressKey('Space')
96
+ await keyboard.press('Space')
86
97
  expect(onEnter).toHaveBeenCalledTimes(1)
87
98
  expect(onLeave).toHaveBeenCalledTimes(1)
88
99
  })
89
100
 
90
101
  it('can delete the matched text', async () => {
91
- const { editor, onEnter, getOnEnterOptions } = setupSlashMenu()
102
+ const { editor, onEnter, getMatching } = setupSlashMenu()
92
103
 
93
104
  expect(onEnter).not.toHaveBeenCalled()
94
105
 
95
106
  await inputText('/')
96
107
  expect(onEnter).toHaveBeenCalledTimes(1)
97
108
 
98
- const options = getOnEnterOptions()
109
+ const options = getMatching()
99
110
  expect(editor.state.doc.textContent).toBe('/')
100
111
  options.deleteMatch()
101
112
  expect(editor.state.doc.textContent).toBe('')
102
113
  })
103
114
 
104
- it('can ignore the match', async () => {
105
- const { editor, onEnter, onLeave, getOnEnterOptions } = setupSlashMenu()
115
+ it('can ignore the match by calling `ignoreMatch`', async () => {
116
+ const { editor, onEnter, onLeave, getMatching } = setupSlashMenu()
106
117
 
107
118
  expect(onEnter).not.toHaveBeenCalled()
108
119
 
@@ -118,7 +129,7 @@ describe('defineAutocomplete', () => {
118
129
  expect(editor.state.doc.textContent).toBe('/a')
119
130
 
120
131
  // Call `ignoreMatch` to dismiss the match
121
- const options = getOnEnterOptions()
132
+ const options = getMatching()
122
133
  options.ignoreMatch()
123
134
  expect(onEnter).toHaveBeenCalledTimes(2)
124
135
  expect(onLeave).toHaveBeenCalledTimes(1)
@@ -129,4 +140,197 @@ describe('defineAutocomplete', () => {
129
140
  expect(onLeave).toHaveBeenCalledTimes(1)
130
141
  expect(editor.state.doc.textContent).toBe('/aa')
131
142
  })
143
+
144
+ it('can dismiss the match by deleting the matched text', async () => {
145
+ const { isMatching, showSelection } = setupSlashMenu()
146
+
147
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
148
+ expect(isMatching()).toBe(false)
149
+
150
+ await inputText('/')
151
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
152
+ expect(isMatching()).toBe(true)
153
+
154
+ await keyboard.press('Backspace')
155
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
156
+ expect(isMatching()).toBe(false)
157
+ })
158
+
159
+ it('can recover the match after dismissing from Backspace', async () => {
160
+ const { isMatching, showSelection } = setupSlashMenu()
161
+
162
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
163
+ expect(isMatching()).toBe(false)
164
+
165
+ await inputText('/')
166
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
167
+ expect(isMatching()).toBe(true)
168
+
169
+ await keyboard.press('Backspace')
170
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
171
+ expect(isMatching()).toBe(false)
172
+
173
+ await inputText('/')
174
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
175
+ expect(isMatching()).toBe(true)
176
+ })
177
+
178
+ it('can recover the match after dismissing from onLeave', async () => {
179
+ const { isMatching, showSelection, getMatching } = setupSlashMenu()
180
+
181
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
182
+ expect(isMatching()).toBe(false)
183
+
184
+ await inputText('/')
185
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
186
+ expect(isMatching()).toBe(true)
187
+
188
+ const matching = getMatching()
189
+ expect(matching).toBeTruthy()
190
+ matching?.ignoreMatch()
191
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
192
+ expect(isMatching()).toBe(false)
193
+
194
+ await keyboard.press('Backspace')
195
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
196
+ expect(isMatching()).toBe(false)
197
+
198
+ await inputText('/')
199
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
200
+ expect(isMatching()).toBe(true)
201
+ })
202
+
203
+ it('can start a new match after dismissing the previous match', async () => {
204
+ const { isMatching, showSelection, getMatching, getMatchingText } = setupSlashMenu()
205
+
206
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
207
+ expect(isMatching()).toBe(false)
208
+
209
+ await inputText('a /b')
210
+ expect(showSelection()).toMatchInlineSnapshot(`"a /b<cursor>"`)
211
+ expect(isMatching()).toBe(true)
212
+
213
+ getMatching().ignoreMatch()
214
+ expect(showSelection()).toMatchInlineSnapshot(`"a /b<cursor>"`)
215
+ expect(isMatching()).toBe(false)
216
+
217
+ await keyboard.press('Space')
218
+ expect(showSelection()).toMatchInlineSnapshot(`"a /b <cursor>"`)
219
+ expect(isMatching()).toBe(false)
220
+
221
+ await inputText('/')
222
+ expect(showSelection()).toMatchInlineSnapshot(`"a /b /<cursor>"`)
223
+ expect(isMatching()).toBe(true)
224
+ expect(getMatchingText()).toBe('/')
225
+
226
+ await inputText('c')
227
+ expect(showSelection()).toMatchInlineSnapshot(`"a /b /c<cursor>"`)
228
+ expect(isMatching()).toBe(true)
229
+ expect(getMatchingText()).toBe('/c')
230
+ })
231
+
232
+ it('can dismiss the match by creating a new paragraph', async () => {
233
+ const { isMatching, showSelection } = setupSlashMenu()
234
+
235
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
236
+ expect(isMatching()).toBe(false)
237
+
238
+ await inputText('/')
239
+ expect(showSelection()).toMatchInlineSnapshot(`"/<cursor>"`)
240
+ expect(isMatching()).toBe(true)
241
+
242
+ await keyboard.press('Enter')
243
+ expect(showSelection()).toMatchInlineSnapshot(`
244
+ "/
245
+ <cursor>"
246
+ `)
247
+ expect(isMatching()).toBe(false)
248
+ })
249
+
250
+ it('can keep the match when selecting the text', async () => {
251
+ const { isMatching, showSelection } = setupSlashMenu()
252
+
253
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
254
+ expect(isMatching()).toBe(false)
255
+
256
+ await inputText('/page')
257
+ expect(showSelection()).toMatchInlineSnapshot(`"/page<cursor>"`)
258
+ expect(isMatching()).toBe(true)
259
+
260
+ await keyboard.down('Shift')
261
+ await keyboard.press('ArrowLeft')
262
+ await keyboard.press('ArrowLeft')
263
+ await keyboard.up('Shift')
264
+ expect(showSelection()).toMatchInlineSnapshot(`"/pa<selection>ge<selection>"`)
265
+ expect(isMatching()).toBe(true)
266
+ })
267
+
268
+ it('can ignore the match by moving the text cursor outside of the match', async () => {
269
+ const { onEnter, isMatching, getMatchingText, showSelection } = setupSlashMenu()
270
+
271
+ expect(onEnter).not.toHaveBeenCalled()
272
+
273
+ expect(showSelection()).toMatchInlineSnapshot(`"<cursor>"`)
274
+ expect(isMatching()).toBe(false)
275
+
276
+ await inputText('a ')
277
+ expect(showSelection()).toMatchInlineSnapshot(`"a <cursor>"`)
278
+ expect(isMatching()).toBe(false)
279
+
280
+ await inputText('/')
281
+ expect(showSelection()).toMatchInlineSnapshot(`"a /<cursor>"`)
282
+ expect(isMatching()).toBe(true)
283
+ expect(getMatchingText()).toBe('/')
284
+
285
+ await inputText('b')
286
+ expect(showSelection()).toMatchInlineSnapshot(`"a /b<cursor>"`)
287
+ expect(isMatching()).toBe(true)
288
+ expect(getMatchingText()).toBe('/b')
289
+
290
+ await keyboard.press('ArrowLeft')
291
+ expect(showSelection()).toMatchInlineSnapshot(`"a /<cursor>b"`)
292
+ expect(isMatching()).toBe(true)
293
+ expect(getMatchingText()).toBe('/b')
294
+
295
+ await keyboard.press('ArrowLeft')
296
+ expect(showSelection()).toMatchInlineSnapshot(`"a <cursor>/b"`)
297
+ expect(isMatching()).toBe(true)
298
+ expect(getMatchingText()).toBe('/b')
299
+
300
+ await keyboard.press('ArrowLeft')
301
+ expect(showSelection()).toMatchInlineSnapshot(`"a<cursor> /b"`)
302
+ expect(isMatching()).toBe(false)
303
+
304
+ await keyboard.press('ArrowRight')
305
+ expect(showSelection()).toMatchInlineSnapshot(`"a <cursor>/b"`)
306
+ expect(isMatching()).toBe(false)
307
+
308
+ await keyboard.press('ArrowRight')
309
+ expect(showSelection()).toMatchInlineSnapshot(`"a /<cursor>b"`)
310
+ expect(isMatching()).toBe(false)
311
+
312
+ await keyboard.press('Backspace')
313
+ await keyboard.press('Backspace')
314
+ expect(showSelection()).toMatchInlineSnapshot(`"a<cursor>b"`)
315
+ expect(isMatching()).toBe(false)
316
+
317
+ await inputText(' /')
318
+ expect(showSelection()).toMatchInlineSnapshot(`"a /<cursor>b"`)
319
+ expect(isMatching()).toBe(true)
320
+ expect(getMatchingText()).toBe('/')
321
+
322
+ await inputText('c')
323
+ expect(showSelection()).toMatchInlineSnapshot(`"a /c<cursor>b"`)
324
+ expect(isMatching()).toBe(true)
325
+ expect(getMatchingText()).toBe('/c')
326
+
327
+ await inputText('d')
328
+ expect(showSelection()).toMatchInlineSnapshot(`"a /cd<cursor>b"`)
329
+ expect(isMatching()).toBe(true)
330
+ expect(getMatchingText()).toBe('/cd')
331
+
332
+ await keyboard.press('ArrowRight')
333
+ expect(showSelection()).toMatchInlineSnapshot(`"a /cdb<cursor>"`)
334
+ expect(isMatching()).toBe(false)
335
+ })
132
336
  })
@@ -1,14 +1,16 @@
1
- import {
2
- defineFacet,
3
- defineFacetPayload,
4
- pluginFacet,
5
- type Extension,
6
- type PluginPayload,
7
- } from '@prosekit/core'
1
+ import { defineFacet, defineFacetPayload, pluginFacet, type Extension, type PluginPayload } from '@prosekit/core'
8
2
 
9
3
  import { createAutocompletePlugin } from './autocomplete-plugin'
10
4
  import type { AutocompleteRule } from './autocomplete-rule'
11
5
 
6
+ /**
7
+ * Defines an autocomplete extension that executes logic when the text before
8
+ * the cursor matches the given regular expression.
9
+ *
10
+ * When a match is found, an inline decoration is applied to the matched text
11
+ * with the class `prosekit-autocomplete-match` and a `data-autocomplete-match-text`
12
+ * attribute containing the full matched string.
13
+ */
12
14
  export function defineAutocomplete(rule: AutocompleteRule): Extension {
13
15
  return defineFacetPayload(autocompleteFacet, [rule])
14
16
  }
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { setupTest } from '../testing'
4
+
5
+ describe('addBackgroundColor', () => {
6
+ it('can add background color to text', () => {
7
+ const { editor, n, m } = setupTest()
8
+
9
+ const doc1 = n.doc(
10
+ n.p('Hello <a>world<b>'),
11
+ )
12
+ const doc2 = n.doc(
13
+ n.p('Hello ', m.backgroundColor({ color: 'red' }, 'world')),
14
+ )
15
+
16
+ editor.set(doc1)
17
+ editor.commands.addBackgroundColor({ color: 'red' })
18
+ expect(editor.getDocJSON()).toEqual(doc2.toJSON())
19
+ })
20
+
21
+ it('can override existing background color', () => {
22
+ const { editor, n, m } = setupTest()
23
+
24
+ const doc1 = n.doc(
25
+ n.p(
26
+ 'A',
27
+ m.backgroundColor({ color: 'red' }, 'B<a>C'),
28
+ m.backgroundColor({ color: 'red' }, 'DE'),
29
+ 'F<b>G',
30
+ ),
31
+ )
32
+ const doc2 = n.doc(
33
+ n.p(
34
+ 'A',
35
+ m.backgroundColor({ color: 'red' }, 'B'),
36
+ m.backgroundColor({ color: 'blue' }, 'CDEF'),
37
+ 'G',
38
+ ),
39
+ )
40
+
41
+ editor.set(doc1)
42
+ editor.commands.addBackgroundColor({ color: 'blue' })
43
+ expect(editor.getDocJSON()).toEqual(doc2.toJSON())
44
+ })
45
+ })
46
+
47
+ describe('removeBackgroundColor', () => {
48
+ it('can remove background color from text', () => {
49
+ const { editor, n, m } = setupTest()
50
+
51
+ const doc1 = n.doc(
52
+ n.p(
53
+ 'A',
54
+ m.backgroundColor({ color: 'red' }, 'B<a>C'),
55
+ m.backgroundColor({ color: 'blue' }, 'DE'),
56
+ 'F<b>G',
57
+ ),
58
+ )
59
+ const doc2 = n.doc(
60
+ n.p(
61
+ 'A',
62
+ m.backgroundColor({ color: 'red' }, 'B'),
63
+ 'CDEFG',
64
+ ),
65
+ )
66
+
67
+ editor.set(doc1)
68
+ editor.commands.removeBackgroundColor()
69
+ expect(editor.getDocJSON()).toEqual(doc2.toJSON())
70
+ })
71
+ })
@@ -0,0 +1,35 @@
1
+ import { addMark, defineCommands, removeMark, type Extension } from '@prosekit/core'
2
+ import type { Command } from '@prosekit/pm/state'
3
+
4
+ import type { BackgroundColorAttrs } from './background-color-spec'
5
+
6
+ /**
7
+ * @internal
8
+ */
9
+ export function addBackgroundColor(attrs: BackgroundColorAttrs): Command {
10
+ return addMark({ type: 'backgroundColor', attrs })
11
+ }
12
+
13
+ /**
14
+ * @internal
15
+ */
16
+ export function removeBackgroundColor(): Command {
17
+ return removeMark({ type: 'backgroundColor' })
18
+ }
19
+
20
+ /**
21
+ * @internal
22
+ */
23
+ export type BackgroundColorCommandsExtension = Extension<{
24
+ Commands: {
25
+ addBackgroundColor: [attrs: BackgroundColorAttrs]
26
+ removeBackgroundColor: []
27
+ }
28
+ }>
29
+
30
+ /**
31
+ * @internal
32
+ */
33
+ export function defineBackgroundColorCommands(): BackgroundColorCommandsExtension {
34
+ return defineCommands({ addBackgroundColor, removeBackgroundColor })
35
+ }