@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,439 @@
1
+ // Copy and paste, in the formats every other application speaks.
2
+ //
3
+ // A copy offers three things: `text/html` (the schema's `toDOM`, with
4
+ // ProseMirror's `data-pm-slice` marker so the open sides survive), plain text
5
+ // (the `clipboardTextSerializer` prop, or blocks a blank line apart — the
6
+ // markdown editor's default hands markdown here), and, in this process only,
7
+ // the slice itself. The last is not an optimisation: react-x11's Cocoa
8
+ // clipboard carries one flavour, text (react-x11 docs/clipboard.md,
9
+ // "Limits"), so without it a copy and paste inside one editor would flatten
10
+ // every heading and list on a Mac. A paste whose text is exactly what this
11
+ // process last put there takes the slice back instead of re-reading it.
12
+ //
13
+ // Reading is prosemirror-view's `parseFromClipboard`, ported: the same
14
+ // props, in the same order, over the DOM shim in `./html.ts` instead of a
15
+ // browser's — so a paste from a web page is read by the schema's own
16
+ // `parseDOM` rules, and a plugin's `transformPasted` sees what it would see
17
+ // in a browser. The helpers below `parseFromClipboard` are prosemirror-view's
18
+ // own (MIT), which does not export them; they are pure functions over the
19
+ // model and came across unchanged.
20
+ import { DOMParser, DOMSerializer, Fragment, Slice } from 'prosemirror-model';
21
+ import type {
22
+ Node as PMNode,
23
+ NodeType,
24
+ ParseOptions,
25
+ ResolvedPos,
26
+ } from 'prosemirror-model';
27
+ import type { EditorProps, EditorView } from 'prosemirror-view';
28
+
29
+ import { clipboardHTML, readHTML, sliceFromElement } from './html.js';
30
+ import type { ShimElement } from './html.js';
31
+ import type { EditorState } from 'prosemirror-state';
32
+
33
+ /** What the clipboard code needs of the view — `someProp` over the props a
34
+ * plugin can supply, and the state. */
35
+ export interface ClipboardView {
36
+ readonly state: EditorState;
37
+ someProp<K extends keyof EditorProps, R>(
38
+ name: K,
39
+ f: (value: NonNullable<EditorProps[K]>) => R,
40
+ ): R | undefined;
41
+ /** The view as a plugin's callback is handed it. */
42
+ readonly asEditorView: EditorView;
43
+ }
44
+
45
+ /** What a copy puts on the clipboard. */
46
+ export interface ClipboardContent {
47
+ html: string;
48
+ text: string;
49
+ slice: Slice;
50
+ }
51
+
52
+ export function serializeForClipboard(
53
+ view: ClipboardView,
54
+ slice: Slice,
55
+ ): ClipboardContent {
56
+ view.someProp('transformCopied', (f) => {
57
+ slice = f(slice, view.asEditorView);
58
+ });
59
+ const context: unknown[] = [];
60
+ let { content, openStart, openEnd } = slice;
61
+ while (
62
+ openStart > 1 &&
63
+ openEnd > 1 &&
64
+ content.childCount === 1 &&
65
+ content.firstChild!.childCount === 1
66
+ ) {
67
+ openStart--;
68
+ openEnd--;
69
+ const node = content.firstChild!;
70
+ // a node made with no attributes shares its type's default object —
71
+ // ProseMirror's own test, through a member its declarations leave out
72
+ const defaults = (node.type as unknown as { defaultAttrs?: unknown })
73
+ .defaultAttrs;
74
+ context.push(node.type.name, node.attrs !== defaults ? node.attrs : null);
75
+ content = node.content;
76
+ }
77
+ const serializer =
78
+ view.someProp('clipboardSerializer', (s) => s) ??
79
+ DOMSerializer.fromSchema(view.state.schema);
80
+ const html = clipboardHTML(serializer, content, openStart, openEnd, context);
81
+ const text =
82
+ view.someProp('clipboardTextSerializer', (f) =>
83
+ f(slice, view.asEditorView),
84
+ ) || slice.content.textBetween(0, slice.content.size, '\n\n');
85
+ return { html, text, slice };
86
+ }
87
+
88
+ const INLINE_PARENTS =
89
+ /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;
90
+
91
+ /** A paste — or a drop — read into a slice. `plainText` is a paste that
92
+ * asked for no formatting (Shift held). */
93
+ export function parseFromClipboard(
94
+ view: ClipboardView,
95
+ text: string | null,
96
+ html: string | null,
97
+ plainText: boolean,
98
+ $context: ResolvedPos,
99
+ ): Slice | null {
100
+ const { schema } = view.state;
101
+ const inCode = !!$context.parent.type.spec.code;
102
+ let dom: ShimElement | null = null;
103
+ let slice: Slice | null = null;
104
+ if (!html && !text) return null;
105
+ const asText = !!text && (plainText || inCode || !html);
106
+ if (asText) {
107
+ let pasted = text!;
108
+ view.someProp('transformPastedText', (f) => {
109
+ pasted = f(pasted, inCode || plainText, view.asEditorView);
110
+ });
111
+ if (inCode) {
112
+ let codeSlice = pasted
113
+ ? new Slice(
114
+ Fragment.from(schema.text(pasted.replace(/\r\n?/g, '\n'))),
115
+ 0,
116
+ 0,
117
+ )
118
+ : Slice.empty;
119
+ view.someProp('transformPasted', (f) => {
120
+ codeSlice = f(codeSlice, view.asEditorView, true);
121
+ });
122
+ return codeSlice;
123
+ }
124
+ const parsed = view.someProp('clipboardTextParser', (f) =>
125
+ f(pasted, $context, plainText, view.asEditorView),
126
+ );
127
+ if (parsed) {
128
+ slice = parsed;
129
+ } else {
130
+ // One paragraph per line, carrying the marks at the caret — what
131
+ // ProseMirror builds as `<p>`s and parses, built directly.
132
+ const marks = $context.marks();
133
+ const paragraph = defaultTextblock($context);
134
+ if (paragraph) {
135
+ const blocks = pasted
136
+ .split(/(?:\r\n?|\n)+/)
137
+ .map((line) =>
138
+ paragraph.create(null, line ? schema.text(line, marks) : null),
139
+ );
140
+ slice = Slice.maxOpen(Fragment.from(blocks));
141
+ } else {
142
+ slice = new Slice(Fragment.from(schema.text(pasted, marks)), 0, 0);
143
+ }
144
+ }
145
+ } else {
146
+ let source = html!;
147
+ view.someProp('transformPastedHTML', (f) => {
148
+ source = f(source, view.asEditorView);
149
+ });
150
+ dom = readHTML(source);
151
+ }
152
+
153
+ const contextNode = dom
154
+ ? dom.matches('[data-pm-slice]')
155
+ ? dom
156
+ : dom.querySelector('[data-pm-slice]')
157
+ : null;
158
+ const sliceData =
159
+ contextNode &&
160
+ /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(
161
+ contextNode.getAttribute('data-pm-slice') || '',
162
+ );
163
+ if (dom && sliceData && sliceData[3]) {
164
+ for (let i = +sliceData[3]; i > 0; i--) {
165
+ let child = dom.firstChild;
166
+ while (child && child.nodeType !== 1) child = child.nextSibling;
167
+ if (!child) break;
168
+ dom = child as ShimElement;
169
+ }
170
+ }
171
+
172
+ if (!slice && dom) {
173
+ const parser =
174
+ view.someProp('clipboardParser', (p) => p) ??
175
+ view.someProp('domParser', (p) => p) ??
176
+ DOMParser.fromSchema(schema);
177
+ // `ruleFromNode` is a parse option prosemirror-view passes and
178
+ // prosemirror-model's declarations leave out: a trailing `<br>` in a
179
+ // block is the browser's placeholder, not a line break
180
+ const options = {
181
+ preserveWhitespace: !!(asText || sliceData),
182
+ context: $context,
183
+ ruleFromNode(node: unknown) {
184
+ const el = node as ShimElement;
185
+ if (
186
+ el.nodeName === 'BR' &&
187
+ !el.nextSibling &&
188
+ el.parentNode &&
189
+ !INLINE_PARENTS.test(el.parentNode.nodeName)
190
+ ) {
191
+ return { ignore: true };
192
+ }
193
+ return null;
194
+ },
195
+ };
196
+ slice = sliceFromElement(parser, dom, options as ParseOptions);
197
+ }
198
+ if (!slice) return null;
199
+
200
+ if (sliceData) {
201
+ slice = addContext(
202
+ closeSlice(slice, +sliceData[1], +sliceData[2]),
203
+ sliceData[4],
204
+ );
205
+ } else {
206
+ // HTML that did not come from ProseMirror: make the top-level siblings
207
+ // coherent, so `replace` can place them at all
208
+ slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
209
+ if (slice.openStart || slice.openEnd) {
210
+ let openStart = 0;
211
+ let openEnd = 0;
212
+ for (
213
+ let node = slice.content.firstChild;
214
+ openStart < slice.openStart && node && !node.type.spec.isolating;
215
+ openStart++, node = node.firstChild
216
+ ) {
217
+ // counting
218
+ }
219
+ for (
220
+ let node = slice.content.lastChild;
221
+ openEnd < slice.openEnd && node && !node.type.spec.isolating;
222
+ openEnd++, node = node.lastChild
223
+ ) {
224
+ // counting
225
+ }
226
+ slice = closeSlice(slice, openStart, openEnd);
227
+ }
228
+ }
229
+ const result = slice;
230
+ let transformed = result;
231
+ view.someProp('transformPasted', (f) => {
232
+ transformed = f(transformed, view.asEditorView, asText);
233
+ });
234
+ return transformed;
235
+ }
236
+
237
+ /** The textblock a line of pasted text becomes: the first one the context
238
+ * can hold, which in every schema anyone writes is the paragraph. */
239
+ function defaultTextblock($context: ResolvedPos): NodeType | null {
240
+ for (let d = $context.depth; d >= 0; d--) {
241
+ const type = $context.node(d).contentMatchAt($context.index(d)).defaultType;
242
+ if (type?.isTextblock) return type;
243
+ }
244
+ return null;
245
+ }
246
+
247
+ // --- prosemirror-view's helpers (MIT, Marijn Haverbeke) ----------------------
248
+
249
+ // Takes a slice parsed with parseSlice, which means there hasn't been any
250
+ // content-expression checking done on the top nodes, tries to find a parent
251
+ // node in the current context that might fit the nodes, and if successful,
252
+ // rebuilds the slice so that it fits into that parent.
253
+ function normalizeSiblings(
254
+ fragment: Fragment,
255
+ $context: ResolvedPos,
256
+ ): Fragment {
257
+ if (fragment.childCount < 2) return fragment;
258
+ for (let d = $context.depth; d >= 0; d--) {
259
+ const parent = $context.node(d);
260
+ let match = parent.contentMatchAt($context.index(d));
261
+ let lastWrap: readonly NodeType[] = [];
262
+ let result: PMNode[] | null = [];
263
+ fragment.forEach((node) => {
264
+ if (!result) return;
265
+ const wrap = match.findWrapping(node.type);
266
+ if (!wrap) {
267
+ result = null;
268
+ return;
269
+ }
270
+ const inLast =
271
+ result.length &&
272
+ lastWrap.length &&
273
+ addToSibling(wrap, lastWrap, node, result[result.length - 1], 0);
274
+ if (inLast) {
275
+ result[result.length - 1] = inLast;
276
+ } else {
277
+ if (result.length) {
278
+ result[result.length - 1] = closeRight(
279
+ result[result.length - 1],
280
+ lastWrap.length,
281
+ );
282
+ }
283
+ const wrapped = withWrappers(node, wrap);
284
+ result.push(wrapped);
285
+ match = match.matchType(wrapped.type)!;
286
+ lastWrap = wrap;
287
+ }
288
+ });
289
+ if (result) return Fragment.from(result);
290
+ }
291
+ return fragment;
292
+ }
293
+
294
+ function withWrappers(
295
+ node: PMNode,
296
+ wrap: readonly NodeType[],
297
+ from = 0,
298
+ ): PMNode {
299
+ for (let i = wrap.length - 1; i >= from; i--) {
300
+ node = wrap[i].create(null, Fragment.from(node));
301
+ }
302
+ return node;
303
+ }
304
+
305
+ function addToSibling(
306
+ wrap: readonly NodeType[],
307
+ lastWrap: readonly NodeType[],
308
+ node: PMNode,
309
+ sibling: PMNode,
310
+ depth: number,
311
+ ): PMNode | undefined {
312
+ if (
313
+ depth < wrap.length &&
314
+ depth < lastWrap.length &&
315
+ wrap[depth] === lastWrap[depth]
316
+ ) {
317
+ const inner = addToSibling(
318
+ wrap,
319
+ lastWrap,
320
+ node,
321
+ sibling.lastChild!,
322
+ depth + 1,
323
+ );
324
+ if (inner) {
325
+ return sibling.copy(
326
+ sibling.content.replaceChild(sibling.childCount - 1, inner),
327
+ );
328
+ }
329
+ const match = sibling.contentMatchAt(sibling.childCount);
330
+ if (
331
+ match.matchType(depth === wrap.length - 1 ? node.type : wrap[depth + 1])
332
+ ) {
333
+ return sibling.copy(
334
+ sibling.content.append(
335
+ Fragment.from(withWrappers(node, wrap, depth + 1)),
336
+ ),
337
+ );
338
+ }
339
+ }
340
+ return undefined;
341
+ }
342
+
343
+ function closeRight(node: PMNode, depth: number): PMNode {
344
+ if (depth === 0) return node;
345
+ const fragment = node.content.replaceChild(
346
+ node.childCount - 1,
347
+ closeRight(node.lastChild!, depth - 1),
348
+ );
349
+ const fill = node
350
+ .contentMatchAt(node.childCount)
351
+ .fillBefore(Fragment.empty, true)!;
352
+ return node.copy(fragment.append(fill));
353
+ }
354
+
355
+ function closeRange(
356
+ fragment: Fragment,
357
+ side: number,
358
+ from: number,
359
+ to: number,
360
+ depth: number,
361
+ openEnd: number,
362
+ ): Fragment {
363
+ const node = side < 0 ? fragment.firstChild! : fragment.lastChild!;
364
+ let inner = node.content;
365
+ if (fragment.childCount > 1) openEnd = 0;
366
+ if (depth < to - 1)
367
+ inner = closeRange(inner, side, from, to, depth + 1, openEnd);
368
+ if (depth >= from) {
369
+ inner =
370
+ side < 0
371
+ ? node
372
+ .contentMatchAt(0)
373
+ .fillBefore(inner, openEnd <= depth)!
374
+ .append(inner)
375
+ : inner.append(
376
+ node
377
+ .contentMatchAt(node.childCount)
378
+ .fillBefore(Fragment.empty, true)!,
379
+ );
380
+ }
381
+ return fragment.replaceChild(
382
+ side < 0 ? 0 : fragment.childCount - 1,
383
+ node.copy(inner),
384
+ );
385
+ }
386
+
387
+ function closeSlice(slice: Slice, openStart: number, openEnd: number): Slice {
388
+ if (openStart < slice.openStart) {
389
+ slice = new Slice(
390
+ closeRange(
391
+ slice.content,
392
+ -1,
393
+ openStart,
394
+ slice.openStart,
395
+ 0,
396
+ slice.openEnd,
397
+ ),
398
+ openStart,
399
+ slice.openEnd,
400
+ );
401
+ }
402
+ if (openEnd < slice.openEnd) {
403
+ slice = new Slice(
404
+ closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0),
405
+ slice.openStart,
406
+ openEnd,
407
+ );
408
+ }
409
+ return slice;
410
+ }
411
+
412
+ function addContext(slice: Slice, context: string): Slice {
413
+ if (!slice.size) return slice;
414
+ const schema = slice.content.firstChild!.type.schema;
415
+ let array: unknown[];
416
+ try {
417
+ array = JSON.parse(context) as unknown[];
418
+ } catch {
419
+ return slice;
420
+ }
421
+ let { content, openStart, openEnd } = slice;
422
+ for (let i = array.length - 2; i >= 0; i -= 2) {
423
+ const type = schema.nodes[array[i] as string];
424
+ if (!type || type.hasRequiredAttrs()) break;
425
+ try {
426
+ (type as unknown as { checkAttrs(attrs: unknown): void }).checkAttrs(
427
+ array[i + 1],
428
+ );
429
+ } catch {
430
+ break;
431
+ }
432
+ content = Fragment.from(
433
+ type.create(array[i + 1] as Record<string, unknown>, content),
434
+ );
435
+ openStart++;
436
+ openEnd++;
437
+ }
438
+ return new Slice(content, openStart, openEnd);
439
+ }
@@ -0,0 +1,91 @@
1
+ // Collaboration: what the editor draws for a collaborator's caret.
2
+ //
3
+ // y-prosemirror's cursor plugin — the one a Yjs-backed ProseMirror editor
4
+ // runs — shows another user's caret as a *widget decoration* whose `toDOM`
5
+ // builds an element: its `cursorBuilder` option, which by default makes a
6
+ // `<span>` with the user's name in it. A widget is DOM, and this view has no
7
+ // DOM to draw it with (./view.ts, "what a DOM view would draw that this one
8
+ // cannot"). So the editor supplies a builder of its own, `remoteCaret`, that
9
+ // returns not an element but a description of one — whose caret it is, and
10
+ // in what colour — and the view, meeting a widget with no `text` to draw,
11
+ // asks its `toDOM` once and draws what comes back as a caret beside the
12
+ // editor's own: a bar and a flag, set on the textblock's element the way the
13
+ // local caret is, re-rendering nothing.
14
+ //
15
+ // The other half of y-prosemirror's cursor plugin, the collaborator's
16
+ // selection, is an inline decoration with a `style` — a background colour —
17
+ // and the view draws that already.
18
+ import type { Decoration, EditorView } from 'prosemirror-view';
19
+
20
+ // a registered symbol, so two copies of this package in one app agree
21
+ const CARET = Symbol.for('@react-x11/components/rich-text-editor:remote-caret');
22
+
23
+ /** A collaborator's caret, as `remoteCaret` describes it. */
24
+ export interface RemoteCaret {
25
+ /** The bar's and the flag's colour — the awareness state's `user.color`. */
26
+ readonly color: string;
27
+ /** Whose caret it is — carried along; the caret is drawn without it. */
28
+ readonly name: string;
29
+ }
30
+
31
+ /**
32
+ * y-prosemirror's `cursorBuilder`, for this editor:
33
+ *
34
+ * ```ts
35
+ * yCursorPlugin(awareness, { cursorBuilder: remoteCaret })
36
+ * ```
37
+ *
38
+ * `user` is the awareness state's `user` field, `{ name, color }`, with the
39
+ * colour a `#rrggbb` (y-prosemirror's own requirement). Typed as the
40
+ * `HTMLElement` y-prosemirror asks a builder for; what it returns is a
41
+ * description only this editor's view reads.
42
+ */
43
+ export function remoteCaret(
44
+ user?: { name?: unknown; color?: unknown } | null,
45
+ ): HTMLElement {
46
+ const caret = {
47
+ [CARET]: true,
48
+ color: typeof user?.color === 'string' ? user.color : '#ffa500',
49
+ name: typeof user?.name === 'string' ? user.name : '',
50
+ };
51
+ return caret as unknown as HTMLElement;
52
+ }
53
+
54
+ function isRemoteCaret(value: unknown): value is RemoteCaret {
55
+ return (
56
+ typeof value === 'object' &&
57
+ value !== null &&
58
+ (value as Record<symbol, unknown>)[CARET] === true
59
+ );
60
+ }
61
+
62
+ /** What a widget's `toDOM` made, by the widget's type — which a mapped
63
+ * decoration keeps, so a caret that only moved is not asked again. */
64
+ const made = new WeakMap<object, RemoteCaret | null>();
65
+
66
+ /**
67
+ * The caret a widget decoration stands for, when its `toDOM` is
68
+ * `remoteCaret`'s — or null for any other widget, which has nothing here to
69
+ * draw it with. A `toDOM` that reaches for `document` throws under this view:
70
+ * that is a widget written for a browser, skipped like the rest.
71
+ */
72
+ export function caretOf(
73
+ deco: Decoration,
74
+ view: EditorView,
75
+ ): RemoteCaret | null {
76
+ const type = (deco as unknown as { type?: { toDOM?: unknown } }).type;
77
+ if (!type || typeof type.toDOM !== 'function') return null;
78
+ const known = made.get(type);
79
+ if (known !== undefined) return known;
80
+ let caret: RemoteCaret | null = null;
81
+ try {
82
+ const out = (
83
+ type.toDOM as (view: EditorView, getPos: () => number) => unknown
84
+ )(view, () => deco.from);
85
+ caret = isRemoteCaret(out) ? out : null;
86
+ } catch {
87
+ caret = null;
88
+ }
89
+ made.set(type, caret);
90
+ return caret;
91
+ }