@react-x11/components 0.8.0 → 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 (323) hide show
  1. package/README.md +95 -34
  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/embed/client.d.ts +48 -3
  7. package/dist/embed/client.d.ts.map +1 -1
  8. package/dist/embed/client.js +79 -5
  9. package/dist/embed/client.js.map +1 -1
  10. package/dist/embed/index.d.ts +1 -1
  11. package/dist/embed/index.d.ts.map +1 -1
  12. package/dist/embed/index.js +1 -1
  13. package/dist/embed/index.js.map +1 -1
  14. package/dist/html/node.d.ts +3 -1
  15. package/dist/html/node.d.ts.map +1 -1
  16. package/dist/html/node.js +7 -5
  17. package/dist/html/node.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
  23. package/dist/internal/markdown/ast.d.ts.map +1 -0
  24. package/dist/internal/markdown/ast.js +22 -0
  25. package/dist/internal/markdown/ast.js.map +1 -0
  26. package/dist/internal/markdown/parse.d.ts.map +1 -0
  27. package/dist/{markdown → internal/markdown}/parse.js +112 -22
  28. package/dist/internal/markdown/parse.js.map +1 -0
  29. package/dist/internal/markdown/stringify.d.ts +4 -0
  30. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  31. package/dist/internal/markdown/stringify.js +684 -0
  32. package/dist/internal/markdown/stringify.js.map +1 -0
  33. package/dist/internal/markdown/tags.d.ts.map +1 -0
  34. package/dist/internal/markdown/tags.js.map +1 -0
  35. package/dist/internal/scroll.d.ts +3 -2
  36. package/dist/internal/scroll.d.ts.map +1 -1
  37. package/dist/internal/scroll.js +1 -0
  38. package/dist/internal/scroll.js.map +1 -1
  39. package/dist/maps/anchors.d.ts +98 -0
  40. package/dist/maps/anchors.d.ts.map +1 -0
  41. package/dist/maps/anchors.js +531 -0
  42. package/dist/maps/anchors.js.map +1 -0
  43. package/dist/maps/controller.d.ts +283 -0
  44. package/dist/maps/controller.d.ts.map +1 -0
  45. package/dist/maps/controller.js +743 -0
  46. package/dist/maps/controller.js.map +1 -0
  47. package/dist/maps/gl/buckets.d.ts +95 -0
  48. package/dist/maps/gl/buckets.d.ts.map +1 -0
  49. package/dist/maps/gl/buckets.js +362 -0
  50. package/dist/maps/gl/buckets.js.map +1 -0
  51. package/dist/maps/gl/build-worker.d.ts +13 -0
  52. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  53. package/dist/maps/gl/build-worker.js +44 -0
  54. package/dist/maps/gl/build-worker.js.map +1 -0
  55. package/dist/maps/gl/color.d.ts +8 -0
  56. package/dist/maps/gl/color.d.ts.map +1 -0
  57. package/dist/maps/gl/color.js +57 -0
  58. package/dist/maps/gl/color.js.map +1 -0
  59. package/dist/maps/gl/cover.d.ts +51 -0
  60. package/dist/maps/gl/cover.d.ts.map +1 -0
  61. package/dist/maps/gl/cover.js +124 -0
  62. package/dist/maps/gl/cover.js.map +1 -0
  63. package/dist/maps/gl/index.d.ts +3 -0
  64. package/dist/maps/gl/index.d.ts.map +1 -0
  65. package/dist/maps/gl/index.js +20 -0
  66. package/dist/maps/gl/index.js.map +1 -0
  67. package/dist/maps/gl/markers.d.ts +28 -0
  68. package/dist/maps/gl/markers.d.ts.map +1 -0
  69. package/dist/maps/gl/markers.js +60 -0
  70. package/dist/maps/gl/markers.js.map +1 -0
  71. package/dist/maps/gl/overlays.d.ts +68 -0
  72. package/dist/maps/gl/overlays.d.ts.map +1 -0
  73. package/dist/maps/gl/overlays.js +300 -0
  74. package/dist/maps/gl/overlays.js.map +1 -0
  75. package/dist/maps/gl/pane.d.ts +12 -0
  76. package/dist/maps/gl/pane.d.ts.map +1 -0
  77. package/dist/maps/gl/pane.js +37 -0
  78. package/dist/maps/gl/pane.js.map +1 -0
  79. package/dist/maps/gl/placement.d.ts +111 -0
  80. package/dist/maps/gl/placement.d.ts.map +1 -0
  81. package/dist/maps/gl/placement.js +567 -0
  82. package/dist/maps/gl/placement.js.map +1 -0
  83. package/dist/maps/gl/renderer.d.ts +352 -0
  84. package/dist/maps/gl/renderer.d.ts.map +1 -0
  85. package/dist/maps/gl/renderer.js +1127 -0
  86. package/dist/maps/gl/renderer.js.map +1 -0
  87. package/dist/maps/gl/shaders.d.ts +120 -0
  88. package/dist/maps/gl/shaders.d.ts.map +1 -0
  89. package/dist/maps/gl/shaders.js +451 -0
  90. package/dist/maps/gl/shaders.js.map +1 -0
  91. package/dist/maps/gl/store.d.ts +115 -0
  92. package/dist/maps/gl/store.d.ts.map +1 -0
  93. package/dist/maps/gl/store.js +469 -0
  94. package/dist/maps/gl/store.js.map +1 -0
  95. package/dist/maps/gl/text.d.ts +154 -0
  96. package/dist/maps/gl/text.d.ts.map +1 -0
  97. package/dist/maps/gl/text.js +382 -0
  98. package/dist/maps/gl/text.js.map +1 -0
  99. package/dist/maps/gl/view.d.ts +54 -0
  100. package/dist/maps/gl/view.d.ts.map +1 -0
  101. package/dist/maps/gl/view.js +974 -0
  102. package/dist/maps/gl/view.js.map +1 -0
  103. package/dist/maps/index.d.ts +1 -1
  104. package/dist/maps/index.d.ts.map +1 -1
  105. package/dist/maps/index.js +216 -52
  106. package/dist/maps/index.js.map +1 -1
  107. package/dist/maps/labels.d.ts +37 -16
  108. package/dist/maps/labels.d.ts.map +1 -1
  109. package/dist/maps/labels.js +188 -119
  110. package/dist/maps/labels.js.map +1 -1
  111. package/dist/maps/node.d.ts +162 -56
  112. package/dist/maps/node.d.ts.map +1 -1
  113. package/dist/maps/node.js +607 -515
  114. package/dist/maps/node.js.map +1 -1
  115. package/dist/maps/overlay.d.ts +72 -9
  116. package/dist/maps/overlay.d.ts.map +1 -1
  117. package/dist/maps/overlay.js +108 -28
  118. package/dist/maps/overlay.js.map +1 -1
  119. package/dist/maps/paint.d.ts +3 -0
  120. package/dist/maps/paint.d.ts.map +1 -1
  121. package/dist/maps/paint.js.map +1 -1
  122. package/dist/maps/proj.d.ts +14 -0
  123. package/dist/maps/proj.d.ts.map +1 -1
  124. package/dist/maps/proj.js +28 -0
  125. package/dist/maps/proj.js.map +1 -1
  126. package/dist/maps/renderer.d.ts +69 -0
  127. package/dist/maps/renderer.d.ts.map +1 -0
  128. package/dist/maps/renderer.js +66 -0
  129. package/dist/maps/renderer.js.map +1 -0
  130. package/dist/maps/sources.d.ts +25 -3
  131. package/dist/maps/sources.d.ts.map +1 -1
  132. package/dist/maps/sources.js +17 -0
  133. package/dist/maps/sources.js.map +1 -1
  134. package/dist/maps/theme.d.ts +19 -0
  135. package/dist/maps/theme.d.ts.map +1 -0
  136. package/dist/maps/theme.js +44 -0
  137. package/dist/maps/theme.js.map +1 -0
  138. package/dist/maps/tiles.d.ts +117 -17
  139. package/dist/maps/tiles.d.ts.map +1 -1
  140. package/dist/maps/tiles.js +163 -30
  141. package/dist/maps/tiles.js.map +1 -1
  142. package/dist/maps/types.d.ts +188 -20
  143. package/dist/maps/types.d.ts.map +1 -1
  144. package/dist/markdown/expressions.d.ts +1 -1
  145. package/dist/markdown/expressions.d.ts.map +1 -1
  146. package/dist/markdown/expressions.js.map +1 -1
  147. package/dist/markdown/index.d.ts +7 -6
  148. package/dist/markdown/index.d.ts.map +1 -1
  149. package/dist/markdown/index.js +9 -7
  150. package/dist/markdown/index.js.map +1 -1
  151. package/dist/markdown/spans.d.ts +1 -1
  152. package/dist/markdown/spans.d.ts.map +1 -1
  153. package/dist/media-player/index.d.ts +11 -3
  154. package/dist/media-player/index.d.ts.map +1 -1
  155. package/dist/media-player/index.js +13 -2
  156. package/dist/media-player/index.js.map +1 -1
  157. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  158. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  159. package/dist/rich-text-editor/clipboard.js +307 -0
  160. package/dist/rich-text-editor/clipboard.js.map +1 -0
  161. package/dist/rich-text-editor/collab.d.ts +32 -0
  162. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  163. package/dist/rich-text-editor/collab.js +55 -0
  164. package/dist/rich-text-editor/collab.js.map +1 -0
  165. package/dist/rich-text-editor/commands.d.ts +50 -0
  166. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  167. package/dist/rich-text-editor/commands.js +376 -0
  168. package/dist/rich-text-editor/commands.js.map +1 -0
  169. package/dist/rich-text-editor/drag.d.ts +68 -0
  170. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  171. package/dist/rich-text-editor/drag.js +148 -0
  172. package/dist/rich-text-editor/drag.js.map +1 -0
  173. package/dist/rich-text-editor/html.d.ts +145 -0
  174. package/dist/rich-text-editor/html.d.ts.map +1 -0
  175. package/dist/rich-text-editor/html.js +516 -0
  176. package/dist/rich-text-editor/html.js.map +1 -0
  177. package/dist/rich-text-editor/index.d.ts +263 -0
  178. package/dist/rich-text-editor/index.d.ts.map +1 -0
  179. package/dist/rich-text-editor/index.js +822 -0
  180. package/dist/rich-text-editor/index.js.map +1 -0
  181. package/dist/rich-text-editor/inline.d.ts +94 -0
  182. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  183. package/dist/rich-text-editor/inline.js +252 -0
  184. package/dist/rich-text-editor/inline.js.map +1 -0
  185. package/dist/rich-text-editor/keymap.d.ts +71 -0
  186. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  187. package/dist/rich-text-editor/keymap.js +393 -0
  188. package/dist/rich-text-editor/keymap.js.map +1 -0
  189. package/dist/rich-text-editor/keys.d.ts +29 -0
  190. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  191. package/dist/rich-text-editor/keys.js +93 -0
  192. package/dist/rich-text-editor/keys.js.map +1 -0
  193. package/dist/rich-text-editor/look.d.ts +71 -0
  194. package/dist/rich-text-editor/look.d.ts.map +1 -0
  195. package/dist/rich-text-editor/look.js +342 -0
  196. package/dist/rich-text-editor/look.js.map +1 -0
  197. package/dist/rich-text-editor/markdown.d.ts +18 -0
  198. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  199. package/dist/rich-text-editor/markdown.js +496 -0
  200. package/dist/rich-text-editor/markdown.js.map +1 -0
  201. package/dist/rich-text-editor/nodes.d.ts +135 -0
  202. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  203. package/dist/rich-text-editor/nodes.js +397 -0
  204. package/dist/rich-text-editor/nodes.js.map +1 -0
  205. package/dist/rich-text-editor/render.d.ts +68 -0
  206. package/dist/rich-text-editor/render.d.ts.map +1 -0
  207. package/dist/rich-text-editor/render.js +500 -0
  208. package/dist/rich-text-editor/render.js.map +1 -0
  209. package/dist/rich-text-editor/schema.d.ts +16 -0
  210. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  211. package/dist/rich-text-editor/schema.js +386 -0
  212. package/dist/rich-text-editor/schema.js.map +1 -0
  213. package/dist/rich-text-editor/store.d.ts +11 -0
  214. package/dist/rich-text-editor/store.d.ts.map +1 -0
  215. package/dist/rich-text-editor/store.js +43 -0
  216. package/dist/rich-text-editor/store.js.map +1 -0
  217. package/dist/rich-text-editor/suggest.d.ts +127 -0
  218. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  219. package/dist/rich-text-editor/suggest.js +0 -0
  220. package/dist/rich-text-editor/suggest.js.map +1 -0
  221. package/dist/rich-text-editor/tables.d.ts +41 -0
  222. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  223. package/dist/rich-text-editor/tables.js +204 -0
  224. package/dist/rich-text-editor/tables.js.map +1 -0
  225. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  226. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  227. package/dist/rich-text-editor/toolbar.js +548 -0
  228. package/dist/rich-text-editor/toolbar.js.map +1 -0
  229. package/dist/rich-text-editor/view.d.ts +368 -0
  230. package/dist/rich-text-editor/view.d.ts.map +1 -0
  231. package/dist/rich-text-editor/view.js +1968 -0
  232. package/dist/rich-text-editor/view.js.map +1 -0
  233. package/dist/rich-text-editor/virtual.d.ts +54 -0
  234. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  235. package/dist/rich-text-editor/virtual.js +262 -0
  236. package/dist/rich-text-editor/virtual.js.map +1 -0
  237. package/dist/richtext/node.d.ts +25 -2
  238. package/dist/richtext/node.d.ts.map +1 -1
  239. package/dist/richtext/node.js +46 -24
  240. package/dist/richtext/node.js.map +1 -1
  241. package/dist/terminal/backends.d.ts +4 -1
  242. package/dist/terminal/backends.d.ts.map +1 -1
  243. package/dist/terminal/backends.js.map +1 -1
  244. package/dist/terminal/index.d.ts +18 -7
  245. package/dist/terminal/index.d.ts.map +1 -1
  246. package/dist/terminal/index.js +28 -11
  247. package/dist/terminal/index.js.map +1 -1
  248. package/package.json +23 -4
  249. package/src/charts/node.ts +6 -2
  250. package/src/embed/client.ts +102 -8
  251. package/src/embed/index.ts +2 -0
  252. package/src/html/node.ts +7 -5
  253. package/src/index.ts +2 -0
  254. package/src/{markdown → internal/markdown}/ast.ts +24 -7
  255. package/src/{markdown → internal/markdown}/parse.ts +134 -24
  256. package/src/internal/markdown/stringify.ts +772 -0
  257. package/src/internal/scroll.ts +5 -3
  258. package/src/maps/anchors.ts +607 -0
  259. package/src/maps/controller.ts +999 -0
  260. package/src/maps/gl/buckets.ts +457 -0
  261. package/src/maps/gl/build-worker.ts +55 -0
  262. package/src/maps/gl/color.ts +62 -0
  263. package/src/maps/gl/cover.ts +170 -0
  264. package/src/maps/gl/index.ts +23 -0
  265. package/src/maps/gl/markers.ts +83 -0
  266. package/src/maps/gl/overlays.ts +379 -0
  267. package/src/maps/gl/pane.ts +42 -0
  268. package/src/maps/gl/placement.ts +816 -0
  269. package/src/maps/gl/renderer.ts +1576 -0
  270. package/src/maps/gl/shaders.ts +486 -0
  271. package/src/maps/gl/store.ts +566 -0
  272. package/src/maps/gl/text.ts +495 -0
  273. package/src/maps/gl/view.ts +1224 -0
  274. package/src/maps/index.ts +270 -81
  275. package/src/maps/labels.ts +214 -121
  276. package/src/maps/node.ts +763 -629
  277. package/src/maps/overlay.ts +148 -43
  278. package/src/maps/paint.ts +3 -0
  279. package/src/maps/proj.ts +31 -0
  280. package/src/maps/renderer.ts +155 -0
  281. package/src/maps/sources.ts +38 -3
  282. package/src/maps/theme.ts +51 -0
  283. package/src/maps/tiles.ts +191 -33
  284. package/src/maps/types.ts +188 -20
  285. package/src/markdown/expressions.ts +5 -1
  286. package/src/markdown/index.ts +14 -11
  287. package/src/markdown/spans.ts +1 -1
  288. package/src/media-player/index.ts +18 -4
  289. package/src/rich-text-editor/clipboard.ts +439 -0
  290. package/src/rich-text-editor/collab.ts +91 -0
  291. package/src/rich-text-editor/commands.ts +448 -0
  292. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  293. package/src/rich-text-editor/drag.ts +211 -0
  294. package/src/rich-text-editor/html.ts +619 -0
  295. package/src/rich-text-editor/index.ts +1331 -0
  296. package/src/rich-text-editor/inline.ts +345 -0
  297. package/src/rich-text-editor/keymap.ts +510 -0
  298. package/src/rich-text-editor/keys.ts +119 -0
  299. package/src/rich-text-editor/look.ts +427 -0
  300. package/src/rich-text-editor/markdown.ts +653 -0
  301. package/src/rich-text-editor/nodes.ts +497 -0
  302. package/src/rich-text-editor/render.ts +800 -0
  303. package/src/rich-text-editor/schema.ts +431 -0
  304. package/src/rich-text-editor/store.ts +39 -0
  305. package/src/rich-text-editor/suggest.ts +597 -0
  306. package/src/rich-text-editor/tables.ts +272 -0
  307. package/src/rich-text-editor/toolbar.ts +724 -0
  308. package/src/rich-text-editor/view.ts +2287 -0
  309. package/src/rich-text-editor/virtual.ts +347 -0
  310. package/src/richtext/node.ts +59 -30
  311. package/src/terminal/backends.ts +4 -1
  312. package/src/terminal/index.ts +43 -15
  313. package/dist/markdown/ast.d.ts.map +0 -1
  314. package/dist/markdown/ast.js +0 -17
  315. package/dist/markdown/ast.js.map +0 -1
  316. package/dist/markdown/parse.d.ts.map +0 -1
  317. package/dist/markdown/parse.js.map +0 -1
  318. package/dist/markdown/tags.d.ts.map +0 -1
  319. package/dist/markdown/tags.js.map +0 -1
  320. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  321. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  322. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  323. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
@@ -0,0 +1,431 @@
1
+ // The document the editor edits when the app does not bring one: GitHub-
2
+ // flavoured markdown, written in ProseMirror's reference vocabulary.
3
+ //
4
+ // The node and mark names are prosemirror-schema-basic's and
5
+ // prosemirror-schema-list's, with prosemirror-markdown's attributes (`order`,
6
+ // `tight`, `params`) — so a command, an input rule or a plugin written
7
+ // against the reference schema works on this one unchanged. What GFM adds is
8
+ // named the way the ecosystem already names it: `strike`, a `checked`
9
+ // attribute on `list_item` for task items, and table nodes whose `tableRole`s
10
+ // and cell attributes are prosemirror-tables' own, so that package's commands
11
+ // can be pointed at a document of this schema.
12
+ //
13
+ // `parseDOM`/`toDOM` are not decoration either. They are how HTML comes in
14
+ // (a paste from a browser, `format="html"`) and goes out (a copy another
15
+ // application can read, `format="html"`), through ProseMirror's own
16
+ // DOMParser and DOMSerializer over the DOM-shaped shim in `./html.ts`. The
17
+ // editor does not *draw* from them — `./render.ts` does that — except for a
18
+ // node or mark of an app's schema that nothing more specific describes,
19
+ // where `toDOM`'s tag is read as a statement of what the node is.
20
+ import { Schema } from 'prosemirror-model';
21
+ import type {
22
+ Attrs,
23
+ DOMOutputSpec,
24
+ MarkSpec,
25
+ Node as PMNode,
26
+ NodeSpec,
27
+ } from 'prosemirror-model';
28
+
29
+ /** The DOM surface `getAttrs` reads, whether it is a browser's element or
30
+ * the shim's — named here so the rules below say what they need. */
31
+ interface ElementLike {
32
+ getAttribute(name: string): string | null;
33
+ hasAttribute(name: string): boolean;
34
+ readonly childNodes: ArrayLike<ElementLike>;
35
+ readonly nodeName: string;
36
+ readonly style?: { textAlign?: string; fontWeight?: string };
37
+ }
38
+
39
+ function el(dom: unknown): ElementLike {
40
+ return dom as ElementLike;
41
+ }
42
+
43
+ /** `language-js` on a `<pre>` or the `<code>` in it: how every HTML
44
+ * highlighter marks a block's language. */
45
+ function fenceParams(dom: ElementLike): string {
46
+ const own = dom.getAttribute('data-params');
47
+ if (own != null) return own;
48
+ const classes = [dom.getAttribute('class') ?? ''];
49
+ for (const child of Array.from(dom.childNodes)) {
50
+ if (child.nodeName === 'CODE')
51
+ classes.push(child.getAttribute('class') ?? '');
52
+ }
53
+ for (const cls of classes) {
54
+ const m = /(?:^|\s)(?:language|lang)-(\S+)/.exec(cls);
55
+ if (m) return m[1];
56
+ }
57
+ return '';
58
+ }
59
+
60
+ /**
61
+ * A GFM task item's state, from the HTML GitHub renders one as: a disabled
62
+ * checkbox as the item's first content, directly or inside its first
63
+ * paragraph. `data-checked` is what this schema writes itself. Only the item's
64
+ * own box counts — a nested list's checkboxes are its items' business.
65
+ */
66
+ function taskState(dom: ElementLike): boolean | null {
67
+ const flag = dom.getAttribute('data-checked');
68
+ if (flag === 'true') return true;
69
+ if (flag === 'false') return false;
70
+ const kids = Array.from(dom.childNodes);
71
+ const first = kids.find((k) => k.nodeName !== '#text') ?? null;
72
+ const candidates = first
73
+ ? [first, ...(first.nodeName === 'P' ? Array.from(first.childNodes) : [])]
74
+ : [];
75
+ for (const node of candidates) {
76
+ if (
77
+ node.nodeName === 'INPUT' &&
78
+ (node.getAttribute('type') ?? '').toLowerCase() === 'checkbox'
79
+ ) {
80
+ return node.hasAttribute('checked');
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+
86
+ type Align = 'left' | 'center' | 'right' | null;
87
+
88
+ function alignOf(value: string | null | undefined): Align {
89
+ const v = (value ?? '').trim().toLowerCase();
90
+ return v === 'left' || v === 'center' || v === 'right' ? v : null;
91
+ }
92
+
93
+ /** prosemirror-tables' cell attributes, plus GFM's column alignment. */
94
+ const cellAttrs = {
95
+ colspan: { default: 1 },
96
+ rowspan: { default: 1 },
97
+ colwidth: { default: null },
98
+ align: { default: null },
99
+ };
100
+
101
+ function getCellAttrs(dom: unknown): Attrs {
102
+ const e = el(dom);
103
+ const widthAttr = e.getAttribute('data-colwidth');
104
+ const colspan = Number(e.getAttribute('colspan') || 1);
105
+ const widths =
106
+ widthAttr && /^\d+(,\d+)*$/.test(widthAttr)
107
+ ? widthAttr.split(',').map(Number)
108
+ : null;
109
+ return {
110
+ colspan,
111
+ rowspan: Number(e.getAttribute('rowspan') || 1),
112
+ colwidth: widths && widths.length === colspan ? widths : null,
113
+ align: alignOf(e.getAttribute('align') ?? e.style?.textAlign),
114
+ };
115
+ }
116
+
117
+ function setCellAttrs(node: PMNode): Record<string, string> {
118
+ const attrs: Record<string, string> = {};
119
+ if (node.attrs.colspan !== 1) attrs.colspan = String(node.attrs.colspan);
120
+ if (node.attrs.rowspan !== 1) attrs.rowspan = String(node.attrs.rowspan);
121
+ if (node.attrs.colwidth) {
122
+ attrs['data-colwidth'] = (node.attrs.colwidth as number[]).join(',');
123
+ }
124
+ if (node.attrs.align) attrs.style = `text-align: ${node.attrs.align}`;
125
+ return attrs;
126
+ }
127
+
128
+ /** The node specs, in the order the schema ranks them. Exported so an app
129
+ * can build on them — `new Schema({ nodes: schema.spec.nodes.addBefore(…),
130
+ * marks: schema.spec.marks })` is ProseMirror's own idiom for that. */
131
+ export const nodes: Record<string, NodeSpec> = {
132
+ doc: { content: 'block+' },
133
+
134
+ paragraph: {
135
+ content: 'inline*',
136
+ group: 'block',
137
+ parseDOM: [{ tag: 'p' }],
138
+ toDOM: (): DOMOutputSpec => ['p', 0],
139
+ },
140
+
141
+ blockquote: {
142
+ content: 'block+',
143
+ group: 'block',
144
+ defining: true,
145
+ parseDOM: [{ tag: 'blockquote' }],
146
+ toDOM: (): DOMOutputSpec => ['blockquote', 0],
147
+ },
148
+
149
+ horizontal_rule: {
150
+ group: 'block',
151
+ parseDOM: [{ tag: 'hr' }],
152
+ toDOM: (): DOMOutputSpec => ['hr'],
153
+ },
154
+
155
+ heading: {
156
+ attrs: { level: { default: 1, validate: 'number' } },
157
+ content: 'inline*',
158
+ group: 'block',
159
+ defining: true,
160
+ parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
161
+ tag: `h${level}`,
162
+ attrs: { level },
163
+ })),
164
+ toDOM: (node): DOMOutputSpec => [`h${node.attrs.level}`, 0],
165
+ },
166
+
167
+ code_block: {
168
+ // the whole info string, as prosemirror-markdown keeps it; the language
169
+ // is its first word
170
+ attrs: { params: { default: '', validate: 'string' } },
171
+ content: 'text*',
172
+ marks: '',
173
+ group: 'block',
174
+ code: true,
175
+ defining: true,
176
+ parseDOM: [
177
+ {
178
+ tag: 'pre',
179
+ preserveWhitespace: 'full',
180
+ getAttrs: (dom) => ({ params: fenceParams(el(dom)) }),
181
+ },
182
+ ],
183
+ toDOM: (node): DOMOutputSpec => {
184
+ const params = String(node.attrs.params ?? '');
185
+ const lang = params.split(/\s/, 1)[0];
186
+ return [
187
+ 'pre',
188
+ params ? { 'data-params': params } : {},
189
+ ['code', lang ? { class: `language-${lang}` } : {}, 0],
190
+ ];
191
+ },
192
+ },
193
+
194
+ ordered_list: {
195
+ attrs: {
196
+ order: { default: 1, validate: 'number' },
197
+ tight: { default: true },
198
+ },
199
+ content: 'list_item+',
200
+ group: 'block',
201
+ parseDOM: [
202
+ {
203
+ tag: 'ol',
204
+ getAttrs: (dom) => {
205
+ const e = el(dom);
206
+ return {
207
+ order: e.hasAttribute('start')
208
+ ? Number(e.getAttribute('start'))
209
+ : 1,
210
+ tight: e.getAttribute('data-tight') !== 'false',
211
+ };
212
+ },
213
+ },
214
+ ],
215
+ toDOM: (node): DOMOutputSpec => [
216
+ 'ol',
217
+ {
218
+ start: node.attrs.order === 1 ? null : String(node.attrs.order),
219
+ 'data-tight': node.attrs.tight ? null : 'false',
220
+ },
221
+ 0,
222
+ ],
223
+ },
224
+
225
+ bullet_list: {
226
+ attrs: { tight: { default: true } },
227
+ content: 'list_item+',
228
+ group: 'block',
229
+ parseDOM: [
230
+ {
231
+ tag: 'ul',
232
+ getAttrs: (dom) => ({
233
+ tight: el(dom).getAttribute('data-tight') !== 'false',
234
+ }),
235
+ },
236
+ ],
237
+ toDOM: (node): DOMOutputSpec => [
238
+ 'ul',
239
+ { 'data-tight': node.attrs.tight ? null : 'false' },
240
+ 0,
241
+ ],
242
+ },
243
+
244
+ list_item: {
245
+ // `null` for a plain item; `true`/`false` is a GFM task item
246
+ attrs: { checked: { default: null } },
247
+ content: 'paragraph block*',
248
+ defining: true,
249
+ parseDOM: [
250
+ { tag: 'li', getAttrs: (dom) => ({ checked: taskState(el(dom)) }) },
251
+ ],
252
+ toDOM: (node): DOMOutputSpec =>
253
+ node.attrs.checked == null
254
+ ? ['li', 0]
255
+ : [
256
+ 'li',
257
+ {
258
+ class: 'task-list-item',
259
+ 'data-checked': String(node.attrs.checked),
260
+ },
261
+ [
262
+ 'input',
263
+ {
264
+ type: 'checkbox',
265
+ disabled: '',
266
+ checked: node.attrs.checked ? '' : null,
267
+ },
268
+ ],
269
+ ['div', 0],
270
+ ],
271
+ },
272
+
273
+ table: {
274
+ content: 'table_row+',
275
+ tableRole: 'table',
276
+ isolating: true,
277
+ group: 'block',
278
+ parseDOM: [{ tag: 'table' }],
279
+ toDOM: (): DOMOutputSpec => ['table', ['tbody', 0]],
280
+ },
281
+
282
+ table_row: {
283
+ content: '(table_cell | table_header)*',
284
+ tableRole: 'row',
285
+ parseDOM: [{ tag: 'tr' }],
286
+ toDOM: (): DOMOutputSpec => ['tr', 0],
287
+ },
288
+
289
+ table_cell: {
290
+ // GFM cells hold a line of inline content, not blocks: each cell is a
291
+ // textblock, which is what makes a table typable with nothing special
292
+ content: 'inline*',
293
+ attrs: cellAttrs,
294
+ tableRole: 'cell',
295
+ isolating: true,
296
+ parseDOM: [{ tag: 'td', getAttrs: getCellAttrs }],
297
+ toDOM: (node): DOMOutputSpec => ['td', setCellAttrs(node), 0],
298
+ },
299
+
300
+ table_header: {
301
+ content: 'inline*',
302
+ attrs: cellAttrs,
303
+ tableRole: 'header_cell',
304
+ isolating: true,
305
+ parseDOM: [{ tag: 'th', getAttrs: getCellAttrs }],
306
+ toDOM: (node): DOMOutputSpec => ['th', setCellAttrs(node), 0],
307
+ },
308
+
309
+ text: { group: 'inline' },
310
+
311
+ image: {
312
+ inline: true,
313
+ attrs: {
314
+ src: { validate: 'string' },
315
+ alt: { default: null },
316
+ title: { default: null },
317
+ },
318
+ group: 'inline',
319
+ draggable: true,
320
+ parseDOM: [
321
+ {
322
+ tag: 'img[src]',
323
+ getAttrs: (dom) => {
324
+ const e = el(dom);
325
+ return {
326
+ src: e.getAttribute('src'),
327
+ title: e.getAttribute('title'),
328
+ alt: e.getAttribute('alt'),
329
+ };
330
+ },
331
+ },
332
+ ],
333
+ toDOM: (node): DOMOutputSpec => {
334
+ const { src, alt, title } = node.attrs;
335
+ return ['img', { src, alt, title }];
336
+ },
337
+ },
338
+
339
+ hard_break: {
340
+ inline: true,
341
+ group: 'inline',
342
+ selectable: false,
343
+ parseDOM: [{ tag: 'br' }],
344
+ toDOM: (): DOMOutputSpec => ['br'],
345
+ },
346
+ };
347
+
348
+ /** The mark specs. Order is rank: a mark earlier here wraps a later one when
349
+ * both cover the same text, which is what puts `code` innermost. */
350
+ export const marks: Record<string, MarkSpec> = {
351
+ link: {
352
+ attrs: {
353
+ href: { validate: 'string' },
354
+ title: { default: null },
355
+ },
356
+ // typing at the end of a link continues the text, not the link
357
+ inclusive: false,
358
+ parseDOM: [
359
+ {
360
+ tag: 'a[href]',
361
+ getAttrs: (dom) => {
362
+ const e = el(dom);
363
+ return {
364
+ href: e.getAttribute('href'),
365
+ title: e.getAttribute('title'),
366
+ };
367
+ },
368
+ },
369
+ ],
370
+ toDOM: (mark): DOMOutputSpec => [
371
+ 'a',
372
+ { href: mark.attrs.href, title: mark.attrs.title },
373
+ 0,
374
+ ],
375
+ },
376
+
377
+ em: {
378
+ parseDOM: [
379
+ { tag: 'i' },
380
+ { tag: 'em' },
381
+ { style: 'font-style=italic' },
382
+ { style: 'font-style=normal', clearMark: (m) => m.type.name === 'em' },
383
+ ],
384
+ toDOM: (): DOMOutputSpec => ['em', 0],
385
+ },
386
+
387
+ strong: {
388
+ parseDOM: [
389
+ { tag: 'strong' },
390
+ // Google Docs wraps a whole paste in `<b style="font-weight: normal">`
391
+ {
392
+ tag: 'b',
393
+ getAttrs: (dom) => el(dom).style?.fontWeight !== 'normal' && null,
394
+ },
395
+ {
396
+ style: 'font-weight=400',
397
+ clearMark: (m) => m.type.name === 'strong',
398
+ },
399
+ {
400
+ style: 'font-weight',
401
+ getAttrs: (value) =>
402
+ /^(bold(er)?|[5-9]\d{2,})$/.test(value as string) && null,
403
+ },
404
+ ],
405
+ toDOM: (): DOMOutputSpec => ['strong', 0],
406
+ },
407
+
408
+ strike: {
409
+ parseDOM: [
410
+ { tag: 's' },
411
+ { tag: 'del' },
412
+ { tag: 'strike' },
413
+ { style: 'text-decoration=line-through' },
414
+ { style: 'text-decoration-line=line-through' },
415
+ ],
416
+ toDOM: (): DOMOutputSpec => ['s', 0],
417
+ },
418
+
419
+ code: {
420
+ code: true,
421
+ parseDOM: [{ tag: 'code' }],
422
+ toDOM: (): DOMOutputSpec => ['code', 0],
423
+ },
424
+ };
425
+
426
+ /**
427
+ * The default schema: GFM — paragraphs, six heading levels, quotes, fenced
428
+ * code, rules, bullet/ordered/task lists, tables, images, hard breaks; bold,
429
+ * italic, strikethrough, inline code and links.
430
+ */
431
+ export const schema = new Schema({ nodes, marks });
@@ -0,0 +1,39 @@
1
+ // A value per block, with a subscription per block.
2
+ //
3
+ // What lets a decoration, or a node selection, repaint the one block it is
4
+ // on: the block's component subscribes to its own key (React's
5
+ // `useSyncExternalStore`), so a plugin lighting up a search match re-renders
6
+ // that paragraph — not the document, and not the chain of memoized blocks
7
+ // between the document and the paragraph, which would each have to be told.
8
+ export class KeyedStore<T> {
9
+ private values = new Map<string, T>();
10
+ private listeners = new Map<string, Set<() => void>>();
11
+
12
+ get(key: string): T | undefined {
13
+ return this.values.get(key);
14
+ }
15
+
16
+ /** Set, or with `undefined` clear, one key's value. Identical values are
17
+ * not news: nothing is told. */
18
+ set(key: string, value: T | undefined): void {
19
+ if (Object.is(this.values.get(key), value)) return;
20
+ if (value === undefined) this.values.delete(key);
21
+ else this.values.set(key, value);
22
+ const set = this.listeners.get(key);
23
+ if (set) for (const fn of [...set]) fn();
24
+ }
25
+
26
+ keys(): IterableIterator<string> {
27
+ return this.values.keys();
28
+ }
29
+
30
+ subscribe(key: string, fn: () => void): () => void {
31
+ let set = this.listeners.get(key);
32
+ if (!set) this.listeners.set(key, (set = new Set()));
33
+ set.add(fn);
34
+ return () => {
35
+ set.delete(fn);
36
+ if (set.size === 0) this.listeners.delete(key);
37
+ };
38
+ }
39
+ }