@prosekit/extensions 0.11.7 → 0.12.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 (81) hide show
  1. package/dist/{drop-indicator-E7nCfdnR.js → drop-indicator-BMvWUDXz.js} +2 -2
  2. package/dist/{drop-indicator-E7nCfdnR.js.map → drop-indicator-BMvWUDXz.js.map} +1 -1
  3. package/dist/{enter-rule-5tkoU2Ir.js → enter-rule-D-p4ykfv.js} +2 -2
  4. package/dist/{enter-rule-5tkoU2Ir.js.map → enter-rule-D-p4ykfv.js.map} +1 -1
  5. package/dist/{file-nRyo7PMB.js → file-DKoIIa7q.js} +2 -2
  6. package/dist/{file-nRyo7PMB.js.map → file-DKoIIa7q.js.map} +1 -1
  7. package/dist/{index-DY6lIIYV.d.ts → index-DdjnBeho.d.ts} +2 -2
  8. package/dist/index-DdjnBeho.d.ts.map +1 -0
  9. package/dist/{input-rule-DO_iy2aT.js → input-rule-COGr_GBb.js} +2 -2
  10. package/dist/{input-rule-DO_iy2aT.js.map → input-rule-COGr_GBb.js.map} +1 -1
  11. package/dist/{mark-rule-CGmswjQ_.js → mark-rule-v2E7B4C0.js} +2 -2
  12. package/dist/{mark-rule-CGmswjQ_.js.map → mark-rule-v2E7B4C0.js.map} +1 -1
  13. package/dist/{paste-rule-Brej6cWi.js → paste-rule-qSz46pqD.js} +2 -2
  14. package/dist/{paste-rule-Brej6cWi.js.map → paste-rule-qSz46pqD.js.map} +1 -1
  15. package/dist/prosekit-extensions-autocomplete.d.ts.map +1 -1
  16. package/dist/prosekit-extensions-blockquote.d.ts.map +1 -1
  17. package/dist/prosekit-extensions-blockquote.js +1 -1
  18. package/dist/prosekit-extensions-bold.d.ts.map +1 -1
  19. package/dist/prosekit-extensions-bold.js +1 -1
  20. package/dist/prosekit-extensions-code-block.d.ts +1 -1
  21. package/dist/prosekit-extensions-code-block.d.ts.map +1 -1
  22. package/dist/prosekit-extensions-code-block.js +2 -2
  23. package/dist/prosekit-extensions-code.d.ts.map +1 -1
  24. package/dist/prosekit-extensions-code.js +1 -1
  25. package/dist/prosekit-extensions-commit.d.ts.map +1 -1
  26. package/dist/prosekit-extensions-doc.d.ts.map +1 -1
  27. package/dist/prosekit-extensions-drop-cursor.d.ts.map +1 -1
  28. package/dist/prosekit-extensions-drop-indicator.d.ts.map +1 -1
  29. package/dist/prosekit-extensions-drop-indicator.js +1 -1
  30. package/dist/prosekit-extensions-enter-rule.d.ts.map +1 -1
  31. package/dist/prosekit-extensions-enter-rule.js +1 -1
  32. package/dist/prosekit-extensions-file.d.ts +1 -1
  33. package/dist/prosekit-extensions-file.js +1 -1
  34. package/dist/prosekit-extensions-hard-break.d.ts.map +1 -1
  35. package/dist/prosekit-extensions-heading.d.ts.map +1 -1
  36. package/dist/prosekit-extensions-heading.js +1 -1
  37. package/dist/prosekit-extensions-horizontal-rule.d.ts.map +1 -1
  38. package/dist/prosekit-extensions-horizontal-rule.js +1 -1
  39. package/dist/prosekit-extensions-image.d.ts +1 -1
  40. package/dist/prosekit-extensions-image.d.ts.map +1 -1
  41. package/dist/prosekit-extensions-image.js +1 -1
  42. package/dist/prosekit-extensions-input-rule.d.ts.map +1 -1
  43. package/dist/prosekit-extensions-input-rule.js +1 -1
  44. package/dist/prosekit-extensions-italic.d.ts.map +1 -1
  45. package/dist/prosekit-extensions-italic.js +1 -1
  46. package/dist/prosekit-extensions-link.d.ts.map +1 -1
  47. package/dist/prosekit-extensions-link.js +4 -4
  48. package/dist/prosekit-extensions-list.d.ts.map +1 -1
  49. package/dist/prosekit-extensions-list.js +36 -7
  50. package/dist/prosekit-extensions-list.js.map +1 -1
  51. package/dist/prosekit-extensions-loro.d.ts.map +1 -1
  52. package/dist/prosekit-extensions-mark-rule.d.ts.map +1 -1
  53. package/dist/prosekit-extensions-mark-rule.js +1 -1
  54. package/dist/prosekit-extensions-mention.d.ts.map +1 -1
  55. package/dist/prosekit-extensions-paragraph.d.ts.map +1 -1
  56. package/dist/prosekit-extensions-paste-rule.d.ts.map +1 -1
  57. package/dist/prosekit-extensions-paste-rule.js +1 -1
  58. package/dist/prosekit-extensions-placeholder.d.ts.map +1 -1
  59. package/dist/prosekit-extensions-placeholder.js +2 -2
  60. package/dist/prosekit-extensions-search.d.ts.map +1 -1
  61. package/dist/prosekit-extensions-strike.d.ts.map +1 -1
  62. package/dist/prosekit-extensions-strike.js +1 -1
  63. package/dist/prosekit-extensions-table.d.ts.map +1 -1
  64. package/dist/prosekit-extensions-table.js +2 -2
  65. package/dist/prosekit-extensions-text-align.d.ts.map +1 -1
  66. package/dist/prosekit-extensions-text.d.ts.map +1 -1
  67. package/dist/prosekit-extensions-underline.d.ts.map +1 -1
  68. package/dist/prosekit-extensions-yjs.d.ts.map +1 -1
  69. package/dist/{shiki-highlighter-chunk-Cwu1Jr9o.d.ts → shiki-highlighter-chunk-DcY3Ud8v.d.ts} +2 -2
  70. package/dist/shiki-highlighter-chunk-DcY3Ud8v.d.ts.map +1 -0
  71. package/dist/shiki-highlighter-chunk.d.ts +1 -1
  72. package/dist/{table-DND_1127.js → table-BLjD91VB.js} +3 -3
  73. package/dist/{table-DND_1127.js.map → table-BLjD91VB.js.map} +1 -1
  74. package/package.json +14 -14
  75. package/src/link/index.spec.ts +1 -1
  76. package/src/list/list-serializer.ts +57 -3
  77. package/src/list/list.spec.ts +129 -0
  78. package/src/testing/keyboard.ts +1 -1
  79. package/src/testing/markdown.ts +3 -0
  80. package/dist/index-DY6lIIYV.d.ts.map +0 -1
  81. package/dist/shiki-highlighter-chunk-Cwu1Jr9o.d.ts.map +0 -1
@@ -4,6 +4,7 @@ import {
4
4
  type PlainExtension,
5
5
  } from '@prosekit/core'
6
6
  import {
7
+ findCheckboxInListItem,
7
8
  joinListElements,
8
9
  listToDOM,
9
10
  } from 'prosemirror-flat-list'
@@ -16,13 +17,15 @@ export function defineListSerializer(): PlainExtension {
16
17
  serializeFragmentWrapper: (fn) => {
17
18
  return (...args) => {
18
19
  const dom = fn(...args)
19
- return joinListElements(dom)
20
+ return normalizeElementTree(joinListElements(dom))
20
21
  }
21
22
  },
22
23
  serializeNodeWrapper: (fn) => {
23
24
  return (...args) => {
24
25
  const dom = fn(...args)
25
- return isElementLike(dom) ? joinListElements(dom) : dom
26
+ return isElementLike(dom)
27
+ ? normalizeElementTree(joinListElements(dom))
28
+ : dom
26
29
  }
27
30
  },
28
31
  nodesFromSchemaWrapper: (fn) => {
@@ -30,9 +33,60 @@ export function defineListSerializer(): PlainExtension {
30
33
  const nodes = fn(...args)
31
34
  return {
32
35
  ...nodes,
33
- list: (node) => listToDOM({ node, nativeList: true, getMarkers: () => null }),
36
+ list: (node) => listToDOM({ node, nativeList: true }),
34
37
  }
35
38
  }
36
39
  },
37
40
  })
38
41
  }
42
+
43
+ function normalizeElementTree<T extends Element | DocumentFragment>(
44
+ node: T,
45
+ ): T {
46
+ if (isElementLike(node)) {
47
+ normalizeTaskList(node)
48
+ }
49
+
50
+ for (const child of node.children) {
51
+ normalizeElementTree(child)
52
+ }
53
+
54
+ return node
55
+ }
56
+
57
+ /**
58
+ * Modifies the DOM tree for task lists to ensure that the output HTML can be
59
+ * parsed by rehype-remark.
60
+ */
61
+ function normalizeTaskList(node: Element): void {
62
+ if (
63
+ !node.classList.contains('prosemirror-flat-list')
64
+ || node.getAttribute('data-list-kind') !== 'task'
65
+ || node.children.length !== 2
66
+ ) {
67
+ return
68
+ }
69
+
70
+ const marker = node.children.item(0)
71
+ if (!marker || !marker.classList.contains('list-marker')) {
72
+ return
73
+ }
74
+
75
+ const checkbox = findCheckboxInListItem(marker)
76
+ if (!checkbox) {
77
+ return
78
+ }
79
+
80
+ const content = node.children.item(1)
81
+ if (!content || !content.classList.contains('list-content')) {
82
+ return
83
+ }
84
+
85
+ const textBlock = content.children.item(0)
86
+ if (!textBlock || !['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(textBlock.tagName)) {
87
+ return
88
+ }
89
+
90
+ node.replaceChildren(...content.children)
91
+ textBlock.prepend(checkbox)
92
+ }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  createEditor,
3
+ nodeFromHTML,
3
4
  union,
4
5
  } from '@prosekit/core'
5
6
  import type { ProseMirrorNode } from '@prosekit/pm/model'
@@ -12,6 +13,11 @@ import {
12
13
  import { defineDoc } from '../doc'
13
14
  import { defineParagraph } from '../paragraph'
14
15
  import { setupTest } from '../testing'
16
+ import { formatHTML } from '../testing/format-html'
17
+ import {
18
+ htmlFromMarkdown,
19
+ markdownFromHTML,
20
+ } from '../testing/markdown'
15
21
  import { defineText } from '../text'
16
22
 
17
23
  import { defineList } from './index'
@@ -116,6 +122,10 @@ describe('defineList', () => {
116
122
  data-list-checked
117
123
  >
118
124
  <p>
125
+ <input
126
+ type="checkbox"
127
+ checked
128
+ >
119
129
  Checked 1
120
130
  </p>
121
131
  </li>
@@ -124,6 +134,7 @@ describe('defineList', () => {
124
134
  data-list-kind="task"
125
135
  >
126
136
  <p>
137
+ <input type="checkbox">
127
138
  Unchecked 2
128
139
  </p>
129
140
  </li>
@@ -131,4 +142,122 @@ describe('defineList', () => {
131
142
  "
132
143
  `)
133
144
  })
145
+
146
+ it('can generate html that can be parsed by remark', () => {
147
+ const { editor, n } = setupTest()
148
+
149
+ const doc1 = n.doc(
150
+ n.bullet(n.paragraph('Bullet')),
151
+ n.ordered(n.paragraph('Ordered')),
152
+ n.checked(n.paragraph('Checked')),
153
+ n.unchecked(n.paragraph('Unchecked')),
154
+ )
155
+
156
+ editor.set(doc1)
157
+ const html1 = editor.getDocHTML()
158
+ expect(formatHTML(html1)).toMatchInlineSnapshot(
159
+ `
160
+ "
161
+ <div>
162
+ <ul>
163
+ <li
164
+ class="prosemirror-flat-list"
165
+ data-list-kind="bullet"
166
+ >
167
+ <p>
168
+ Bullet
169
+ </p>
170
+ </li>
171
+ </ul>
172
+ <ol>
173
+ <li
174
+ class="prosemirror-flat-list"
175
+ data-list-kind="ordered"
176
+ >
177
+ <p>
178
+ Ordered
179
+ </p>
180
+ </li>
181
+ </ol>
182
+ <ul>
183
+ <li
184
+ class="prosemirror-flat-list"
185
+ data-list-kind="task"
186
+ data-list-checked
187
+ >
188
+ <p>
189
+ <input
190
+ type="checkbox"
191
+ checked
192
+ >
193
+ Checked
194
+ </p>
195
+ </li>
196
+ <li
197
+ class="prosemirror-flat-list"
198
+ data-list-kind="task"
199
+ >
200
+ <p>
201
+ <input type="checkbox">
202
+ Unchecked
203
+ </p>
204
+ </li>
205
+ </ul>
206
+ </div>
207
+ "
208
+ `,
209
+ )
210
+
211
+ const markdown1 = markdownFromHTML(html1)
212
+ expect(markdown1).toMatchInlineSnapshot(`
213
+ "* Bullet
214
+
215
+ 1. Ordered
216
+
217
+ * [x] Checked
218
+
219
+ * [ ] Unchecked
220
+ "
221
+ `)
222
+
223
+ const html2 = htmlFromMarkdown(markdown1)
224
+ expect(formatHTML(html2)).toMatchInlineSnapshot(`
225
+ "
226
+ <ul>
227
+ <li>
228
+ Bullet
229
+ </li>
230
+ </ul>
231
+ <ol>
232
+ <li>
233
+ Ordered
234
+ </li>
235
+ </ol>
236
+ <ul class="contains-task-list">
237
+ <li class="task-list-item">
238
+ <p>
239
+ <input
240
+ type="checkbox"
241
+ checked
242
+ disabled
243
+ >
244
+ Checked
245
+ </p>
246
+ </li>
247
+ <li class="task-list-item">
248
+ <p>
249
+ <input
250
+ type="checkbox"
251
+ disabled
252
+ >
253
+ Unchecked
254
+ </p>
255
+ </li>
256
+ </ul>
257
+ "
258
+ `)
259
+
260
+ const doc2 = nodeFromHTML(html2, { schema: editor.schema })
261
+ expect(doc2.toJSON()).toEqual(doc1.toJSON())
262
+ })
134
263
  })
@@ -1,5 +1,5 @@
1
1
  import { isApple } from '@prosekit/core'
2
- import { userEvent } from '@vitest/browser/context'
2
+ import { userEvent } from 'vitest/browser'
3
3
 
4
4
  /**
5
5
  * @example
@@ -1,5 +1,6 @@
1
1
  import rehypeParse from 'rehype-parse'
2
2
  import rehypeRemark from 'rehype-remark'
3
+ import remarkGfm from 'remark-gfm'
3
4
  import remarkHtml from 'remark-html'
4
5
  import remarkParse from 'remark-parse'
5
6
  import remarkStringify from 'remark-stringify'
@@ -9,6 +10,7 @@ export function markdownFromHTML(html: string): string {
9
10
  return unified()
10
11
  .use(rehypeParse)
11
12
  .use(rehypeRemark)
13
+ .use(remarkGfm)
12
14
  .use(remarkStringify)
13
15
  .processSync(html)
14
16
  .toString()
@@ -17,6 +19,7 @@ export function markdownFromHTML(html: string): string {
17
19
  export function htmlFromMarkdown(markdown: string): string {
18
20
  return unified()
19
21
  .use(remarkParse)
22
+ .use(remarkGfm)
20
23
  .use(remarkHtml)
21
24
  .processSync(markdown)
22
25
  .toString()
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-DY6lIIYV.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;;MAIQ,EAAA,UAAA;;;;EAwBI,KAAA,EAnBH,SAmBG;EAII;;;MAEb,EApBK,IAoBL;EAAc;;;;AClCjB;;;;;;AAuBA;AAIgB,KDCJ,eAAA,GCD0B,CAAA,OAAA,EDE3B,sBCF2B,EAAA,GAAA,OAAA,GAAA,IAAA;AAAA,iBDKtB,qBAAA,CCLsB,OAAA,EDM3B,eCN2B,CAAA,EDOnC,cCPmC;;;UA3BrB,uBAAA;;ADAjB;;MAIQ,ECAA,UDAA;;;;EAwBI,KAAA,ECnBH,cDmBkB;EAIX;;;MAEb,ECpBK,IDoBL;;;;;AClCH;;;AASS,KAcG,gBAAA,GAdH,CAAA,OAAA,EAeE,uBAfF,EAAA,GAAA,OAAA,GAAA,IAAA;AAKD,iBAaQ,sBAAA,CAbR,OAAA,EAcG,gBAdH,CAAA,EAeL,cAfK;;;;;;UCrBS,cAAA;EFOA,MAAA,EAAA,MAAA;EAAsB,KAAA,EAAA,MAAA;;AAS9B,UEPQ,eAAA,CFOR;;;AAmBT;EAIgB,IAAA,EE1BR,IF0BQ;EAAqB;;;EAEpB,UAAA,EAAA,CAAA,QAAA,EEvBQ,cFuBR,EAAA,GAAA,IAAA;;;;AClCjB;;AAIQ,KCcI,QDdJ,CAAA,MAAA,CAAA,GAAA,CAAA,OAAA,ECciC,eDdjC,EAAA,GCcqD,ODdrD,CCc6D,MDd7D,CAAA;;;;AAmBI,cCAC,UDAe,CAAA,MACjB,CAAA,CAAA;EAGK;;;;;;;;AClChB;EASiB,UAAA,IAAA,EAAA,OAAe;EAAA;;;EASO,UAAA,MAAA,EA4BnB,MA5BmB,GAAA,SAAA;EAO3B;;;YAAyD,KAAA,EA0BlD,KA1BkD,GAAA,SAAA;;;AAKrE;EAAuB,SAAA,QAAA,EA0BF,OA1BE,CA0BM,MA1BN,CAAA;UAgBH,WAAA;;;;;;;aAoB+C,CAAA;IAAA,IAAA;IAAA;GAAA,EAAA;IAAT,IAAA,EAAhB,IAAgB;IAiCjC,QAAA,EAjCiC,QAiCjC,CAjC0C,MAiC1C,CAAA;;;;;yCAAA,0BACpB;;;;mDAcA,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"shiki-highlighter-chunk-Cwu1Jr9o.d.ts","names":[],"sources":["../src/code-block/shiki-highlighter-chunk.ts"],"sourcesContent":[],"mappings":";;;UAeiB,uBAAA,SAAgC,0BAA0B,iBAAiB;AAA3E,UAEA,kBAAA,SAA2B,IAFH,CAEQ,uBAFR,EAAA,OAAA,GAAA,QAAA,CAAA,CAAA;EAAA,MAAA,EAG/B,YAH+B,EAAA;OAAkC,EAAA,CAIjE,eAJiE,GAI/C,eAJ+C,CAAA,EAAA;;AAA1B,KAwCrC,iBAAA,GAxCqC;EAAyB,WAAA,EA0CzD,WA1CyD;EAEzD,OAAA,CAAA,EAAA,SAAA;CAAmB,GAAA;aAAa,CAAA,EAAA,SAAA;SACvC,EA4CG,OA5CH,CAAA,IAAA,CAAA;;AACkB,iBA8CZ,sBAAA,CA9CY,OAAA,EA+CjB,kBA/CiB,CAAA,EAgDzB,iBAhDyB"}