@react-x11/components 0.7.1 → 0.9.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 (354) hide show
  1. package/README.md +295 -51
  2. package/dist/charts/node.d.ts +1 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +6 -2
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/color-picker/ColorPicker.d.ts.map +1 -1
  7. package/dist/color-picker/ColorPicker.js +43 -33
  8. package/dist/color-picker/ColorPicker.js.map +1 -1
  9. package/dist/embed/client.d.ts +48 -3
  10. package/dist/embed/client.d.ts.map +1 -1
  11. package/dist/embed/client.js +79 -5
  12. package/dist/embed/client.js.map +1 -1
  13. package/dist/embed/index.d.ts +1 -1
  14. package/dist/embed/index.d.ts.map +1 -1
  15. package/dist/embed/index.js +1 -1
  16. package/dist/embed/index.js.map +1 -1
  17. package/dist/html/node.d.ts +3 -1
  18. package/dist/html/node.d.ts.map +1 -1
  19. package/dist/html/node.js +7 -5
  20. package/dist/html/node.js.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
  26. package/dist/internal/markdown/ast.d.ts.map +1 -0
  27. package/dist/internal/markdown/ast.js +22 -0
  28. package/dist/internal/markdown/ast.js.map +1 -0
  29. package/dist/internal/markdown/parse.d.ts.map +1 -0
  30. package/dist/{markdown → internal/markdown}/parse.js +112 -22
  31. package/dist/internal/markdown/parse.js.map +1 -0
  32. package/dist/internal/markdown/stringify.d.ts +4 -0
  33. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  34. package/dist/internal/markdown/stringify.js +684 -0
  35. package/dist/internal/markdown/stringify.js.map +1 -0
  36. package/dist/internal/markdown/tags.d.ts.map +1 -0
  37. package/dist/internal/markdown/tags.js.map +1 -0
  38. package/dist/internal/scroll.d.ts +3 -2
  39. package/dist/internal/scroll.d.ts.map +1 -1
  40. package/dist/internal/scroll.js +33 -15
  41. package/dist/internal/scroll.js.map +1 -1
  42. package/dist/internal/units.d.ts +2 -0
  43. package/dist/internal/units.d.ts.map +1 -0
  44. package/dist/internal/units.js +35 -0
  45. package/dist/internal/units.js.map +1 -0
  46. package/dist/internal/window.d.ts.map +1 -1
  47. package/dist/internal/window.js +4 -2
  48. package/dist/internal/window.js.map +1 -1
  49. package/dist/maps/anchors.d.ts +98 -0
  50. package/dist/maps/anchors.d.ts.map +1 -0
  51. package/dist/maps/anchors.js +531 -0
  52. package/dist/maps/anchors.js.map +1 -0
  53. package/dist/maps/controller.d.ts +283 -0
  54. package/dist/maps/controller.d.ts.map +1 -0
  55. package/dist/maps/controller.js +743 -0
  56. package/dist/maps/controller.js.map +1 -0
  57. package/dist/maps/gl/buckets.d.ts +95 -0
  58. package/dist/maps/gl/buckets.d.ts.map +1 -0
  59. package/dist/maps/gl/buckets.js +362 -0
  60. package/dist/maps/gl/buckets.js.map +1 -0
  61. package/dist/maps/gl/build-worker.d.ts +13 -0
  62. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  63. package/dist/maps/gl/build-worker.js +44 -0
  64. package/dist/maps/gl/build-worker.js.map +1 -0
  65. package/dist/maps/gl/color.d.ts +8 -0
  66. package/dist/maps/gl/color.d.ts.map +1 -0
  67. package/dist/maps/gl/color.js +57 -0
  68. package/dist/maps/gl/color.js.map +1 -0
  69. package/dist/maps/gl/cover.d.ts +51 -0
  70. package/dist/maps/gl/cover.d.ts.map +1 -0
  71. package/dist/maps/gl/cover.js +124 -0
  72. package/dist/maps/gl/cover.js.map +1 -0
  73. package/dist/maps/gl/index.d.ts +3 -0
  74. package/dist/maps/gl/index.d.ts.map +1 -0
  75. package/dist/maps/gl/index.js +20 -0
  76. package/dist/maps/gl/index.js.map +1 -0
  77. package/dist/maps/gl/markers.d.ts +28 -0
  78. package/dist/maps/gl/markers.d.ts.map +1 -0
  79. package/dist/maps/gl/markers.js +60 -0
  80. package/dist/maps/gl/markers.js.map +1 -0
  81. package/dist/maps/gl/overlays.d.ts +68 -0
  82. package/dist/maps/gl/overlays.d.ts.map +1 -0
  83. package/dist/maps/gl/overlays.js +300 -0
  84. package/dist/maps/gl/overlays.js.map +1 -0
  85. package/dist/maps/gl/pane.d.ts +12 -0
  86. package/dist/maps/gl/pane.d.ts.map +1 -0
  87. package/dist/maps/gl/pane.js +37 -0
  88. package/dist/maps/gl/pane.js.map +1 -0
  89. package/dist/maps/gl/placement.d.ts +111 -0
  90. package/dist/maps/gl/placement.d.ts.map +1 -0
  91. package/dist/maps/gl/placement.js +567 -0
  92. package/dist/maps/gl/placement.js.map +1 -0
  93. package/dist/maps/gl/renderer.d.ts +352 -0
  94. package/dist/maps/gl/renderer.d.ts.map +1 -0
  95. package/dist/maps/gl/renderer.js +1127 -0
  96. package/dist/maps/gl/renderer.js.map +1 -0
  97. package/dist/maps/gl/shaders.d.ts +120 -0
  98. package/dist/maps/gl/shaders.d.ts.map +1 -0
  99. package/dist/maps/gl/shaders.js +451 -0
  100. package/dist/maps/gl/shaders.js.map +1 -0
  101. package/dist/maps/gl/store.d.ts +115 -0
  102. package/dist/maps/gl/store.d.ts.map +1 -0
  103. package/dist/maps/gl/store.js +469 -0
  104. package/dist/maps/gl/store.js.map +1 -0
  105. package/dist/maps/gl/text.d.ts +154 -0
  106. package/dist/maps/gl/text.d.ts.map +1 -0
  107. package/dist/maps/gl/text.js +382 -0
  108. package/dist/maps/gl/text.js.map +1 -0
  109. package/dist/maps/gl/view.d.ts +54 -0
  110. package/dist/maps/gl/view.d.ts.map +1 -0
  111. package/dist/maps/gl/view.js +974 -0
  112. package/dist/maps/gl/view.js.map +1 -0
  113. package/dist/maps/index.d.ts +1 -1
  114. package/dist/maps/index.d.ts.map +1 -1
  115. package/dist/maps/index.js +216 -52
  116. package/dist/maps/index.js.map +1 -1
  117. package/dist/maps/labels.d.ts +37 -16
  118. package/dist/maps/labels.d.ts.map +1 -1
  119. package/dist/maps/labels.js +188 -119
  120. package/dist/maps/labels.js.map +1 -1
  121. package/dist/maps/node.d.ts +162 -56
  122. package/dist/maps/node.d.ts.map +1 -1
  123. package/dist/maps/node.js +607 -515
  124. package/dist/maps/node.js.map +1 -1
  125. package/dist/maps/overlay.d.ts +72 -9
  126. package/dist/maps/overlay.d.ts.map +1 -1
  127. package/dist/maps/overlay.js +108 -28
  128. package/dist/maps/overlay.js.map +1 -1
  129. package/dist/maps/paint.d.ts +3 -0
  130. package/dist/maps/paint.d.ts.map +1 -1
  131. package/dist/maps/paint.js.map +1 -1
  132. package/dist/maps/proj.d.ts +14 -0
  133. package/dist/maps/proj.d.ts.map +1 -1
  134. package/dist/maps/proj.js +28 -0
  135. package/dist/maps/proj.js.map +1 -1
  136. package/dist/maps/renderer.d.ts +69 -0
  137. package/dist/maps/renderer.d.ts.map +1 -0
  138. package/dist/maps/renderer.js +66 -0
  139. package/dist/maps/renderer.js.map +1 -0
  140. package/dist/maps/sources.d.ts +25 -3
  141. package/dist/maps/sources.d.ts.map +1 -1
  142. package/dist/maps/sources.js +17 -0
  143. package/dist/maps/sources.js.map +1 -1
  144. package/dist/maps/theme.d.ts +19 -0
  145. package/dist/maps/theme.d.ts.map +1 -0
  146. package/dist/maps/theme.js +44 -0
  147. package/dist/maps/theme.js.map +1 -0
  148. package/dist/maps/tiles.d.ts +117 -17
  149. package/dist/maps/tiles.d.ts.map +1 -1
  150. package/dist/maps/tiles.js +163 -30
  151. package/dist/maps/tiles.js.map +1 -1
  152. package/dist/maps/types.d.ts +188 -20
  153. package/dist/maps/types.d.ts.map +1 -1
  154. package/dist/markdown/expressions.d.ts +1 -1
  155. package/dist/markdown/expressions.d.ts.map +1 -1
  156. package/dist/markdown/expressions.js.map +1 -1
  157. package/dist/markdown/index.d.ts +7 -6
  158. package/dist/markdown/index.d.ts.map +1 -1
  159. package/dist/markdown/index.js +9 -7
  160. package/dist/markdown/index.js.map +1 -1
  161. package/dist/markdown/spans.d.ts +1 -1
  162. package/dist/markdown/spans.d.ts.map +1 -1
  163. package/dist/media-player/index.d.ts +11 -3
  164. package/dist/media-player/index.d.ts.map +1 -1
  165. package/dist/media-player/index.js +13 -2
  166. package/dist/media-player/index.js.map +1 -1
  167. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  168. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  169. package/dist/rich-text-editor/clipboard.js +307 -0
  170. package/dist/rich-text-editor/clipboard.js.map +1 -0
  171. package/dist/rich-text-editor/collab.d.ts +32 -0
  172. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  173. package/dist/rich-text-editor/collab.js +55 -0
  174. package/dist/rich-text-editor/collab.js.map +1 -0
  175. package/dist/rich-text-editor/commands.d.ts +50 -0
  176. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  177. package/dist/rich-text-editor/commands.js +376 -0
  178. package/dist/rich-text-editor/commands.js.map +1 -0
  179. package/dist/rich-text-editor/drag.d.ts +68 -0
  180. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  181. package/dist/rich-text-editor/drag.js +148 -0
  182. package/dist/rich-text-editor/drag.js.map +1 -0
  183. package/dist/rich-text-editor/html.d.ts +145 -0
  184. package/dist/rich-text-editor/html.d.ts.map +1 -0
  185. package/dist/rich-text-editor/html.js +516 -0
  186. package/dist/rich-text-editor/html.js.map +1 -0
  187. package/dist/rich-text-editor/index.d.ts +263 -0
  188. package/dist/rich-text-editor/index.d.ts.map +1 -0
  189. package/dist/rich-text-editor/index.js +822 -0
  190. package/dist/rich-text-editor/index.js.map +1 -0
  191. package/dist/rich-text-editor/inline.d.ts +94 -0
  192. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  193. package/dist/rich-text-editor/inline.js +252 -0
  194. package/dist/rich-text-editor/inline.js.map +1 -0
  195. package/dist/rich-text-editor/keymap.d.ts +71 -0
  196. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  197. package/dist/rich-text-editor/keymap.js +393 -0
  198. package/dist/rich-text-editor/keymap.js.map +1 -0
  199. package/dist/rich-text-editor/keys.d.ts +29 -0
  200. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  201. package/dist/rich-text-editor/keys.js +93 -0
  202. package/dist/rich-text-editor/keys.js.map +1 -0
  203. package/dist/rich-text-editor/look.d.ts +71 -0
  204. package/dist/rich-text-editor/look.d.ts.map +1 -0
  205. package/dist/rich-text-editor/look.js +342 -0
  206. package/dist/rich-text-editor/look.js.map +1 -0
  207. package/dist/rich-text-editor/markdown.d.ts +18 -0
  208. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  209. package/dist/rich-text-editor/markdown.js +496 -0
  210. package/dist/rich-text-editor/markdown.js.map +1 -0
  211. package/dist/rich-text-editor/nodes.d.ts +135 -0
  212. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  213. package/dist/rich-text-editor/nodes.js +397 -0
  214. package/dist/rich-text-editor/nodes.js.map +1 -0
  215. package/dist/rich-text-editor/render.d.ts +68 -0
  216. package/dist/rich-text-editor/render.d.ts.map +1 -0
  217. package/dist/rich-text-editor/render.js +500 -0
  218. package/dist/rich-text-editor/render.js.map +1 -0
  219. package/dist/rich-text-editor/schema.d.ts +16 -0
  220. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  221. package/dist/rich-text-editor/schema.js +386 -0
  222. package/dist/rich-text-editor/schema.js.map +1 -0
  223. package/dist/rich-text-editor/store.d.ts +11 -0
  224. package/dist/rich-text-editor/store.d.ts.map +1 -0
  225. package/dist/rich-text-editor/store.js +43 -0
  226. package/dist/rich-text-editor/store.js.map +1 -0
  227. package/dist/rich-text-editor/suggest.d.ts +127 -0
  228. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  229. package/dist/rich-text-editor/suggest.js +0 -0
  230. package/dist/rich-text-editor/suggest.js.map +1 -0
  231. package/dist/rich-text-editor/tables.d.ts +41 -0
  232. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  233. package/dist/rich-text-editor/tables.js +204 -0
  234. package/dist/rich-text-editor/tables.js.map +1 -0
  235. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  236. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  237. package/dist/rich-text-editor/toolbar.js +548 -0
  238. package/dist/rich-text-editor/toolbar.js.map +1 -0
  239. package/dist/rich-text-editor/view.d.ts +368 -0
  240. package/dist/rich-text-editor/view.d.ts.map +1 -0
  241. package/dist/rich-text-editor/view.js +1968 -0
  242. package/dist/rich-text-editor/view.js.map +1 -0
  243. package/dist/rich-text-editor/virtual.d.ts +54 -0
  244. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  245. package/dist/rich-text-editor/virtual.js +262 -0
  246. package/dist/rich-text-editor/virtual.js.map +1 -0
  247. package/dist/richtext/node.d.ts +25 -2
  248. package/dist/richtext/node.d.ts.map +1 -1
  249. package/dist/richtext/node.js +46 -24
  250. package/dist/richtext/node.js.map +1 -1
  251. package/dist/table/index.d.ts.map +1 -1
  252. package/dist/table/index.js +14 -5
  253. package/dist/table/index.js.map +1 -1
  254. package/dist/terminal/backends.d.ts +4 -1
  255. package/dist/terminal/backends.d.ts.map +1 -1
  256. package/dist/terminal/backends.js.map +1 -1
  257. package/dist/terminal/index.d.ts +18 -7
  258. package/dist/terminal/index.d.ts.map +1 -1
  259. package/dist/terminal/index.js +28 -11
  260. package/dist/terminal/index.js.map +1 -1
  261. package/dist/terminal/vt/node.d.ts +56 -3
  262. package/dist/terminal/vt/node.d.ts.map +1 -1
  263. package/dist/terminal/vt/node.js +106 -26
  264. package/dist/terminal/vt/node.js.map +1 -1
  265. package/dist/terminal/vt/renderer.d.ts +4 -0
  266. package/dist/terminal/vt/renderer.d.ts.map +1 -1
  267. package/dist/terminal/vt/renderer.js +14 -5
  268. package/dist/terminal/vt/renderer.js.map +1 -1
  269. package/dist/tree/index.d.ts.map +1 -1
  270. package/dist/tree/index.js +6 -4
  271. package/dist/tree/index.js.map +1 -1
  272. package/package.json +23 -4
  273. package/src/charts/node.ts +6 -2
  274. package/src/color-picker/ColorPicker.ts +107 -81
  275. package/src/embed/client.ts +102 -8
  276. package/src/embed/index.ts +2 -0
  277. package/src/html/node.ts +7 -5
  278. package/src/index.ts +2 -0
  279. package/src/{markdown → internal/markdown}/ast.ts +24 -7
  280. package/src/{markdown → internal/markdown}/parse.ts +134 -24
  281. package/src/internal/markdown/stringify.ts +772 -0
  282. package/src/internal/scroll.ts +39 -18
  283. package/src/internal/units.ts +34 -0
  284. package/src/internal/window.ts +4 -2
  285. package/src/maps/anchors.ts +607 -0
  286. package/src/maps/controller.ts +999 -0
  287. package/src/maps/gl/buckets.ts +457 -0
  288. package/src/maps/gl/build-worker.ts +55 -0
  289. package/src/maps/gl/color.ts +62 -0
  290. package/src/maps/gl/cover.ts +170 -0
  291. package/src/maps/gl/index.ts +23 -0
  292. package/src/maps/gl/markers.ts +83 -0
  293. package/src/maps/gl/overlays.ts +379 -0
  294. package/src/maps/gl/pane.ts +42 -0
  295. package/src/maps/gl/placement.ts +816 -0
  296. package/src/maps/gl/renderer.ts +1576 -0
  297. package/src/maps/gl/shaders.ts +486 -0
  298. package/src/maps/gl/store.ts +566 -0
  299. package/src/maps/gl/text.ts +495 -0
  300. package/src/maps/gl/view.ts +1224 -0
  301. package/src/maps/index.ts +270 -81
  302. package/src/maps/labels.ts +214 -121
  303. package/src/maps/node.ts +763 -629
  304. package/src/maps/overlay.ts +148 -43
  305. package/src/maps/paint.ts +3 -0
  306. package/src/maps/proj.ts +31 -0
  307. package/src/maps/renderer.ts +155 -0
  308. package/src/maps/sources.ts +38 -3
  309. package/src/maps/theme.ts +51 -0
  310. package/src/maps/tiles.ts +191 -33
  311. package/src/maps/types.ts +188 -20
  312. package/src/markdown/expressions.ts +5 -1
  313. package/src/markdown/index.ts +14 -11
  314. package/src/markdown/spans.ts +1 -1
  315. package/src/media-player/index.ts +18 -4
  316. package/src/rich-text-editor/clipboard.ts +439 -0
  317. package/src/rich-text-editor/collab.ts +91 -0
  318. package/src/rich-text-editor/commands.ts +448 -0
  319. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  320. package/src/rich-text-editor/drag.ts +211 -0
  321. package/src/rich-text-editor/html.ts +619 -0
  322. package/src/rich-text-editor/index.ts +1331 -0
  323. package/src/rich-text-editor/inline.ts +345 -0
  324. package/src/rich-text-editor/keymap.ts +510 -0
  325. package/src/rich-text-editor/keys.ts +119 -0
  326. package/src/rich-text-editor/look.ts +427 -0
  327. package/src/rich-text-editor/markdown.ts +653 -0
  328. package/src/rich-text-editor/nodes.ts +497 -0
  329. package/src/rich-text-editor/render.ts +800 -0
  330. package/src/rich-text-editor/schema.ts +431 -0
  331. package/src/rich-text-editor/store.ts +39 -0
  332. package/src/rich-text-editor/suggest.ts +597 -0
  333. package/src/rich-text-editor/tables.ts +272 -0
  334. package/src/rich-text-editor/toolbar.ts +724 -0
  335. package/src/rich-text-editor/view.ts +2287 -0
  336. package/src/rich-text-editor/virtual.ts +347 -0
  337. package/src/richtext/node.ts +59 -30
  338. package/src/table/index.ts +14 -5
  339. package/src/terminal/backends.ts +4 -1
  340. package/src/terminal/index.ts +43 -15
  341. package/src/terminal/vt/node.ts +128 -29
  342. package/src/terminal/vt/renderer.ts +27 -5
  343. package/src/tree/index.ts +6 -4
  344. package/dist/markdown/ast.d.ts.map +0 -1
  345. package/dist/markdown/ast.js +0 -17
  346. package/dist/markdown/ast.js.map +0 -1
  347. package/dist/markdown/parse.d.ts.map +0 -1
  348. package/dist/markdown/parse.js.map +0 -1
  349. package/dist/markdown/tags.d.ts.map +0 -1
  350. package/dist/markdown/tags.js.map +0 -1
  351. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  352. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  353. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  354. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
@@ -0,0 +1,653 @@
1
+ // Markdown in and out of the editor's document, through the markdown AST.
2
+ //
3
+ // `src/internal/markdown/parse.ts` is `<Markdown>`'s own parser — GFM, with
4
+ // the same tolerance and the same deliberate deviations — so a note written
5
+ // in the editor and one rendered by `<Markdown>` agree about what the text
6
+ // means, and this file is only the translation between two trees: the AST's
7
+ // nested inline nodes and ProseMirror's flat mark sets. Going out, the flat
8
+ // marks are regrouped into the nesting that needs the fewest delimiters, and
9
+ // `stringify.ts` writes it.
10
+ //
11
+ // It speaks the reference schema's names (`./schema.ts`) — and TipTap's
12
+ // camelCase ones as well, so a schema built from TipTap's extensions round-
13
+ // trips through markdown without a codec of its own. A node neither knows is
14
+ // kept as what it contains: a textblock's text, a container's blocks. A
15
+ // schema with nodes markdown cannot say should pass a codec (`markdown`) or
16
+ // use `format="html"`, which goes through the schema's own `toDOM`.
17
+ import type {
18
+ Attrs,
19
+ Mark,
20
+ MarkType,
21
+ Node as PMNode,
22
+ NodeType,
23
+ Schema,
24
+ } from 'prosemirror-model';
25
+
26
+ import { parse } from '../internal/markdown/parse.js';
27
+ import { stringify } from '../internal/markdown/stringify.js';
28
+ import type {
29
+ BlockNode,
30
+ InlineNode,
31
+ LinkInline,
32
+ ListItem,
33
+ TableAlign,
34
+ } from '../internal/markdown/ast.js';
35
+
36
+ /** How a document becomes markdown and back — the default one, or an app's
37
+ * for a schema this one does not understand. */
38
+ export interface MarkdownCodec {
39
+ parse(source: string): PMNode;
40
+ serialize(doc: PMNode): string;
41
+ }
42
+
43
+ /** The codec for `schema`: the reference names, TipTap's, or both. */
44
+ export function markdownCodec(schema: Schema): MarkdownCodec {
45
+ return {
46
+ parse: (source) => docFromMarkdown(schema, source),
47
+ serialize: (doc) => markdownFromDoc(doc),
48
+ };
49
+ }
50
+
51
+ // --- names -------------------------------------------------------------------
52
+
53
+ /** The spellings each thing goes by: ProseMirror's reference schemas first,
54
+ * then TipTap's. */
55
+ const NODE_NAMES = {
56
+ paragraph: ['paragraph'],
57
+ heading: ['heading'],
58
+ code: ['code_block', 'codeBlock'],
59
+ quote: ['blockquote'],
60
+ bullet: ['bullet_list', 'bulletList'],
61
+ ordered: ['ordered_list', 'orderedList'],
62
+ task: ['task_list', 'taskList'],
63
+ item: ['list_item', 'listItem'],
64
+ taskItem: ['task_item', 'taskItem'],
65
+ rule: ['horizontal_rule', 'horizontalRule'],
66
+ table: ['table'],
67
+ row: ['table_row', 'tableRow'],
68
+ cell: ['table_cell', 'tableCell'],
69
+ header: ['table_header', 'tableHeader'],
70
+ image: ['image'],
71
+ hardBreak: ['hard_break', 'hardBreak'],
72
+ } as const;
73
+
74
+ const MARK_NAMES = {
75
+ strong: ['strong', 'bold'],
76
+ em: ['em', 'italic'],
77
+ del: ['strike', 's', 'del', 'strikethrough'],
78
+ code: ['code'],
79
+ link: ['link'],
80
+ } as const;
81
+
82
+ type NodeRole = keyof typeof NODE_NAMES;
83
+ type MarkRole = keyof typeof MARK_NAMES;
84
+
85
+ const NODE_ROLE = new Map<string, NodeRole>();
86
+ for (const [role, names] of Object.entries(NODE_NAMES)) {
87
+ for (const name of names) NODE_ROLE.set(name, role as NodeRole);
88
+ }
89
+ const MARK_ROLE = new Map<string, MarkRole>();
90
+ for (const [role, names] of Object.entries(MARK_NAMES)) {
91
+ for (const name of names) MARK_ROLE.set(name, role as MarkRole);
92
+ }
93
+
94
+ function nodeType(schema: Schema, role: NodeRole): NodeType | undefined {
95
+ for (const name of NODE_NAMES[role]) {
96
+ const type = schema.nodes[name];
97
+ if (type) return type;
98
+ }
99
+ return undefined;
100
+ }
101
+
102
+ function markType(schema: Schema, role: MarkRole): MarkType | undefined {
103
+ for (const name of MARK_NAMES[role]) {
104
+ const type = schema.marks[name];
105
+ if (type) return type;
106
+ }
107
+ return undefined;
108
+ }
109
+
110
+ /** Only the attributes `type` declares — TipTap's `orderedList` calls the
111
+ * start `start` where prosemirror-markdown calls it `order`, and a node
112
+ * should not be handed a name it does not have. */
113
+ function attrsFor(type: NodeType, wanted: Record<string, unknown>): Attrs {
114
+ const out: Record<string, unknown> = {};
115
+ const spec = type.spec.attrs ?? {};
116
+ for (const [name, value] of Object.entries(wanted)) {
117
+ if (name in spec) out[name] = value;
118
+ }
119
+ return out;
120
+ }
121
+
122
+ // --- markdown → document -----------------------------------------------------
123
+
124
+ /** Parse markdown into a document of `schema`. Never throws on content: what
125
+ * the schema cannot hold is kept as the nearest thing it can. */
126
+ export function docFromMarkdown(schema: Schema, source: string): PMNode {
127
+ const ast = parse(source, { partial: false });
128
+ const blocks = blocksToNodes(schema, ast.blocks);
129
+ return (
130
+ schema.topNodeType.createAndFill(null, blocks) ??
131
+ schema.topNodeType.createAndFill()!
132
+ );
133
+ }
134
+
135
+ function blocksToNodes(schema: Schema, blocks: readonly BlockNode[]): PMNode[] {
136
+ const out: PMNode[] = [];
137
+ for (const block of blocks) out.push(...blockToNodes(schema, block));
138
+ return out;
139
+ }
140
+
141
+ function paragraph(schema: Schema, content: PMNode[]): PMNode {
142
+ const type =
143
+ nodeType(schema, 'paragraph') ??
144
+ schema.topNodeType.contentMatch.defaultType!;
145
+ return type.createAndFill(null, content) ?? type.create();
146
+ }
147
+
148
+ function blockToNodes(schema: Schema, block: BlockNode): PMNode[] {
149
+ switch (block.type) {
150
+ case 'paragraph':
151
+ return [paragraph(schema, inlineToNodes(schema, block.children, []))];
152
+
153
+ case 'heading': {
154
+ const type = nodeType(schema, 'heading');
155
+ const content = inlineToNodes(schema, block.children, []);
156
+ if (!type) return [paragraph(schema, content)];
157
+ return [
158
+ type.createAndFill(attrsFor(type, { level: block.depth }), content) ??
159
+ paragraph(schema, content),
160
+ ];
161
+ }
162
+
163
+ case 'code': {
164
+ const type = nodeType(schema, 'code');
165
+ const text = block.text ? [schema.text(block.text)] : [];
166
+ if (!type) return [paragraph(schema, text)];
167
+ const info = block.info ?? block.lang;
168
+ return [
169
+ type.create(
170
+ attrsFor(type, {
171
+ params: info,
172
+ language: block.lang || null,
173
+ }),
174
+ text,
175
+ ),
176
+ ];
177
+ }
178
+
179
+ case 'quote': {
180
+ const type = nodeType(schema, 'quote');
181
+ const children = blocksToNodes(schema, block.children);
182
+ if (!type) return children;
183
+ return [
184
+ type.createAndFill(
185
+ null,
186
+ children.length ? children : [paragraph(schema, [])],
187
+ ) ?? paragraph(schema, []),
188
+ ];
189
+ }
190
+
191
+ case 'list':
192
+ return listToNodes(
193
+ schema,
194
+ block.ordered,
195
+ block.start,
196
+ block.tight,
197
+ block.items,
198
+ );
199
+
200
+ case 'table': {
201
+ const table = nodeType(schema, 'table');
202
+ const row = nodeType(schema, 'row');
203
+ const cell = nodeType(schema, 'cell');
204
+ const header = nodeType(schema, 'header') ?? cell;
205
+ if (!table || !row || !cell || !header) {
206
+ // no tables here: a row per paragraph, cells kept apart by a bar
207
+ return [block.header, ...block.rows].map((cells) =>
208
+ paragraph(
209
+ schema,
210
+ cells.flatMap((c, i) => [
211
+ ...(i > 0 ? [schema.text(' | ')] : []),
212
+ ...inlineToNodes(schema, c, []),
213
+ ]),
214
+ ),
215
+ );
216
+ }
217
+ const makeRow = (cells: InlineNode[][], type: NodeType): PMNode =>
218
+ row.create(
219
+ null,
220
+ cells.map(
221
+ (c, i) =>
222
+ type.createAndFill(
223
+ attrsFor(type, { align: block.align[i] ?? null }),
224
+ inlineToNodes(schema, c, []),
225
+ ) ?? type.createAndFill()!,
226
+ ),
227
+ );
228
+ return [
229
+ table.create(null, [
230
+ makeRow(block.header, header),
231
+ ...block.rows.map((r) => makeRow(r, cell)),
232
+ ]),
233
+ ];
234
+ }
235
+
236
+ case 'rule': {
237
+ const type = nodeType(schema, 'rule');
238
+ return type ? [type.create()] : [];
239
+ }
240
+
241
+ case 'component':
242
+ return blocksToNodes(schema, block.children);
243
+ }
244
+ }
245
+
246
+ function listToNodes(
247
+ schema: Schema,
248
+ ordered: boolean,
249
+ start: number,
250
+ tight: boolean,
251
+ items: readonly ListItem[],
252
+ ): PMNode[] {
253
+ const isTask =
254
+ !ordered && items.length > 0 && items.every((i) => i.checked !== null);
255
+ // TipTap keeps task items in a list type of their own; the reference
256
+ // schema (and this one) keeps them in a bullet list, as GFM does
257
+ const taskList = isTask ? nodeType(schema, 'task') : undefined;
258
+ const listType = taskList ?? nodeType(schema, ordered ? 'ordered' : 'bullet');
259
+ const itemType =
260
+ (taskList ? nodeType(schema, 'taskItem') : undefined) ??
261
+ nodeType(schema, 'item');
262
+ if (!listType || !itemType) {
263
+ return items.flatMap((item) => blocksToNodes(schema, item.children));
264
+ }
265
+ const nodes = items.map((item) => {
266
+ let children = blocksToNodes(schema, item.children);
267
+ // an item's first child must be a paragraph (`paragraph block*`); one
268
+ // that opened with a fence or a nested list gets an empty paragraph in
269
+ // front of it, which is what that markdown looks like anyway
270
+ const first = itemType.contentMatch.defaultType;
271
+ if (first && (children.length === 0 || children[0].type !== first)) {
272
+ children = [first.createAndFill()!, ...children];
273
+ }
274
+ return (
275
+ itemType.createAndFill(
276
+ attrsFor(itemType, { checked: item.checked }),
277
+ children,
278
+ ) ?? itemType.createAndFill()!
279
+ );
280
+ });
281
+ return [
282
+ listType.create(attrsFor(listType, { order: start, start, tight }), nodes),
283
+ ];
284
+ }
285
+
286
+ function withMark(
287
+ marks: readonly Mark[],
288
+ type: MarkType | undefined,
289
+ attrs?: Attrs,
290
+ ): readonly Mark[] {
291
+ return type ? type.create(attrs).addToSet(marks) : marks;
292
+ }
293
+
294
+ function plainOf(nodes: readonly InlineNode[]): string {
295
+ let out = '';
296
+ for (const node of nodes) {
297
+ if (node.type === 'text' || node.type === 'code') out += node.text;
298
+ else if (node.type === 'break') out += ' ';
299
+ else if ('children' in node) out += plainOf(node.children);
300
+ }
301
+ return out;
302
+ }
303
+
304
+ function inlineToNodes(
305
+ schema: Schema,
306
+ nodes: readonly InlineNode[],
307
+ marks: readonly Mark[],
308
+ ): PMNode[] {
309
+ const out: PMNode[] = [];
310
+ for (const node of nodes) {
311
+ switch (node.type) {
312
+ case 'text':
313
+ if (node.text) out.push(schema.text(node.text, marks));
314
+ break;
315
+ case 'code':
316
+ if (node.text) {
317
+ out.push(
318
+ schema.text(node.text, withMark(marks, markType(schema, 'code'))),
319
+ );
320
+ }
321
+ break;
322
+ case 'strong':
323
+ out.push(
324
+ ...inlineToNodes(
325
+ schema,
326
+ node.children,
327
+ withMark(marks, markType(schema, 'strong')),
328
+ ),
329
+ );
330
+ break;
331
+ case 'em':
332
+ out.push(
333
+ ...inlineToNodes(
334
+ schema,
335
+ node.children,
336
+ withMark(marks, markType(schema, 'em')),
337
+ ),
338
+ );
339
+ break;
340
+ case 'del':
341
+ out.push(
342
+ ...inlineToNodes(
343
+ schema,
344
+ node.children,
345
+ withMark(marks, markType(schema, 'del')),
346
+ ),
347
+ );
348
+ break;
349
+ case 'link':
350
+ out.push(...linkToNodes(schema, node, marks));
351
+ break;
352
+ case 'break': {
353
+ const type = nodeType(schema, 'hardBreak');
354
+ if (type) out.push(type.create(null, null, marks));
355
+ else out.push(schema.text(' ', marks));
356
+ break;
357
+ }
358
+ case 'expression':
359
+ out.push(schema.text(`{${node.src}}`, marks));
360
+ break;
361
+ case 'component':
362
+ out.push(...inlineToNodes(schema, node.children, marks));
363
+ break;
364
+ }
365
+ }
366
+ return out;
367
+ }
368
+
369
+ function linkToNodes(
370
+ schema: Schema,
371
+ node: LinkInline,
372
+ marks: readonly Mark[],
373
+ ): PMNode[] {
374
+ if (node.image) {
375
+ const type = nodeType(schema, 'image');
376
+ const alt = plainOf(node.children);
377
+ if (type && node.href !== null) {
378
+ return [
379
+ type.create(
380
+ attrsFor(type, {
381
+ src: node.href,
382
+ alt: alt || null,
383
+ title: node.title ?? null,
384
+ }),
385
+ null,
386
+ marks,
387
+ ),
388
+ ];
389
+ }
390
+ return alt ? [schema.text(alt, marks)] : [];
391
+ }
392
+ const link = markType(schema, 'link');
393
+ const inner =
394
+ node.href !== null && link
395
+ ? withMark(marks, link, { href: node.href, title: node.title ?? null })
396
+ : marks;
397
+ return inlineToNodes(schema, node.children, inner);
398
+ }
399
+
400
+ // --- document → markdown -----------------------------------------------------
401
+
402
+ /** Write a document of the reference (or TipTap's) schema as markdown. */
403
+ export function markdownFromDoc(doc: PMNode): string {
404
+ return stringify(blocksOf(doc));
405
+ }
406
+
407
+ function blocksOf(parent: PMNode): BlockNode[] {
408
+ const out: BlockNode[] = [];
409
+ parent.forEach((child) => out.push(...nodeToBlocks(child)));
410
+ return out;
411
+ }
412
+
413
+ function nodeToBlocks(node: PMNode): BlockNode[] {
414
+ switch (NODE_ROLE.get(node.type.name)) {
415
+ case 'paragraph':
416
+ return [{ type: 'paragraph', children: inlineOf(node) }];
417
+ case 'heading':
418
+ return [
419
+ {
420
+ type: 'heading',
421
+ depth: Number(node.attrs.level ?? 1),
422
+ children: inlineOf(node),
423
+ },
424
+ ];
425
+ case 'code': {
426
+ const info = String(
427
+ node.attrs.params ?? node.attrs.language ?? '',
428
+ ).trim();
429
+ const lang = (info.split(/[ \t]/, 1)[0] ?? '').toLowerCase();
430
+ return [
431
+ {
432
+ type: 'code',
433
+ lang,
434
+ ...(info !== lang ? { info } : null),
435
+ text: node.textContent,
436
+ closed: true,
437
+ },
438
+ ];
439
+ }
440
+ case 'quote':
441
+ return [{ type: 'quote', children: blocksOf(node) }];
442
+ case 'bullet':
443
+ case 'ordered':
444
+ case 'task': {
445
+ const ordered = NODE_ROLE.get(node.type.name) === 'ordered';
446
+ const items: ListItem[] = [];
447
+ node.forEach((item) => {
448
+ const checked = item.attrs.checked;
449
+ items.push({
450
+ checked:
451
+ typeof checked === 'boolean'
452
+ ? checked
453
+ : NODE_ROLE.get(item.type.name) === 'taskItem'
454
+ ? false
455
+ : null,
456
+ children: blocksOf(item),
457
+ });
458
+ });
459
+ return [
460
+ {
461
+ type: 'list',
462
+ ordered,
463
+ start: Number(node.attrs.order ?? node.attrs.start ?? 1),
464
+ tight: node.attrs.tight !== false,
465
+ items,
466
+ },
467
+ ];
468
+ }
469
+ case 'rule':
470
+ return [{ type: 'rule' }];
471
+ case 'table':
472
+ return [tableOf(node)];
473
+ default:
474
+ if (node.isTextblock)
475
+ return [{ type: 'paragraph', children: inlineOf(node) }];
476
+ if (!node.isLeaf) return blocksOf(node);
477
+ return [];
478
+ }
479
+ }
480
+
481
+ function tableOf(table: PMNode): BlockNode {
482
+ const grid: InlineNode[][][] = [];
483
+ const align: TableAlign[] = [];
484
+ table.forEach((row) => {
485
+ const cells: InlineNode[][] = [];
486
+ row.forEach((cell) => {
487
+ const col = cells.length;
488
+ if (align[col] === undefined) {
489
+ const a = cell.attrs.align;
490
+ align[col] = a === 'left' || a === 'center' || a === 'right' ? a : null;
491
+ }
492
+ cells.push(cellInline(cell));
493
+ });
494
+ grid.push(cells);
495
+ });
496
+ const width = Math.max(1, ...grid.map((r) => r.length));
497
+ const pad = (r: InlineNode[][]): InlineNode[][] =>
498
+ Array.from({ length: width }, (_, i) => r[i] ?? []);
499
+ for (let i = 0; i < width; i++) align[i] ??= null;
500
+ return {
501
+ type: 'table',
502
+ align: align.slice(0, width),
503
+ header: pad(grid[0] ?? []),
504
+ rows: grid.slice(1).map(pad),
505
+ };
506
+ }
507
+
508
+ /** A cell's content as one line: its own inline content, or — for a schema
509
+ * whose cells hold blocks — each textblock's, a space between. */
510
+ function cellInline(cell: PMNode): InlineNode[] {
511
+ if (cell.isTextblock) return inlineOf(cell);
512
+ const out: InlineNode[] = [];
513
+ cell.descendants((node) => {
514
+ if (!node.isTextblock) return true;
515
+ if (out.length) out.push({ type: 'text', text: ' ' });
516
+ out.push(...inlineOf(node));
517
+ return false;
518
+ });
519
+ return out;
520
+ }
521
+
522
+ interface OpenMark {
523
+ mark: Mark;
524
+ children: InlineNode[];
525
+ }
526
+
527
+ /**
528
+ * A textblock's flat marked runs, as the AST's nested inline tree.
529
+ *
530
+ * The open marks are a stack. At each child, every mark the child no longer
531
+ * carries is closed — with everything opened inside it, to be reopened —
532
+ * and the marks it adds are opened, **the longest-running outermost**, so
533
+ * that "bold, then bold italic, then bold" is one bold around an italic and
534
+ * not two bolds either side of one: two runs of `**` meeting reads back as
535
+ * one run of four. Code is never a container: it is the leaf, innermost,
536
+ * where markdown needs it.
537
+ */
538
+ function inlineOf(block: PMNode): InlineNode[] {
539
+ const children: PMNode[] = [];
540
+ block.forEach((child) => children.push(child));
541
+ const containers = children.map((child) =>
542
+ child.marks.filter((m) => {
543
+ const role = MARK_ROLE.get(m.type.name);
544
+ return role !== undefined && role !== 'code';
545
+ }),
546
+ );
547
+ /** The last index, from `i` on, through which `mark` runs unbroken. */
548
+ const runsTo = (i: number, mark: Mark): number => {
549
+ let j = i;
550
+ while (j + 1 < children.length && mark.isInSet(containers[j + 1])) j++;
551
+ return j;
552
+ };
553
+
554
+ const root: InlineNode[] = [];
555
+ const stack: OpenMark[] = [];
556
+ const top = (): InlineNode[] =>
557
+ stack.length ? stack[stack.length - 1].children : root;
558
+
559
+ children.forEach((child, i) => {
560
+ const want = containers[i];
561
+ let keep = 0;
562
+ while (keep < stack.length && stack[keep].mark.isInSet(want)) keep++;
563
+ stack.length = keep;
564
+ // Longest-running first; between two that run as far, emphasis outside
565
+ // a link — `~~[ a](u)~~` can hold the space at the link's edge, while
566
+ // `[~~ a~~](u)` would have to move it out of the strikethrough.
567
+ const opening = want
568
+ .filter((m) => !stack.some((open) => open.mark.eq(m)))
569
+ .map((m, order) => ({
570
+ m,
571
+ end: runsTo(i, m),
572
+ link: MARK_ROLE.get(m.type.name) === 'link' ? 1 : 0,
573
+ order,
574
+ }))
575
+ .sort((a, b) => b.end - a.end || a.link - b.link || a.order - b.order);
576
+ for (const { m } of opening) {
577
+ const inner: InlineNode[] = [];
578
+ top().push(containerOf(m, inner));
579
+ stack.push({ mark: m, children: inner });
580
+ }
581
+ const code = child.marks.some((m) => MARK_ROLE.get(m.type.name) === 'code');
582
+ top().push(...leafOf(child, code));
583
+ });
584
+ return root;
585
+ }
586
+
587
+ function containerOf(mark: Mark, children: InlineNode[]): InlineNode {
588
+ switch (MARK_ROLE.get(mark.type.name)) {
589
+ case 'strong':
590
+ return { type: 'strong', children };
591
+ case 'em':
592
+ return { type: 'em', children };
593
+ case 'del':
594
+ return { type: 'del', children };
595
+ default: {
596
+ const title = mark.attrs.title;
597
+ return {
598
+ type: 'link',
599
+ href: String(mark.attrs.href ?? ''),
600
+ image: false,
601
+ children,
602
+ ...(typeof title === 'string' && title ? { title } : null),
603
+ };
604
+ }
605
+ }
606
+ }
607
+
608
+ function leafOf(node: PMNode, code: boolean): InlineNode[] {
609
+ if (node.isText) {
610
+ const text = node.text ?? '';
611
+ return [code ? { type: 'code', text } : { type: 'text', text }];
612
+ }
613
+ const role = NODE_ROLE.get(node.type.name);
614
+ if (role === 'hardBreak') return [{ type: 'break' }];
615
+ if (role === 'image') {
616
+ const alt = node.attrs.alt;
617
+ const title = node.attrs.title;
618
+ return [
619
+ {
620
+ type: 'link',
621
+ href: String(node.attrs.src ?? ''),
622
+ image: true,
623
+ children:
624
+ typeof alt === 'string' && alt ? [{ type: 'text', text: alt }] : [],
625
+ ...(typeof title === 'string' && title ? { title } : null),
626
+ },
627
+ ];
628
+ }
629
+ // an inline node markdown has no word for: its text, if it has any
630
+ const leaf = node.type.spec.leafText?.(node) ?? node.textContent;
631
+ return leaf ? [{ type: 'text', text: leaf }] : [];
632
+ }
633
+
634
+ // --- plain text ----------------------------------------------------------------
635
+
636
+ /** One textblock per line, both ways — `format="text"`. */
637
+ export function docFromText(schema: Schema, text: string): PMNode {
638
+ const lines = text.replace(/\r\n?/g, '\n').split('\n');
639
+ return (
640
+ schema.topNodeType.createAndFill(
641
+ null,
642
+ lines.map((line) => paragraph(schema, line ? [schema.text(line)] : [])),
643
+ ) ?? schema.topNodeType.createAndFill()!
644
+ );
645
+ }
646
+
647
+ export function textFromDoc(doc: PMNode): string {
648
+ return doc.textBetween(0, doc.content.size, '\n', (leaf) =>
649
+ NODE_ROLE.get(leaf.type.name) === 'hardBreak'
650
+ ? '\n'
651
+ : (leaf.type.spec.leafText?.(leaf) ?? ''),
652
+ );
653
+ }