@prosekit/extensions 0.14.1 → 0.15.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 (310) hide show
  1. package/dist/commit/style.css +1 -3
  2. package/dist/{drop-indicator-DJq8pF92.js → drop-indicator.js} +2 -4
  3. package/dist/drop-indicator.js.map +1 -0
  4. package/dist/{file-upload-I9m1EJAM.js → file.js} +2 -6
  5. package/dist/file.js.map +1 -0
  6. package/dist/gap-cursor/style.css +5 -8
  7. package/dist/{file-upload-dr3IXUty.d.ts → index.d.ts} +1 -1
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/list/style.css +79 -110
  10. package/dist/loro/style.css +18 -21
  11. package/dist/{mark-rule-CUnXwBuy.js → mark-rule.js} +3 -6
  12. package/dist/mark-rule.js.map +1 -0
  13. package/dist/page/style.css +43 -0
  14. package/dist/{mark-paste-rule-n_2Ehmb5.js → paste-rule.js} +3 -7
  15. package/dist/paste-rule.js.map +1 -0
  16. package/dist/placeholder/style.css +4 -7
  17. package/dist/prosekit-extensions-autocomplete.d.ts +1 -1
  18. package/dist/prosekit-extensions-autocomplete.js +17 -21
  19. package/dist/prosekit-extensions-autocomplete.js.map +1 -1
  20. package/dist/prosekit-extensions-background-color.d.ts +1 -1
  21. package/dist/prosekit-extensions-background-color.js +1 -4
  22. package/dist/prosekit-extensions-background-color.js.map +1 -1
  23. package/dist/prosekit-extensions-blockquote.d.ts +13 -13
  24. package/dist/prosekit-extensions-blockquote.d.ts.map +1 -1
  25. package/dist/prosekit-extensions-blockquote.js +6 -10
  26. package/dist/prosekit-extensions-blockquote.js.map +1 -1
  27. package/dist/prosekit-extensions-bold.d.ts +12 -12
  28. package/dist/prosekit-extensions-bold.d.ts.map +1 -1
  29. package/dist/prosekit-extensions-bold.js +1 -6
  30. package/dist/prosekit-extensions-bold.js.map +1 -1
  31. package/dist/prosekit-extensions-code-block.d.ts +56 -37
  32. package/dist/prosekit-extensions-code-block.d.ts.map +1 -1
  33. package/dist/prosekit-extensions-code-block.js +73 -76
  34. package/dist/prosekit-extensions-code-block.js.map +1 -1
  35. package/dist/prosekit-extensions-code.d.ts +12 -12
  36. package/dist/prosekit-extensions-code.d.ts.map +1 -1
  37. package/dist/prosekit-extensions-code.js +1 -6
  38. package/dist/prosekit-extensions-code.js.map +1 -1
  39. package/dist/prosekit-extensions-commit.d.ts +1 -1
  40. package/dist/prosekit-extensions-commit.js +2 -3
  41. package/dist/prosekit-extensions-commit.js.map +1 -1
  42. package/dist/prosekit-extensions-doc.js +1 -2
  43. package/dist/prosekit-extensions-doc.js.map +1 -1
  44. package/dist/prosekit-extensions-drop-cursor.js +1 -2
  45. package/dist/prosekit-extensions-drop-cursor.js.map +1 -1
  46. package/dist/prosekit-extensions-drop-indicator.js +2 -3
  47. package/dist/prosekit-extensions-enter-rule.d.ts +2 -2
  48. package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -1
  49. package/dist/prosekit-extensions-enter-rule.js +1 -2
  50. package/dist/prosekit-extensions-enter-rule.js.map +1 -1
  51. package/dist/prosekit-extensions-file.d.ts +2 -2
  52. package/dist/prosekit-extensions-file.js +2 -3
  53. package/dist/prosekit-extensions-gap-cursor.js +1 -2
  54. package/dist/prosekit-extensions-gap-cursor.js.map +1 -1
  55. package/dist/prosekit-extensions-hard-break.d.ts +7 -7
  56. package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
  57. package/dist/prosekit-extensions-hard-break.js +1 -5
  58. package/dist/prosekit-extensions-hard-break.js.map +1 -1
  59. package/dist/prosekit-extensions-heading.d.ts +15 -15
  60. package/dist/prosekit-extensions-heading.d.ts.map +1 -1
  61. package/dist/prosekit-extensions-heading.js +1 -6
  62. package/dist/prosekit-extensions-heading.js.map +1 -1
  63. package/dist/prosekit-extensions-horizontal-rule.d.ts +7 -7
  64. package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -1
  65. package/dist/prosekit-extensions-horizontal-rule.js +12 -14
  66. package/dist/prosekit-extensions-horizontal-rule.js.map +1 -1
  67. package/dist/prosekit-extensions-image.d.ts +12 -13
  68. package/dist/prosekit-extensions-image.d.ts.map +1 -1
  69. package/dist/prosekit-extensions-image.js +10 -16
  70. package/dist/prosekit-extensions-image.js.map +1 -1
  71. package/dist/prosekit-extensions-input-rule.js +1 -2
  72. package/dist/prosekit-extensions-input-rule.js.map +1 -1
  73. package/dist/prosekit-extensions-italic.d.ts +12 -12
  74. package/dist/prosekit-extensions-italic.d.ts.map +1 -1
  75. package/dist/prosekit-extensions-italic.js +1 -6
  76. package/dist/prosekit-extensions-italic.js.map +1 -1
  77. package/dist/prosekit-extensions-link.js +3 -6
  78. package/dist/prosekit-extensions-link.js.map +1 -1
  79. package/dist/prosekit-extensions-list.d.ts +26 -26
  80. package/dist/prosekit-extensions-list.d.ts.map +1 -1
  81. package/dist/prosekit-extensions-list.js +26 -34
  82. package/dist/prosekit-extensions-list.js.map +1 -1
  83. package/dist/prosekit-extensions-loro.d.ts +16 -16
  84. package/dist/prosekit-extensions-loro.d.ts.map +1 -1
  85. package/dist/prosekit-extensions-loro.js +3 -9
  86. package/dist/prosekit-extensions-loro.js.map +1 -1
  87. package/dist/prosekit-extensions-mark-rule.js +2 -3
  88. package/dist/prosekit-extensions-math.d.ts +3 -3
  89. package/dist/prosekit-extensions-math.d.ts.map +1 -1
  90. package/dist/prosekit-extensions-math.js +5 -5
  91. package/dist/prosekit-extensions-math.js.map +1 -1
  92. package/dist/prosekit-extensions-mention.js +1 -2
  93. package/dist/prosekit-extensions-mention.js.map +1 -1
  94. package/dist/prosekit-extensions-mod-click-prevention.js +2 -3
  95. package/dist/prosekit-extensions-mod-click-prevention.js.map +1 -1
  96. package/dist/prosekit-extensions-page.d.ts +114 -0
  97. package/dist/prosekit-extensions-page.d.ts.map +1 -0
  98. package/dist/prosekit-extensions-page.js +324 -0
  99. package/dist/prosekit-extensions-page.js.map +1 -0
  100. package/dist/prosekit-extensions-paragraph.d.ts +7 -7
  101. package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
  102. package/dist/prosekit-extensions-paragraph.js +3 -7
  103. package/dist/prosekit-extensions-paragraph.js.map +1 -1
  104. package/dist/prosekit-extensions-paste-rule.js +2 -3
  105. package/dist/prosekit-extensions-placeholder.d.ts +1 -1
  106. package/dist/prosekit-extensions-placeholder.js +3 -4
  107. package/dist/prosekit-extensions-placeholder.js.map +1 -1
  108. package/dist/prosekit-extensions-readonly.js +2 -3
  109. package/dist/prosekit-extensions-readonly.js.map +1 -1
  110. package/dist/prosekit-extensions-search.js +1 -2
  111. package/dist/prosekit-extensions-search.js.map +1 -1
  112. package/dist/prosekit-extensions-strike.js +1 -2
  113. package/dist/prosekit-extensions-strike.js.map +1 -1
  114. package/dist/prosekit-extensions-table.d.ts +47 -47
  115. package/dist/prosekit-extensions-table.d.ts.map +1 -1
  116. package/dist/prosekit-extensions-table.js +2 -3
  117. package/dist/prosekit-extensions-text-align.d.ts +1 -1
  118. package/dist/prosekit-extensions-text-align.js +1 -2
  119. package/dist/prosekit-extensions-text-align.js.map +1 -1
  120. package/dist/prosekit-extensions-text-color.d.ts +1 -1
  121. package/dist/prosekit-extensions-text-color.js +1 -4
  122. package/dist/prosekit-extensions-text-color.js.map +1 -1
  123. package/dist/prosekit-extensions-text.js +1 -2
  124. package/dist/prosekit-extensions-text.js.map +1 -1
  125. package/dist/prosekit-extensions-underline.js +1 -2
  126. package/dist/prosekit-extensions-underline.js.map +1 -1
  127. package/dist/prosekit-extensions-virtual-selection.js +2 -3
  128. package/dist/prosekit-extensions-virtual-selection.js.map +1 -1
  129. package/dist/prosekit-extensions-yjs.d.ts +3 -3
  130. package/dist/prosekit-extensions-yjs.d.ts.map +1 -1
  131. package/dist/prosekit-extensions-yjs.js +3 -9
  132. package/dist/prosekit-extensions-yjs.js.map +1 -1
  133. package/dist/prosekit-extensions.js +1 -1
  134. package/dist/search/style.css +4 -8
  135. package/dist/shiki-highlighter-chunk.js +1 -2
  136. package/dist/shiki-highlighter-chunk.js.map +1 -1
  137. package/dist/table/style.css +16 -16
  138. package/dist/{table-UJVYsrB7.js → table.js} +29 -41
  139. package/dist/table.js.map +1 -0
  140. package/dist/virtual-selection/style.css +1 -4
  141. package/dist/yjs/style.css +14 -20
  142. package/package.json +30 -16
  143. package/src/autocomplete/autocomplete-helpers.ts +1 -1
  144. package/src/autocomplete/autocomplete-plugin.ts +2 -2
  145. package/src/autocomplete/autocomplete-rule.ts +1 -1
  146. package/src/autocomplete/autocomplete.spec.ts +4 -4
  147. package/src/autocomplete/autocomplete.ts +2 -2
  148. package/src/autocomplete/index.ts +2 -2
  149. package/src/background-color/background-color-commands.spec.ts +1 -1
  150. package/src/background-color/background-color-commands.ts +1 -1
  151. package/src/background-color/background-color-spec.spec.ts +1 -1
  152. package/src/background-color/background-color.ts +2 -2
  153. package/src/background-color/index.ts +3 -3
  154. package/src/blockquote/blockquote-input-rule.ts +1 -1
  155. package/src/blockquote/blockquote-keymap.spec.ts +1 -1
  156. package/src/blockquote/blockquote-keymap.ts +9 -7
  157. package/src/blockquote/blockquote.ts +4 -4
  158. package/src/blockquote/index.ts +5 -5
  159. package/src/bold/bold-input-rule.spec.ts +2 -2
  160. package/src/bold/bold-input-rule.ts +1 -1
  161. package/src/bold/bold.ts +4 -4
  162. package/src/bold/index.ts +5 -5
  163. package/src/code/code-input-rule.ts +1 -1
  164. package/src/code/code.ts +4 -4
  165. package/src/code/index.ts +5 -5
  166. package/src/code-block/code-block-commands.ts +1 -1
  167. package/src/code-block/code-block-highlight.ts +11 -1
  168. package/src/code-block/code-block-input-rule.ts +3 -3
  169. package/src/code-block/code-block-shiki.ts +13 -5
  170. package/src/code-block/code-block-spec.spec.ts +2 -2
  171. package/src/code-block/code-block-spec.ts +1 -1
  172. package/src/code-block/code-block.ts +4 -4
  173. package/src/code-block/index.ts +9 -9
  174. package/src/code-block/shiki-highlighter.ts +2 -2
  175. package/src/code-block/shiki-parser.ts +2 -2
  176. package/src/drop-cursor/index.ts +1 -1
  177. package/src/drop-indicator/drop-indicator.ts +1 -1
  178. package/src/drop-indicator/index.ts +1 -1
  179. package/src/enter-rule/index.ts +3 -3
  180. package/src/file/file-drop-handler.ts +1 -1
  181. package/src/file/file-paste-handler.spec.ts +3 -3
  182. package/src/file/file-paste-handler.ts +1 -1
  183. package/src/file/index.ts +3 -3
  184. package/src/gap-cursor/index.ts +1 -1
  185. package/src/hard-break/hard-break-keymap.spec.ts +2 -2
  186. package/src/hard-break/hard-break-keymap.ts +1 -1
  187. package/src/hard-break/hard-break.ts +3 -3
  188. package/src/hard-break/index.ts +4 -4
  189. package/src/heading/heading-commands.ts +1 -1
  190. package/src/heading/heading-input-rule.ts +2 -2
  191. package/src/heading/heading-keymap.spec.ts +1 -1
  192. package/src/heading/heading-spec.ts +1 -1
  193. package/src/heading/heading.ts +4 -4
  194. package/src/heading/index.ts +6 -6
  195. package/src/horizontal-rule/horizontal-rule-commands.spec.ts +1 -1
  196. package/src/horizontal-rule/horizontal-rule-commands.ts +14 -11
  197. package/src/horizontal-rule/horizontal-rule-input-rule.spec.ts +2 -2
  198. package/src/horizontal-rule/horizontal-rule-input-rule.ts +1 -1
  199. package/src/horizontal-rule/horizontal-rule.ts +3 -3
  200. package/src/horizontal-rule/index.ts +4 -4
  201. package/src/image/image-commands/insert-image.ts +1 -1
  202. package/src/image/image-commands/upload-image.spec.ts +4 -4
  203. package/src/image/image-commands/upload-image.ts +2 -2
  204. package/src/image/image-commands.ts +3 -3
  205. package/src/image/image-upload-handler.ts +2 -2
  206. package/src/image/image.ts +2 -2
  207. package/src/image/index.ts +6 -6
  208. package/src/italic/index.ts +5 -5
  209. package/src/italic/italic-commands.spec.ts +4 -4
  210. package/src/italic/italic-input-rule.spec.ts +2 -2
  211. package/src/italic/italic-input-rule.ts +1 -1
  212. package/src/italic/italic.ts +4 -4
  213. package/src/link/index.spec.ts +2 -2
  214. package/src/link/index.ts +6 -6
  215. package/src/link/link-paste-rule.spec.ts +2 -2
  216. package/src/link/link-paste-rule.ts +3 -3
  217. package/src/link/link-regex.spec.ts +1 -1
  218. package/src/list/index.ts +8 -8
  219. package/src/list/list-drop-indicator.ts +2 -2
  220. package/src/list/list-input-rules.ts +1 -1
  221. package/src/list/list-keymap.spec.ts +1 -1
  222. package/src/list/list-spec.ts +1 -1
  223. package/src/list/list-types.spec.ts +2 -2
  224. package/src/list/list.spec.ts +6 -6
  225. package/src/list/list.ts +7 -7
  226. package/src/loro/index.ts +6 -6
  227. package/src/loro/loro.ts +8 -7
  228. package/src/mark-rule/apply.ts +2 -2
  229. package/src/mark-rule/index.ts +2 -2
  230. package/src/mark-rule/mark-rule.spec.ts +4 -4
  231. package/src/mark-rule/mark-rule.ts +2 -2
  232. package/src/math/index.ts +4 -4
  233. package/src/math/math-block.ts +8 -1
  234. package/src/math/math-inline.ts +1 -1
  235. package/src/math/math.ts +3 -3
  236. package/src/page/index.ts +5 -0
  237. package/src/page/page-break-commands.spec.ts +61 -0
  238. package/src/page/page-break-commands.ts +41 -0
  239. package/src/page/page-break-keymap.ts +17 -0
  240. package/src/page/page-break-spec.ts +33 -0
  241. package/src/page/page-break.ts +23 -0
  242. package/src/page/page-element.ts +246 -0
  243. package/src/page/page-rendering.ts +164 -0
  244. package/src/page/style.css +43 -0
  245. package/src/paragraph/index.ts +7 -7
  246. package/src/paragraph/paragraph-keymap.ts +1 -1
  247. package/src/paragraph/paragraph.ts +6 -5
  248. package/src/paste-rule/index.ts +2 -2
  249. package/src/paste-rule/mark-paste-rule.spec.ts +8 -8
  250. package/src/paste-rule/mark-paste-rule.ts +2 -2
  251. package/src/paste-rule/paste-rule.spec.ts +2 -2
  252. package/src/paste-rule/paste-rule.ts +1 -1
  253. package/src/paste-rule/split-text-by-regex.spec.ts +1 -1
  254. package/src/placeholder/index.ts +1 -1
  255. package/src/strike/index.ts +1 -1
  256. package/src/table/index.ts +14 -14
  257. package/src/table/table-commands/delete-cell-selection.spec.ts +3 -3
  258. package/src/table/table-commands/exit-table.spec.ts +2 -2
  259. package/src/table/table-commands/insert-table.spec.ts +1 -1
  260. package/src/table/table-commands/move-table-column.spec.ts +2 -2
  261. package/src/table/table-commands/move-table-row.spec.ts +2 -2
  262. package/src/table/table-commands/select-table-cell.spec.ts +3 -3
  263. package/src/table/table-commands/select-table-cell.ts +1 -1
  264. package/src/table/table-commands/select-table-column.spec.ts +2 -2
  265. package/src/table/table-commands/select-table-column.ts +1 -1
  266. package/src/table/table-commands/select-table-row.spec.ts +2 -2
  267. package/src/table/table-commands/select-table-row.ts +1 -1
  268. package/src/table/table-commands/select-table.spec.ts +2 -2
  269. package/src/table/table-commands/select-table.ts +1 -1
  270. package/src/table/table-commands.ts +9 -9
  271. package/src/table/table-drop-indicator.ts +2 -2
  272. package/src/table/table-spec.spec.ts +4 -4
  273. package/src/table/table.ts +4 -4
  274. package/src/table/test-utils.ts +1 -1
  275. package/src/testing/index.ts +24 -24
  276. package/src/text-color/index.ts +3 -3
  277. package/src/text-color/text-color-commands.spec.ts +1 -1
  278. package/src/text-color/text-color-commands.ts +1 -1
  279. package/src/text-color/text-color-spec.spec.ts +1 -1
  280. package/src/text-color/text-color.ts +2 -2
  281. package/src/yjs/index.ts +7 -7
  282. package/src/yjs/yjs-cursor-plugin.ts +1 -1
  283. package/src/yjs/yjs-undo-plugin.ts +3 -2
  284. package/src/yjs/yjs.ts +9 -8
  285. package/dist/commit/style.css.map +0 -1
  286. package/dist/commit/style.js +0 -1
  287. package/dist/drop-indicator-DJq8pF92.js.map +0 -1
  288. package/dist/file-upload-I9m1EJAM.js.map +0 -1
  289. package/dist/file-upload-dr3IXUty.d.ts.map +0 -1
  290. package/dist/gap-cursor/style.css.map +0 -1
  291. package/dist/gap-cursor/style.js +0 -1
  292. package/dist/list/style.css.map +0 -1
  293. package/dist/list/style.js +0 -1
  294. package/dist/loro/style.css.map +0 -1
  295. package/dist/loro/style.js +0 -1
  296. package/dist/mark-paste-rule-n_2Ehmb5.js.map +0 -1
  297. package/dist/mark-rule-CUnXwBuy.js.map +0 -1
  298. package/dist/placeholder/style.css.map +0 -1
  299. package/dist/placeholder/style.js +0 -1
  300. package/dist/search/style.css.map +0 -1
  301. package/dist/search/style.js +0 -1
  302. package/dist/shiki-highlighter-chunk.d.ts +0 -19
  303. package/dist/shiki-highlighter-chunk.d.ts.map +0 -1
  304. package/dist/table/style.css.map +0 -1
  305. package/dist/table/style.js +0 -1
  306. package/dist/table-UJVYsrB7.js.map +0 -1
  307. package/dist/virtual-selection/style.css.map +0 -1
  308. package/dist/virtual-selection/style.js +0 -1
  309. package/dist/yjs/style.css.map +0 -1
  310. package/dist/yjs/style.js +0 -1
package/src/math/math.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { union, type Union } from '@prosekit/core'
2
2
  import type { RenderMathBlock, RenderMathInline } from 'prosemirror-math'
3
3
 
4
- import { defineMathBlock, type MathBlockExtension } from './math-block'
5
- import { defineMathInline, type MathInlineExtension } from './math-inline'
6
- import { defineMathPlugin } from './math-plugin'
4
+ import { defineMathBlock, type MathBlockExtension } from './math-block.ts'
5
+ import { defineMathInline, type MathInlineExtension } from './math-inline.ts'
6
+ import { defineMathPlugin } from './math-plugin.ts'
7
7
 
8
8
  /**
9
9
  * @public
@@ -0,0 +1,5 @@
1
+ export { definePageBreakCommands, insertPageBreak, type PageBreakCommandsExtension } from './page-break-commands.ts'
2
+ export { definePageBreakKeymap, type PageBreakKeymapExtension } from './page-break-keymap.ts'
3
+ export { definePageBreakSpec, type PageBreakSpecExtension } from './page-break-spec.ts'
4
+ export { definePageBreak, type PageBreakExtension } from './page-break.ts'
5
+ export { definePageRendering, type PageRenderingExtension, type PageRenderingOptions } from './page-rendering.ts'
@@ -0,0 +1,61 @@
1
+ import { union } from '@prosekit/core'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { defineDoc } from '../doc/index.ts'
5
+ import { defineHardBreak } from '../hard-break/index.ts'
6
+ import { defineHorizontalRule } from '../horizontal-rule/index.ts'
7
+ import { defineParagraph } from '../paragraph/index.ts'
8
+ import { setupTestFromExtension } from '../testing/index.ts'
9
+ import { defineText } from '../text/index.ts'
10
+
11
+ import { definePageBreak } from './page-break.ts'
12
+
13
+ function setup() {
14
+ const extension = union(
15
+ defineDoc(),
16
+ defineText(),
17
+ defineParagraph(),
18
+ defineHorizontalRule(),
19
+ defineHardBreak(),
20
+ definePageBreak(),
21
+ )
22
+ return setupTestFromExtension(extension)
23
+ }
24
+
25
+ describe('insertPageBreak', () => {
26
+ it('should insert a page break in an empty paragraph', () => {
27
+ const { editor, n } = setup()
28
+ editor.set(n.doc(n.paragraph('<a>')))
29
+ editor.commands.insertPageBreak()
30
+ expect(editor.view.state.doc.toJSON()).toEqual(
31
+ n.doc(n.pageBreak()).toJSON(),
32
+ )
33
+ })
34
+
35
+ it('should insert a page break after text', () => {
36
+ const { editor, n } = setup()
37
+ editor.set(n.doc(n.paragraph('hello<a>')))
38
+ editor.commands.insertPageBreak()
39
+ expect(editor.view.state.doc.toJSON()).toEqual(
40
+ n.doc(n.paragraph('hello'), n.pageBreak()).toJSON(),
41
+ )
42
+ })
43
+
44
+ it('should insert a page break before text', () => {
45
+ const { editor, n } = setup()
46
+ editor.set(n.doc(n.paragraph('<a>hello')))
47
+ editor.commands.insertPageBreak()
48
+ expect(editor.view.state.doc.toJSON()).toEqual(
49
+ n.doc(n.pageBreak(), n.paragraph('hello')).toJSON(),
50
+ )
51
+ })
52
+
53
+ it('should insert a page break between text', () => {
54
+ const { editor, n } = setup()
55
+ editor.set(n.doc(n.paragraph('hel<a>lo')))
56
+ editor.commands.insertPageBreak()
57
+ expect(editor.view.state.doc.toJSON()).toEqual(
58
+ n.doc(n.paragraph('hel'), n.pageBreak(), n.paragraph('lo')).toJSON(),
59
+ )
60
+ })
61
+ })
@@ -0,0 +1,41 @@
1
+ import { defineCommands, getNodeType, type Extension } from '@prosekit/core'
2
+ import { Fragment, Slice } from '@prosekit/pm/model'
3
+ import type { Command } from '@prosekit/pm/state'
4
+
5
+ /**
6
+ * @internal
7
+ */
8
+ export type PageBreakCommandsExtension = Extension<{
9
+ Commands: {
10
+ insertPageBreak: []
11
+ }
12
+ }>
13
+
14
+ const insertPageBreakCommand: Command = (state, dispatch): boolean => {
15
+ if (!dispatch) return true
16
+
17
+ const { schema, tr } = state
18
+ const type = getNodeType(schema, 'pageBreak')
19
+ const node = type.createChecked()
20
+ const pos = tr.selection.anchor
21
+ const slice = new Slice(Fragment.from(node), 0, 0)
22
+ tr.replaceRange(pos, pos, slice).scrollIntoView()
23
+ dispatch(tr)
24
+ return true
25
+ }
26
+
27
+ /**
28
+ * @internal
29
+ */
30
+ export function insertPageBreak(): Command {
31
+ return insertPageBreakCommand
32
+ }
33
+
34
+ /**
35
+ * @internal
36
+ */
37
+ export function definePageBreakCommands(): PageBreakCommandsExtension {
38
+ return defineCommands({
39
+ insertPageBreak: insertPageBreak,
40
+ })
41
+ }
@@ -0,0 +1,17 @@
1
+ import { defineKeymap, type PlainExtension } from '@prosekit/core'
2
+
3
+ import { insertPageBreak } from './page-break-commands.ts'
4
+
5
+ /**
6
+ * @internal
7
+ */
8
+ export type PageBreakKeymapExtension = PlainExtension
9
+
10
+ /**
11
+ * @internal
12
+ */
13
+ export function definePageBreakKeymap(): PageBreakKeymapExtension {
14
+ return defineKeymap({
15
+ 'Mod-Enter': insertPageBreak(),
16
+ })
17
+ }
@@ -0,0 +1,33 @@
1
+ import { defineNodeSpec, type Extension } from '@prosekit/core'
2
+ import type { Attrs } from '@prosekit/pm/model'
3
+
4
+ /**
5
+ * @internal
6
+ */
7
+ export type PageBreakSpecExtension = Extension<{
8
+ Nodes: {
9
+ pageBreak: Attrs
10
+ }
11
+ }>
12
+
13
+ /**
14
+ * @internal
15
+ */
16
+ export function definePageBreakSpec(): PageBreakSpecExtension {
17
+ return defineNodeSpec({
18
+ name: 'pageBreak',
19
+ group: 'block',
20
+ selectable: true,
21
+ parseDOM: [{ tag: 'div.prosekit-page-break' }],
22
+ toDOM() {
23
+ return ['div', { class: 'prosekit-horizontal-rule prosekit-page-break' }, ['hr']]
24
+ },
25
+ pageBreak: true,
26
+ })
27
+ }
28
+
29
+ declare module '@prosekit/pm/model' {
30
+ interface NodeSpec {
31
+ pageBreak?: boolean | undefined
32
+ }
33
+ }
@@ -0,0 +1,23 @@
1
+ import { union, type Union } from '@prosekit/core'
2
+
3
+ import { definePageBreakCommands, type PageBreakCommandsExtension } from './page-break-commands.ts'
4
+ import { definePageBreakKeymap, type PageBreakKeymapExtension } from './page-break-keymap.ts'
5
+ import { definePageBreakSpec, type PageBreakSpecExtension } from './page-break-spec.ts'
6
+
7
+ /**
8
+ * @internal
9
+ */
10
+ export type PageBreakExtension = Union<
11
+ [PageBreakSpecExtension, PageBreakCommandsExtension, PageBreakKeymapExtension]
12
+ >
13
+
14
+ /**
15
+ * @public
16
+ */
17
+ export function definePageBreak(): PageBreakExtension {
18
+ return union(
19
+ definePageBreakSpec(),
20
+ definePageBreakCommands(),
21
+ definePageBreakKeymap(),
22
+ )
23
+ }
@@ -0,0 +1,246 @@
1
+ import { once } from '@ocavue/utils'
2
+ import { customElements, HTMLElement } from 'server-dom-shim'
3
+
4
+ /**
5
+ * @internal
6
+ */
7
+ export const PAGE_CHUNK_TAG_NAME = 'pm-page-chunk'
8
+
9
+ /**
10
+ * @internal
11
+ */
12
+ export const registerPageChunkElement: VoidFunction = /* @__PURE__ */ once(() => {
13
+ if (typeof window === 'undefined' || customElements.get(PAGE_CHUNK_TAG_NAME)) return
14
+ customElements.define(PAGE_CHUNK_TAG_NAME, PageChunkElement)
15
+ })
16
+
17
+ class PageChunkElement extends HTMLElement {
18
+ static observedAttributes = [
19
+ 'data-group',
20
+ 'data-break',
21
+ 'data-h',
22
+ 'data-mt',
23
+ 'data-mb',
24
+
25
+ // Only the first chunk of the whole document has this attribute.
26
+ 'data-size',
27
+ ]
28
+
29
+ // Data attributes set by external code
30
+ #group: string = ''
31
+ #forceNextBreak: boolean = false
32
+ #pageHeight: number = 0
33
+ #pageMarginTop: number = 0
34
+ #pageMarginBottom: number = 0
35
+ #size: number | undefined = undefined
36
+
37
+ // Internal states
38
+ #updateRequested: boolean = false
39
+ #contentBoxHeight: number = 0
40
+
41
+ // Rendering states
42
+ #isHead: boolean = false
43
+ #isTail: boolean = false
44
+ #paddingTop: number = 0
45
+ #paddingBottom: number = 0
46
+
47
+ // Pending rendering states
48
+ #isHeadPending: boolean = false
49
+ #isTailPending: boolean = false
50
+ #paddingTopPending: number = 0
51
+ #paddingBottomPending: number = 0
52
+
53
+ connectedCallback() {
54
+ this.#parseDataAttributes()
55
+
56
+ if (this.#isLeader()) {
57
+ this.#isHeadPending = true
58
+ }
59
+
60
+ this.#render()
61
+
62
+ // Get the initial content box height when the resize observer is not started yet. Notice that
63
+ // `this.clientHeight` is an integer while the content box height can be a float, so this is not
64
+ // accurate but should be good enough for the first render.
65
+ this.#contentBoxHeight = this.clientHeight - this.#paddingTop - this.#paddingBottom
66
+
67
+ observeElement(this)
68
+
69
+ this.#requestUpdate()
70
+ }
71
+
72
+ disconnectedCallback() {
73
+ unobserveElement(this)
74
+ }
75
+
76
+ attributeChangedCallback(_: string, oldValue: string | null, newValue: string | null) {
77
+ if (oldValue === newValue) return
78
+ this.#parseDataAttributes()
79
+ this.#requestUpdate()
80
+ }
81
+
82
+ #parseDataAttributes() {
83
+ this.#group = this.getAttribute('data-group') || ''
84
+ this.#forceNextBreak = this.hasAttribute('data-break')
85
+ this.#pageHeight = this.#parseFloatAttribute('data-h')
86
+ this.#pageMarginTop = this.#parseFloatAttribute('data-mt')
87
+ this.#pageMarginBottom = this.#parseFloatAttribute('data-mb')
88
+
89
+ const sizeAttr = this.getAttribute('data-size')
90
+ this.#size = sizeAttr ? Number.parseInt(sizeAttr, 10) : undefined
91
+ }
92
+
93
+ #parseFloatAttribute(name: string): number {
94
+ const value = this.getAttribute(name)
95
+ return value != null ? Number.parseFloat(value) : 0
96
+ }
97
+
98
+ #isLeader() {
99
+ return this.#size != null
100
+ }
101
+
102
+ #render() {
103
+ if (this.#paddingTop !== this.#paddingTopPending || this.#paddingBottom !== this.#paddingBottomPending) {
104
+ Object.assign(this.style, {
105
+ paddingTop: `${this.#paddingTop = this.#paddingTopPending}px`,
106
+ paddingBottom: `${this.#paddingBottom = this.#paddingBottomPending}px`,
107
+ })
108
+ }
109
+ if (this.#isHead !== this.#isHeadPending) {
110
+ this.toggleAttribute('data-page-head', this.#isHead = this.#isHeadPending)
111
+ }
112
+ if (this.#isTail !== this.#isTailPending) {
113
+ this.toggleAttribute('data-page-tail', this.#isTail = this.#isTailPending)
114
+ }
115
+ }
116
+
117
+ setHeight(height: number) {
118
+ // Avoid potential float number precision issues
119
+ if (Math.abs(this.#contentBoxHeight - height) < 0.1) {
120
+ return
121
+ }
122
+ this.#contentBoxHeight = height
123
+ this.#requestUpdate()
124
+ }
125
+
126
+ /**
127
+ * Schedules a batched page layout recalculation.
128
+ *
129
+ * Any chunk can call this method, but the actual layout work (#updateAll)
130
+ * always runs on the leader chunk, because it needs to iterate over every
131
+ * chunk in order to compute page breaks.
132
+ *
133
+ * Two nested microtasks are used to batch updates:
134
+ *
135
+ * Microtask 1 – Delegation: non-leader chunks forward the request to the
136
+ * leader chunk, so multiple chunks changing in the same tick only trigger
137
+ * one layout pass.
138
+ *
139
+ * Microtask 2 – Execution: the leader chunk defers #updateAll to a second
140
+ * microtask so that any other attribute / resize changes that were queued
141
+ * in the same tick (and forwarded during microtask 1) are already reflected
142
+ * before the layout is recalculated.
143
+ *
144
+ * The #updateRequested flag acts as a deduplication guard so that rapid
145
+ * successive calls (e.g. multiple attributes changing at once) result in at
146
+ * most one scheduled pass per chunk.
147
+ */
148
+ #requestUpdate() {
149
+ if (this.#updateRequested) {
150
+ return
151
+ }
152
+
153
+ this.#updateRequested = true
154
+ queueMicrotask(() => {
155
+ if (!this.#isLeader()) {
156
+ this.#updateRequested = false
157
+ const leader = findLeaderChunk(this, this.#group)
158
+ if (!leader) return
159
+ leader.#requestUpdate()
160
+ return
161
+ }
162
+ queueMicrotask(() => {
163
+ this.#updateRequested = false
164
+ this.#updateAll()
165
+ })
166
+ })
167
+ }
168
+
169
+ #updateAll() {
170
+ if (!this.isConnected) {
171
+ return
172
+ }
173
+
174
+ const elements = findAllChunks(this, this.#group)
175
+ const count = elements.length
176
+ if (count === 0) return
177
+
178
+ const pageHeight = this.#pageHeight
179
+ const pageMarginTop = this.#pageMarginTop
180
+ const maxContentHeight = pageHeight - pageMarginTop - this.#pageMarginBottom
181
+
182
+ let currentContentHeight = 0
183
+ let forceNextBreak = false
184
+
185
+ for (let i = 0; i < count; i++) {
186
+ const element = elements[i]
187
+ const h = element.#contentBoxHeight
188
+ const isHead = forceNextBreak || i === 0 || (currentContentHeight + h > maxContentHeight)
189
+
190
+ forceNextBreak = element.#forceNextBreak
191
+
192
+ if (isHead && i > 0) {
193
+ const prev = elements[i - 1]
194
+ prev.#paddingBottomPending = Math.max(0, pageHeight - pageMarginTop - currentContentHeight)
195
+ prev.#isTailPending = true
196
+ currentContentHeight = h
197
+ } else {
198
+ currentContentHeight += h
199
+ }
200
+
201
+ element.#paddingTopPending = isHead ? pageMarginTop : 0
202
+ element.#paddingBottomPending = 0
203
+ element.#isTailPending = false
204
+ element.#isHeadPending = isHead
205
+ }
206
+
207
+ const last = elements[count - 1]
208
+ last.#paddingBottomPending = Math.max(0, pageHeight - pageMarginTop - currentContentHeight)
209
+ last.#isTailPending = true
210
+
211
+ for (const element of elements) {
212
+ element.#render()
213
+ }
214
+ }
215
+ }
216
+
217
+ function handleResize(entries: ResizeObserverEntry[]) {
218
+ for (const entry of entries) {
219
+ const contentBoxHeight = entry.contentBoxSize?.[0]?.blockSize ?? entry.contentRect.height
220
+ const element = entry.target as PageChunkElement
221
+ element.setHeight(contentBoxHeight)
222
+ }
223
+ }
224
+
225
+ const getResizeObserver = /* @__PURE__ */ once(() => {
226
+ return new ResizeObserver(handleResize)
227
+ })
228
+
229
+ function observeElement(element: PageChunkElement) {
230
+ getResizeObserver().observe(element)
231
+ }
232
+
233
+ function unobserveElement(element: PageChunkElement) {
234
+ getResizeObserver().unobserve(element)
235
+ }
236
+
237
+ function findLeaderChunk(element: HTMLElement, group: string): PageChunkElement | null | undefined {
238
+ const root = element.closest('.ProseMirror')
239
+ return root?.querySelector<PageChunkElement>(`${PAGE_CHUNK_TAG_NAME}[data-group="${group}"][data-size]`)
240
+ }
241
+
242
+ function findAllChunks(element: HTMLElement, group: string): PageChunkElement[] {
243
+ const root = element.closest('.ProseMirror')
244
+ const elements = root?.querySelectorAll<PageChunkElement>(`${PAGE_CHUNK_TAG_NAME}[data-group="${group}"]`)
245
+ return Array.from(elements || [])
246
+ }
@@ -0,0 +1,164 @@
1
+ import { getId } from '@ocavue/utils'
2
+ import { definePlugin, type Extension } from '@prosekit/core'
3
+ import type { Node } from '@prosekit/pm/model'
4
+ import { Plugin, PluginKey } from '@prosekit/pm/state'
5
+ import { Decoration, DecorationSet } from '@prosekit/pm/view'
6
+
7
+ import { PAGE_CHUNK_TAG_NAME, registerPageChunkElement } from './page-element.ts'
8
+
9
+ /**
10
+ * @public
11
+ */
12
+ export interface PageRenderingOptions {
13
+ /**
14
+ * The width of the page in px.
15
+ *
16
+ * @default 794 (Portrait A4 paper size in 96 DPI)
17
+ */
18
+ pageWidth?: number
19
+
20
+ /**
21
+ * The height of the page in px.
22
+ *
23
+ * @default 1123 (Portrait A4 paper size in 96 DPI)
24
+ */
25
+ pageHeight?: number
26
+
27
+ /**
28
+ * The top margin of the page in px.
29
+ *
30
+ * @default 70
31
+ */
32
+ marginTop?: number
33
+
34
+ /**
35
+ * The right margin of the page in px.
36
+ *
37
+ * @default 70
38
+ */
39
+ marginRight?: number
40
+
41
+ /**
42
+ * The bottom margin of the page in px.
43
+ *
44
+ * @default 70
45
+ */
46
+ marginBottom?: number
47
+
48
+ /**
49
+ * The left margin of the page in px.
50
+ *
51
+ * @default 70
52
+ */
53
+ marginLeft?: number
54
+ }
55
+
56
+ /**
57
+ * @public
58
+ */
59
+ export function definePageRendering(options: PageRenderingOptions = {}): PageRenderingExtension {
60
+ return definePlugin(
61
+ createPageRenderingPlugin(options),
62
+ )
63
+ }
64
+
65
+ /**
66
+ * @internal
67
+ */
68
+ export type PageRenderingExtension = Extension
69
+
70
+ function createPageRenderingPlugin(options: PageRenderingOptions): Plugin {
71
+ const {
72
+ pageWidth = 794,
73
+ pageHeight = 1123,
74
+ marginTop = 70,
75
+ marginRight = 70,
76
+ marginBottom = 70,
77
+ marginLeft = 70,
78
+ } = options
79
+
80
+ type PluginState = [group: string, decoration: DecorationSet]
81
+
82
+ const key = new PluginKey<PluginState>('prosekit-page-render')
83
+
84
+ function createDecorationSet(doc: Node, group: string): DecorationSet {
85
+ const decorations: Decoration[] = []
86
+ const totalCount = doc.childCount
87
+
88
+ doc.forEach((node, pos, index) => {
89
+ const isPageBreak: boolean | undefined = node.type.spec.pageBreak
90
+
91
+ decorations.push(Decoration.node(pos, pos + node.nodeSize, {
92
+ 'nodeName': PAGE_CHUNK_TAG_NAME,
93
+ 'data-group': group,
94
+ 'data-break': isPageBreak ? 'true' : undefined,
95
+ 'data-h': String(pageHeight),
96
+ 'data-mt': String(marginTop),
97
+ 'data-mb': String(marginBottom),
98
+ 'data-size': index === 0 ? String(totalCount) : undefined,
99
+ }))
100
+ })
101
+
102
+ return DecorationSet.create(doc, decorations)
103
+ }
104
+
105
+ return new Plugin<PluginState>({
106
+ key,
107
+ view: () => {
108
+ registerPageChunkElement()
109
+ return {}
110
+ },
111
+ state: {
112
+ init: (_config, state): PluginState => {
113
+ const group = `page-group-${getId()}`
114
+ const decoration = createDecorationSet(state.doc, group)
115
+ return [group, decoration]
116
+ },
117
+ apply: (tr, value, oldState, newState): PluginState => {
118
+ if (!tr.docChanged) return value
119
+
120
+ const [group, decoration] = value
121
+
122
+ let needRecreate = oldState.doc.childCount !== newState.doc.childCount
123
+
124
+ if (!needRecreate) {
125
+ const count = oldState.doc.childCount
126
+ for (let i = 0; i < count; i++) {
127
+ const oldNode = oldState.doc.child(i)
128
+ const newNode = newState.doc.child(i)
129
+ if (oldNode.type !== newNode.type) {
130
+ needRecreate = true
131
+ break
132
+ }
133
+ }
134
+ }
135
+
136
+ if (!needRecreate) {
137
+ const mapped = decoration.map(tr.mapping, tr.doc, {
138
+ onRemove: () => {
139
+ needRecreate = true
140
+ },
141
+ })
142
+ if (!needRecreate) {
143
+ return [group, mapped]
144
+ }
145
+ }
146
+
147
+ return [group, createDecorationSet(newState.doc, group)]
148
+ },
149
+ },
150
+ props: {
151
+ decorations: (state) => {
152
+ return key.getState(state)?.[1]
153
+ },
154
+ attributes: {
155
+ style: [
156
+ `--page-margin-right:${marginRight}px;`,
157
+ `--page-margin-left:${marginLeft}px;`,
158
+ `--page-width:${pageWidth}px;`,
159
+ `--page-height:${pageHeight}px;`,
160
+ ].join(''),
161
+ },
162
+ },
163
+ })
164
+ }
@@ -0,0 +1,43 @@
1
+ .ProseMirror pm-page-chunk {
2
+ box-sizing: border-box;
3
+ display: flow-root;
4
+ position: relative;
5
+ width: var(--page-width, 100%);
6
+ margin: 0;
7
+ padding-top: 0;
8
+ padding-right: var(--page-margin-right, 0);
9
+ padding-bottom: 0;
10
+ padding-left: var(--page-margin-left, 0);
11
+ }
12
+
13
+ .ProseMirror pm-page-chunk ~ pm-page-chunk[data-page-head] {
14
+ margin-top: 50px;
15
+ }
16
+
17
+ @media print {
18
+ .ProseMirror pm-page-chunk ~ pm-page-chunk[data-page-head] {
19
+ margin-top: 0;
20
+ }
21
+ }
22
+
23
+ .ProseMirror pm-page-chunk[data-page-head]::before {
24
+ box-sizing: border-box;
25
+ position: absolute;
26
+ top: 0;
27
+ left: 0;
28
+ width: 100%;
29
+ height: var(--page-height);
30
+ outline: 1px solid CanvasText;
31
+ content: "";
32
+ pointer-events: none;
33
+ }
34
+
35
+ @media print {
36
+ .ProseMirror pm-page-chunk[data-page-head]::before {
37
+ outline-color: transparent;
38
+ }
39
+ }
40
+
41
+ .ProseMirror pm-page-chunk[data-page-tail] {
42
+ break-after: page;
43
+ }
@@ -1,7 +1,7 @@
1
- export { defineParagraph } from './paragraph'
2
- export type { ParagraphExtension } from './paragraph'
3
- export { defineParagraphCommands } from './paragraph-commands'
4
- export type { ParagraphCommandsExtension } from './paragraph-commands'
5
- export { defineParagraphKeymap } from './paragraph-keymap'
6
- export { defineParagraphSpec } from './paragraph-spec'
7
- export type { ParagraphSpecExtension } from './paragraph-spec'
1
+ export { defineParagraphCommands } from './paragraph-commands.ts'
2
+ export type { ParagraphCommandsExtension } from './paragraph-commands.ts'
3
+ export { defineParagraphKeymap } from './paragraph-keymap.ts'
4
+ export { defineParagraphSpec } from './paragraph-spec.ts'
5
+ export type { ParagraphSpecExtension } from './paragraph-spec.ts'
6
+ export { defineParagraph } from './paragraph.ts'
7
+ export type { ParagraphExtension } from './paragraph.ts'
@@ -1,6 +1,6 @@
1
1
  import { defineKeymap, type PlainExtension } from '@prosekit/core'
2
2
 
3
- import { setParagraph } from './paragraph-commands'
3
+ import { setParagraph } from './paragraph-commands.ts'
4
4
 
5
5
  /**
6
6
  * @internal