edytor 2.0.0 → 4.0.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 (346) hide show
  1. package/README.md +19 -4
  2. package/dist/block/block.svelte.js +2 -4
  3. package/dist/clipboard/htmlFlow.d.ts +20 -7
  4. package/dist/clipboard/htmlFlow.js +23 -15
  5. package/dist/clipboard/serializeClipboardFragment.d.ts +37 -4
  6. package/dist/clipboard/serializeClipboardFragment.js +6 -4
  7. package/dist/clipboard/textBlocks.d.ts +8 -0
  8. package/dist/clipboard/textBlocks.js +6 -2
  9. package/dist/cloudflare/DocumentRoom.d.ts +13 -9
  10. package/dist/cloudflare/DocumentRoom.js +10 -6
  11. package/dist/cloudflare/room/admission.js +3 -3
  12. package/dist/cloudflare/room/context.d.ts +28 -27
  13. package/dist/cloudflare/room/context.js +2 -2
  14. package/dist/cloudflare/room/purge.d.ts +6 -3
  15. package/dist/cloudflare/room/purge.js +44 -19
  16. package/dist/cloudflare/room/updates.d.ts +3 -4
  17. package/dist/cloudflare/room/updates.js +6 -8
  18. package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
  19. package/dist/cloudflare/routeDocumentSocket.js +53 -0
  20. package/dist/collaboration/comments/client.d.ts +1 -1
  21. package/dist/collaboration/comments/client.js +1 -1
  22. package/dist/collaboration/remoteSelection.js +3 -3
  23. package/dist/components/Edytor.svelte +2 -10
  24. package/dist/components/Edytor.svelte.d.ts +1 -3
  25. package/dist/crdt/admission.d.ts +5 -10
  26. package/dist/crdt/admission.js +5 -11
  27. package/dist/crdt/attribution/attribution.d.ts +31 -12
  28. package/dist/crdt/attribution/attribution.js +56 -18
  29. package/dist/crdt/attribution/block.d.ts +3 -3
  30. package/dist/crdt/attribution/block.js +7 -8
  31. package/dist/crdt/attribution/index.d.ts +1 -1
  32. package/dist/crdt/attribution/index.js +1 -1
  33. package/dist/crdt/data.d.ts +62 -31
  34. package/dist/crdt/data.js +211 -103
  35. package/dist/crdt/doc/delete.d.ts +1 -1
  36. package/dist/crdt/doc/delete.js +13 -30
  37. package/dist/crdt/doc/funnel.d.ts +7 -6
  38. package/dist/crdt/doc/funnel.js +39 -33
  39. package/dist/crdt/doc/gate.d.ts +3 -20
  40. package/dist/crdt/doc/gate.js +2 -41
  41. package/dist/crdt/doc/history.d.ts +25 -9
  42. package/dist/crdt/doc/history.js +152 -42
  43. package/dist/crdt/doc/layout.js +11 -22
  44. package/dist/crdt/doc/meta.js +12 -17
  45. package/dist/crdt/doc/moves.d.ts +1 -1
  46. package/dist/crdt/doc/moves.js +52 -32
  47. package/dist/crdt/doc/reads.js +2 -2
  48. package/dist/crdt/doc/seed.js +2 -2
  49. package/dist/crdt/doc/split-merge.js +12 -15
  50. package/dist/crdt/doc/steps.d.ts +25 -13
  51. package/dist/crdt/doc/steps.js +52 -122
  52. package/dist/crdt/doc/types.d.ts +21 -9
  53. package/dist/crdt/document.d.ts +21 -19
  54. package/dist/crdt/document.js +26 -23
  55. package/dist/crdt/edytor-doc.d.ts +66 -68
  56. package/dist/crdt/edytor-doc.js +21 -20
  57. package/dist/crdt/engine-api.d.ts +33 -1
  58. package/dist/crdt/engine.d.ts +0 -1
  59. package/dist/crdt/engine.js +1 -4
  60. package/dist/crdt/flow.d.ts +1 -1
  61. package/dist/crdt/flow.js +25 -27
  62. package/dist/crdt/index.d.ts +29 -28
  63. package/dist/crdt/index.js +3 -3
  64. package/dist/crdt/placement/display.d.ts +5 -30
  65. package/dist/crdt/placement/display.js +39 -144
  66. package/dist/crdt/placement/keys.d.ts +58 -0
  67. package/dist/crdt/placement/keys.js +90 -0
  68. package/dist/crdt/placement/model.d.ts +119 -118
  69. package/dist/crdt/placement/model.js +242 -142
  70. package/dist/crdt/placement/resolve.d.ts +123 -35
  71. package/dist/crdt/placement/resolve.js +187 -153
  72. package/dist/crdt/placement/slots.d.ts +134 -0
  73. package/dist/crdt/placement/slots.js +152 -0
  74. package/dist/crdt/protocol.d.ts +2 -2
  75. package/dist/crdt/protocol.js +2 -2
  76. package/dist/crdt/providers/index.d.ts +1 -1
  77. package/dist/crdt/providers/room.d.ts +26 -11
  78. package/dist/crdt/providers/room.js +77 -27
  79. package/dist/crdt/providers/websocket.d.ts +1 -1
  80. package/dist/crdt/providers/websocket.js +33 -32
  81. package/dist/crdt/purge.d.ts +17 -8
  82. package/dist/crdt/purge.js +236 -126
  83. package/dist/crdt/restore.d.ts +2 -2
  84. package/dist/crdt/restore.js +18 -34
  85. package/dist/crdt/schema.d.ts +22 -21
  86. package/dist/crdt/schema.js +29 -23
  87. package/dist/crdt/semantics.js +3 -1
  88. package/dist/crdt/storage.d.ts +1 -1
  89. package/dist/crdt/storage.js +1 -1
  90. package/dist/crdt/structs.d.ts +15 -15
  91. package/dist/crdt/structs.js +8 -22
  92. package/dist/crdt/text/index/cache.js +4 -0
  93. package/dist/crdt/text/index/claims.js +12 -0
  94. package/dist/crdt/text/index/fold.d.ts +2 -3
  95. package/dist/crdt/text/index/fold.js +194 -44
  96. package/dist/crdt/text/index/layout.js +5 -8
  97. package/dist/crdt/text/index/placement.d.ts +26 -18
  98. package/dist/crdt/text/index/placement.js +404 -138
  99. package/dist/crdt/text/index/records.d.ts +3 -3
  100. package/dist/crdt/text/index/records.js +24 -31
  101. package/dist/crdt/text/index/shared.d.ts +11 -3
  102. package/dist/crdt/text/index/shared.js +12 -3
  103. package/dist/crdt/text/index/state.d.ts +21 -17
  104. package/dist/crdt/text/index/state.js +15 -17
  105. package/dist/crdt/text/index/streams.d.ts +10 -5
  106. package/dist/crdt/text/index/streams.js +78 -27
  107. package/dist/crdt/text/items.d.ts +10 -3
  108. package/dist/crdt/text/items.js +7 -0
  109. package/dist/crdt/text/model.d.ts +77 -62
  110. package/dist/crdt/text/model.js +84 -169
  111. package/dist/crdt/text/ranges.d.ts +1 -1
  112. package/dist/crdt/text/ranges.js +3 -3
  113. package/dist/crdt/text/rows.d.ts +13 -7
  114. package/dist/crdt/text/rows.js +24 -7
  115. package/dist/crdt/text/runs.d.ts +17 -12
  116. package/dist/crdt/text/runs.js +24 -7
  117. package/dist/crdt/text/streams.d.ts +26 -13
  118. package/dist/crdt/text/streams.js +41 -14
  119. package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
  120. package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
  121. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  122. package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
  123. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
  124. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
  125. package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
  126. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
  127. package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
  128. package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
  129. package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
  130. package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
  131. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
  132. package/dist/crdt/vendor/yjs/global.d.ts +2 -0
  133. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  134. package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
  135. package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
  136. package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
  137. package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
  138. package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
  139. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
  140. package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
  141. package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
  142. package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
  143. package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
  144. package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
  145. package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
  146. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
  147. package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
  148. package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
  149. package/dist/dnd/autoScroll.d.ts +15 -7
  150. package/dist/dnd/autoScroll.js +10 -15
  151. package/dist/dnd/dropIndicator.js +2 -5
  152. package/dist/dnd/external.d.ts +6 -0
  153. package/dist/dnd/external.js +17 -0
  154. package/dist/dnd/geometry.d.ts +2 -0
  155. package/dist/dnd/geometry.js +3 -4
  156. package/dist/dnd/placement.d.ts +1 -1
  157. package/dist/dnd/pragmatic.d.ts +3 -1
  158. package/dist/dnd/pragmatic.js +12 -2
  159. package/dist/dnd/vendor/pragmatic/UPSTREAM.md +51 -18
  160. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.d.ts +4 -0
  161. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.js +6 -0
  162. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +0 -2
  163. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +4 -22
  164. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter-types.d.ts +26 -0
  165. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.d.ts +30 -0
  166. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.js +203 -0
  167. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-items.js +13 -0
  168. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-types.js +9 -0
  169. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/is-an-available-type.js +19 -0
  170. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.d.ts +4 -0
  171. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.js +6 -0
  172. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +4 -16
  173. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +1 -8
  174. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +5 -6
  175. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +2 -3
  176. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.d.ts +1 -1
  177. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +6 -24
  178. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.d.ts +2 -0
  179. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.js +5 -0
  180. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.d.ts +3 -0
  181. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.js +12 -0
  182. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/native-types.d.ts +12 -0
  183. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window-in-safari.js +10 -0
  184. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window.js +55 -0
  185. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.d.ts +4 -0
  186. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.js +8 -0
  187. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +0 -4
  188. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +16 -72
  189. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +1 -9
  190. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +4 -29
  191. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +2 -3
  192. package/dist/edytor.svelte.d.ts +2 -5
  193. package/dist/edytor.svelte.js +21 -32
  194. package/dist/events/beforeInputCommands.js +2 -2
  195. package/dist/events/onBeforeInput.js +1 -1
  196. package/dist/events/onDrop.d.ts +0 -6
  197. package/dist/events/onDrop.js +10 -23
  198. package/dist/events/onInput.d.ts +0 -12
  199. package/dist/events/onInput.js +1 -67
  200. package/dist/events/onPageHide.d.ts +3 -0
  201. package/dist/events/onPageHide.js +16 -0
  202. package/dist/export/forms.d.ts +627 -0
  203. package/dist/export/forms.js +749 -0
  204. package/dist/export/index.d.ts +170 -0
  205. package/dist/export/index.js +161 -0
  206. package/dist/export/markdown.d.ts +41 -0
  207. package/dist/export/markdown.js +585 -0
  208. package/dist/export/markdownWriter.d.ts +9 -0
  209. package/dist/export/markdownWriter.js +133 -0
  210. package/dist/export/mdx.d.ts +15 -0
  211. package/dist/export/mdx.js +33 -0
  212. package/dist/index.d.ts +3 -3
  213. package/dist/index.js +1 -1
  214. package/dist/kinds.js +1 -1
  215. package/dist/plugins/anchored.svelte.d.ts +21 -25
  216. package/dist/plugins/anchored.svelte.js +23 -39
  217. package/dist/plugins/blockHandles/BlockHandleController.svelte.js +24 -32
  218. package/dist/plugins/blockHandles/dragPreview.d.ts +26 -7
  219. package/dist/plugins/blockHandles/dragPreview.js +33 -0
  220. package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +5 -0
  221. package/dist/plugins/blockMenu/BlockMenuController.svelte.js +7 -2
  222. package/dist/plugins/blockMenu/blockMenuPlugin.js +5 -50
  223. package/dist/plugins/chrome.d.ts +27 -5
  224. package/dist/plugins/chrome.js +24 -9
  225. package/dist/plugins/code/CodePlugin.svelte +12 -9
  226. package/dist/plugins/code/header.svelte.js +7 -11
  227. package/dist/plugins/code/languageMenu.svelte.d.ts +5 -2
  228. package/dist/plugins/code/languageMenu.svelte.js +13 -20
  229. package/dist/plugins/columns/ColumnsPlugin.svelte +7 -20
  230. package/dist/plugins/columns/resize.svelte.d.ts +2 -3
  231. package/dist/plugins/columns/resize.svelte.js +2 -5
  232. package/dist/plugins/comments/CommentsController.svelte.d.ts +6 -0
  233. package/dist/plugins/comments/CommentsController.svelte.js +20 -0
  234. package/dist/plugins/equation/EquationPlugin.svelte +12 -22
  235. package/dist/plugins/equation/equation.svelte.d.ts +7 -15
  236. package/dist/plugins/equation/equation.svelte.js +13 -50
  237. package/dist/plugins/equation/tex.d.ts +17 -0
  238. package/dist/plugins/equation/tex.js +42 -0
  239. package/dist/plugins/fileDrop.d.ts +38 -23
  240. package/dist/plugins/fileDrop.js +82 -76
  241. package/dist/plugins/find/FindController.svelte.d.ts +5 -0
  242. package/dist/plugins/find/FindController.svelte.js +14 -14
  243. package/dist/plugins/find/findPlugin.js +1 -15
  244. package/dist/plugins/image/ImageChrome.svelte +8 -2
  245. package/dist/plugins/image/ImagePlugin.svelte +15 -52
  246. package/dist/plugins/image/controls.svelte.js +16 -2
  247. package/dist/plugins/image/toolbar.svelte.d.ts +5 -4
  248. package/dist/plugins/image/toolbar.svelte.js +21 -31
  249. package/dist/plugins/index.d.ts +1 -1
  250. package/dist/plugins/list.svelte.d.ts +9 -0
  251. package/dist/plugins/list.svelte.js +18 -0
  252. package/dist/plugins/marquee/marqueePlugin.js +0 -4
  253. package/dist/plugins/media/AudioPlugin.svelte +1 -1
  254. package/dist/plugins/media/BookmarkPlugin.svelte +6 -97
  255. package/dist/plugins/media/EmbedPlugin.svelte +5 -90
  256. package/dist/plugins/media/EmptySlot.svelte +36 -20
  257. package/dist/plugins/media/EmptySlot.svelte.d.ts +6 -15
  258. package/dist/plugins/media/FilePlugin.svelte +1 -19
  259. package/dist/plugins/media/MediaEmpty.svelte +2 -55
  260. package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
  261. package/dist/plugins/media/SourceBlock.svelte +142 -52
  262. package/dist/plugins/media/SourceBlock.svelte.d.ts +10 -4
  263. package/dist/plugins/media/VideoPlugin.svelte +1 -1
  264. package/dist/plugins/media/empty.svelte.js +2 -7
  265. package/dist/plugins/media/media.d.ts +2 -63
  266. package/dist/plugins/media/media.js +1 -194
  267. package/dist/plugins/media/sources.d.ts +60 -0
  268. package/dist/plugins/media/sources.js +165 -0
  269. package/dist/plugins/media/urlPaste.svelte.d.ts +2 -1
  270. package/dist/plugins/media/urlPaste.svelte.js +12 -21
  271. package/dist/plugins/mention/MentionPlugin.svelte +8 -35
  272. package/dist/plugins/page/PagePlugin.svelte +2 -9
  273. package/dist/plugins/page/page.js +1 -1
  274. package/dist/plugins/pageLink/PageLinkPlugin.svelte +6 -13
  275. package/dist/plugins/richtext/RichTextPlugin.svelte +85 -197
  276. package/dist/plugins/richtext/body.js +1 -1
  277. package/dist/plugins/richtext/calloutIcons.svelte.d.ts +2 -2
  278. package/dist/plugins/richtext/calloutIcons.svelte.js +5 -7
  279. package/dist/plugins/richtext/richTextOperations.d.ts +1 -19
  280. package/dist/plugins/richtext/richTextOperations.js +3 -49
  281. package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +2 -0
  282. package/dist/plugins/slashMenu/SlashMenuController.svelte.js +7 -0
  283. package/dist/plugins/slashMenu/slashMenuPlugin.js +5 -14
  284. package/dist/plugins/table/TableChrome.svelte +2 -3
  285. package/dist/plugins/table/TablePlugin.svelte +20 -54
  286. package/dist/plugins/table/chrome.svelte.d.ts +5 -2
  287. package/dist/plugins/table/chrome.svelte.js +30 -54
  288. package/dist/plugins/table/table.js +3 -3
  289. package/dist/plugins/toc/TocPlugin.svelte +2 -4
  290. package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +7 -0
  291. package/dist/plugins/toolbar/ToolbarController.svelte.js +20 -13
  292. package/dist/plugins/toolbar/toolbarPlugin.js +19 -53
  293. package/dist/plugins/triggers/TriggerController.svelte.d.ts +7 -2
  294. package/dist/plugins/triggers/TriggerController.svelte.js +11 -4
  295. package/dist/plugins/triggers/place.js +1 -2
  296. package/dist/plugins/triggers/triggers.d.ts +6 -1
  297. package/dist/plugins/triggers/triggers.js +12 -13
  298. package/dist/plugins/uploads.svelte.d.ts +5 -2
  299. package/dist/plugins/uploads.svelte.js +25 -15
  300. package/dist/plugins.d.ts +26 -0
  301. package/dist/plugins.js +0 -24
  302. package/dist/selection/replaceSelection.js +2 -2
  303. package/dist/selection/selection.svelte.d.ts +4 -10
  304. package/dist/selection/selection.svelte.js +7 -11
  305. package/dist/session/bindings.js +1 -1
  306. package/dist/session/commands.d.ts +7 -11
  307. package/dist/session/commands.js +13 -19
  308. package/dist/session/inputRules.js +1 -1
  309. package/dist/session/navigation.js +1 -1
  310. package/dist/session/suggestions.svelte.d.ts +0 -11
  311. package/dist/session/suggestions.svelte.js +15 -3
  312. package/dist/session/uploads.d.ts +8 -8
  313. package/dist/surface/lines.js +1 -3
  314. package/dist/surface/observer.svelte.d.ts +17 -1
  315. package/dist/surface/observer.svelte.js +74 -19
  316. package/dist/surface/overlay.d.ts +0 -14
  317. package/dist/surface/overlay.js +0 -25
  318. package/dist/surface/projector.svelte.d.ts +15 -3
  319. package/dist/surface/projector.svelte.js +60 -9
  320. package/dist/text/text.svelte.js +3 -13
  321. package/dist/utils/sanitize.d.ts +38 -0
  322. package/dist/utils/sanitize.js +82 -0
  323. package/dist/utils.d.ts +2 -0
  324. package/dist/utils.js +5 -0
  325. package/package.json +19 -2
  326. package/dist/crdt/placement/rank.d.ts +0 -87
  327. package/dist/crdt/placement/rank.js +0 -332
  328. package/dist/crdt/placement/source.d.ts +0 -43
  329. package/dist/crdt/placement/source.js +0 -66
  330. package/dist/crdt/text/delete-index.d.ts +0 -129
  331. package/dist/crdt/text/delete-index.js +0 -365
  332. package/dist/crdt/text/deletes.d.ts +0 -76
  333. package/dist/crdt/text/deletes.js +0 -362
  334. package/dist/crdt/text/index/anchored.d.ts +0 -17
  335. package/dist/crdt/text/index/anchored.js +0 -197
  336. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +0 -3
  337. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +0 -16
  338. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +0 -3
  339. package/dist/plugins/code/LanguageMenu.svelte +0 -25
  340. package/dist/plugins/code/LanguageMenu.svelte.d.ts +0 -9
  341. package/dist/plugins/equation/EquationEditor.svelte +0 -27
  342. package/dist/plugins/equation/EquationEditor.svelte.d.ts +0 -9
  343. package/dist/plugins/image/ImageToolbarHost.svelte +0 -37
  344. package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +0 -9
  345. package/dist/plugins/richtext/CalloutIconPicker.svelte +0 -26
  346. package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +0 -9
@@ -0,0 +1,749 @@
1
+ import { sanitizeCssColorValue, sanitizeLinkHref, safeMediaSrc, safeWebUrl } from '../utils/sanitize.js';
2
+ import { escapeHtml } from '../clipboard/serializeClipboardFragment.js';
3
+ import { codeKinds, equationKinds, imageKinds, layoutKinds, mediaKinds, pageKinds, richTextKinds, richTextMarks, tableKinds, tocKinds } from '../crdt/semantics.js';
4
+ import { calloutHtml, iconOf } from '../plugins/richtext/callouts.js';
5
+ import { imageAlignOf, imageAltOf, imageWidthOf, imgData, safeImageSrc } from '../plugins/image/image.js';
6
+ import { EMBED_PROVIDERS, EMBED_SANDBOX, claimed, embedSourceOf, fileNameFor, fileNameOf, mediaSourceOf } from '../plugins/media/sources.js';
7
+ import { pageHref, pageHtml, pageIdOf, pageTitleOf } from '../plugins/page/page.js';
8
+ import { expressionOf, parseEquation, sourceMathml } from '../plugins/equation/tex.js';
9
+ /** Tables of rows by name as one: each name's rows merged, in the first table's order. */
10
+ /* @__NO_SIDE_EFFECTS__ */
11
+ export const joined = (...tables) => {
12
+ const out = {};
13
+ for (const table of tables)
14
+ for (const name in table)
15
+ out[name] = { ...out[name], ...table[name] };
16
+ return out;
17
+ };
18
+ // Rich text ---------------------------------------------------------------
19
+ /**
20
+ * Notion's three heading levels: a missing level is h1, any other (a
21
+ * stored `h4`–`h6`, as HTML import reads them) h3.
22
+ */
23
+ export const headingLevel = (level) => level === 'h1' || level === 'h2' || level === 'h3' ? level : level === undefined ? 'h1' : 'h3';
24
+ /** A code block's language as stored: a short name, else none. */
25
+ export const languageOf = (name) => typeof name === 'string' && /^[\w#+.-]{1,40}$/.test(name) ? name : undefined;
26
+ /** The heading levels the rich text kinds create (their presets' data). */
27
+ export const HEADING_LEVELS = ['h1', 'h2', 'h3'];
28
+ /**
29
+ * A list's elements without list semantics (`role="none"`): a list's items
30
+ * are sibling blocks, never the only children of one `ul` or `ol`, so an
31
+ * `li` would be an orphan list item to assistive technology (WCAG 1.3.1);
32
+ * the tags stay for styles and the HTML export.
33
+ */
34
+ export const unlisted = (tag) => ({ tag, attributes: { role: 'none' } });
35
+ /** A tag wrapping `inner`. */
36
+ const wrap = (tag, inner) => `<${tag}>${inner}</${tag}>`;
37
+ /** A heading's tag for `data`. */
38
+ const levelOf = (data) => headingLevel(data?.level);
39
+ /** A `<details>` disclosure over `summary`. */
40
+ const disclosure = (summary, children) => `<details><summary>${summary}</summary>${children}</details>`;
41
+ /** `children` alone: a container's plain text and Markdown. */
42
+ const children = (_, __, children) => children;
43
+ /** A `parse` hook claiming the elements `selector` matches, as `data` reads them (none: `{}`). */
44
+ /* @__NO_SIDE_EFFECTS__ */
45
+ const claims = (selector, data = () => ({})) => ((el) => (el.matches(selector) ? data(el) : undefined));
46
+ // Each kind's halves: its HTML writing (`…Write`: `html`, `plain`) and its
47
+ // elements and HTML reading (`…Read`: `element`, `contentElement`, `parse`).
48
+ export const headingWrite = {
49
+ html: (block, content, children) => wrap(levelOf(block.data), content) + children
50
+ };
51
+ export const headingRead = {
52
+ contentElement: levelOf,
53
+ // h1–h3 come from the presets' tags; h4–h6 read as h3.
54
+ parse: claims('h4,h5,h6', () => ({ level: 'h3' }))
55
+ };
56
+ export const itemWrite = { html: 'li' };
57
+ export const itemRead = { element: unlisted('li') };
58
+ export const numberedRead = {
59
+ element: unlisted('li'),
60
+ // An `li` is a bulleted item (the first `li` kind) unless its list is ordered.
61
+ parse: claims('ol>li')
62
+ };
63
+ export const todoWrite = {
64
+ html: (block, content, children) => `<li data-edytor-todo-item="true"><input type="checkbox"${block.data?.checked === true ? ' checked' : ''}>${content}${children}</li>`,
65
+ plain: (block, content, children) => [`${block.data?.checked === true ? '[x]' : '[ ]'} ${content}`.trim(), children]
66
+ .filter(Boolean)
67
+ .join('\n')
68
+ };
69
+ export const toggleRead = { element: 'details' };
70
+ export const toggleHeadingWrite = {
71
+ html: (block, content, children) => disclosure(wrap(levelOf(block.data), content), children)
72
+ };
73
+ export const toggleHeadingRead = {
74
+ element: 'details',
75
+ contentElement: levelOf,
76
+ // A `details` whose summary holds an h1–h6 (its own export).
77
+ parse: claims('details', (el) => {
78
+ const tag = el.querySelector(':scope > summary > :is(h1, h2, h3, h4, h5, h6)');
79
+ return tag ? { level: headingLevel(tag.localName) } : undefined;
80
+ })
81
+ };
82
+ /** A callout's HTML; `icon` is the one a callout without its own shows. */
83
+ export const calloutWrite = (icon) => ({
84
+ html: (block, content, children) => calloutHtml(icon(block.data), content, children)
85
+ });
86
+ // Its own export, the icon in its attribute.
87
+ export const calloutRead = {
88
+ parse: claims('[data-edytor-callout]', (el) => ({
89
+ icon: el.getAttribute('data-edytor-callout')
90
+ }))
91
+ };
92
+ export const quoteWrite = { html: 'blockquote' };
93
+ export const quoteRead = { contentElement: 'blockquote' };
94
+ export const dividerWrite = { html: () => '<hr>', plain: () => '---' };
95
+ // Their roles are the semantics' (`richTextKinds`), spread beside them where a record is built.
96
+ export const dividerRead = { element: 'hr' };
97
+ export const orderedRead = { element: unlisted('ol') };
98
+ export const unorderedRead = { element: unlisted('ul') };
99
+ export const orderedWrite = { itemKind: 'numbered-list-item', html: 'ol' };
100
+ export const unorderedWrite = { itemKind: 'bulleted-list-item', html: 'ul' };
101
+ /**
102
+ * The rich text kinds' forms as one table (a server's). `callout` is the icon a
103
+ * callout without one of its own shows.
104
+ */
105
+ export const richTextForms = (callout) => ({
106
+ heading: { ...headingRead, ...headingWrite },
107
+ 'bulleted-list-item': { ...itemRead, ...itemWrite },
108
+ 'numbered-list-item': { ...numberedRead, ...itemWrite },
109
+ 'todo-item': todoWrite,
110
+ toggle: toggleRead,
111
+ 'toggle-heading': { ...toggleHeadingRead, ...toggleHeadingWrite },
112
+ callout: { ...calloutRead, ...calloutWrite(callout) },
113
+ quote: { ...quoteRead, ...quoteWrite },
114
+ divider: { ...richTextKinds.divider, ...dividerRead, ...dividerWrite },
115
+ 'ordered-list': { ...richTextKinds['ordered-list'], ...orderedRead, ...orderedWrite },
116
+ 'unordered-list': { ...richTextKinds['unordered-list'], ...unorderedRead, ...unorderedWrite },
117
+ 'list-item': { ...itemRead, ...itemWrite }
118
+ });
119
+ /** A hostile color value (a `;` payload would inject declarations) drops the style. */
120
+ /* @__NO_SIDE_EFFECTS__ */
121
+ const styled = (property) => (value) => {
122
+ const safe = sanitizeCssColorValue(value);
123
+ return { style: safe ? `${property}: ${safe};` : undefined };
124
+ };
125
+ /** An unsafe scheme drops the href (and its target): an inert anchor still carries the text. */
126
+ const linkAttributes = (mark) => {
127
+ const href = sanitizeLinkHref(mark?.href) ?? undefined;
128
+ return { href, target: href && mark.target };
129
+ };
130
+ const linkRead = (el) => {
131
+ const href = el.localName === 'a' && sanitizeLinkHref(el.getAttribute('href'));
132
+ const target = el.getAttribute('target');
133
+ return href ? { href, ...(target ? { target } : {}) } : undefined;
134
+ };
135
+ /**
136
+ * HTML import: a pasted color, sanitized, unless it is the page's
137
+ * default (Google Docs writes black text and transparent backgrounds on
138
+ * every span: they are no mark, and a black mark is unreadable in dark mode).
139
+ */
140
+ const colorOf = (value, none) => !value ||
141
+ new RegExp(`^(initial|inherit|unset|revert|currentcolor|${none})$`).test(value.replace(/\s+/g, '').toLowerCase())
142
+ ? undefined
143
+ : (sanitizeCssColorValue(value) ?? undefined);
144
+ const colorRead = (el) => colorOf(el.style.color, 'black|windowtext|#000|#000000|rgba?\\(0,0,0(,1)?\\)');
145
+ const highlightRead = (el) => el.style.backgroundColor
146
+ ? colorOf(el.style.backgroundColor, 'transparent|rgba\\(\\d+,\\d+,\\d+,0\\)|white|#fff|#ffffff|rgba?\\(255,255,255(,1)?\\)')
147
+ : el.localName === 'mark'
148
+ ? 'yellow'
149
+ : undefined;
150
+ /** HTML import: a tag alias (unless its own style says otherwise) or a style (Google Docs). */
151
+ /* @__NO_SIDE_EFFECTS__ */
152
+ const alias = (tags, property, value) => (el) => (tags.test(el.localName) && !el.style[property]) || value.test(el.style[property]) || undefined;
153
+ const boldRead = alias(/^b$/, 'fontWeight', /^(bold|[6-9]00)$/);
154
+ const italicRead = alias(/^i$/, 'fontStyle', /italic/);
155
+ const underlineRead = alias(/^u$/, 'textDecoration', /underline/);
156
+ const strikeRead = alias(/^(strike|del)$/, 'textDecoration', /line-through/);
157
+ /** The rich text marks' forms, in their registration order (export wraps the first innermost). */
158
+ /* @__NO_SIDE_EFFECTS__ */
159
+ const markForms = () => ({
160
+ bold: { tag: 'strong', parse: boldRead },
161
+ italic: { tag: 'em', parse: italicRead },
162
+ underline: { tag: 'u', parse: underlineRead },
163
+ strike: { tag: 's', parse: strikeRead },
164
+ code: { tag: 'code' },
165
+ // Typing at a link's trailing edge extends it only from inside the anchor.
166
+ link: { ...richTextMarks.link, tag: 'a', attributes: linkAttributes, parse: linkRead },
167
+ superscript: { tag: 'sup' },
168
+ subscript: { tag: 'sub' },
169
+ color: { tag: 'span', attributes: styled('color'), parse: colorRead },
170
+ highlight: { tag: 'span', attributes: styled('background-color'), parse: highlightRead }
171
+ });
172
+ /** The rich text marks' forms, in their registration order (export wraps the first innermost). */
173
+ export const richTextMarkForms = markForms();
174
+ // Code --------------------------------------------------------------------
175
+ const codeLineWrite = { html: (_, content) => `<pre><code>${content}</code></pre>` };
176
+ /** The code plugin's forms: a block of `codeLine`s. */
177
+ export const codeForms = () => ({ code: codeKinds.code, codeLine: codeLineWrite });
178
+ // Tables ------------------------------------------------------------------
179
+ /** A table's HTML: its rows, the header row's and column's cells as `th`. */
180
+ export const tableHtml = (data, rows) => {
181
+ let html = rows;
182
+ if (data.headerColumn === true)
183
+ html = html.replace(/<tr><td>([\s\S]*?)<\/td>/g, '<tr><th>$1</th>');
184
+ if (data.headerRow === true)
185
+ html = html.replace(/^<tr>[\s\S]*?<\/tr>/, (row) => row.replace(/<(\/?)td>/g, '<$1th>'));
186
+ return `<table><tbody>${html}</tbody></table>`;
187
+ };
188
+ /** A row's cells (`td`, `th`). */
189
+ const cellsOf = (row) => [...(row?.children ?? [])].filter((cell) => /^t[dh]$/.test(cell.localName));
190
+ /** An imported `<table>`'s data: a column per cell of its widest row, its header flags. */
191
+ export const parseTable = (table) => {
192
+ // Its own rows, not a nested table's.
193
+ const rows = [...table.querySelectorAll('tr')].filter((tr) => tr.closest('table') === table);
194
+ const width = Math.max(1, ...rows.map((tr) => cellsOf(tr).length));
195
+ const head = (cell) => cell?.localName === 'th';
196
+ const headerRow = rows.length > 1 && cellsOf(rows[0]).every(head);
197
+ const headerColumn = rows.length > 0 && rows.slice(headerRow ? 1 : 0).every((tr) => head(cellsOf(tr)[0]));
198
+ return {
199
+ columns: Array.from({ length: width }, (_, i) => ({ id: `c${i + 1}` })),
200
+ ...(headerRow && { headerRow: true }),
201
+ ...(headerColumn && !headerRow && { headerColumn: true })
202
+ };
203
+ };
204
+ /** An imported cell's column: its position in its row (`c1`, `c2`, … as its table's). */
205
+ export const parseCell = (cell) => ({
206
+ column: `c${Math.max(0, cellsOf(cell.parentElement).indexOf(cell)) + 1}`
207
+ });
208
+ const tableWrite = {
209
+ html: (block, _, rows) => tableHtml(block.data ?? {}, rows),
210
+ plain: children
211
+ };
212
+ const tableRead = { parse: claims('table', parseTable) };
213
+ const rowWrite = {
214
+ html: (_, __, cells) => wrap('tr', cells),
215
+ plain: (_, __, cells) => cells.split('\n').join('\t')
216
+ };
217
+ const rowRead = { parse: claims('tr') };
218
+ const cellWrite = {
219
+ html: (_, content) => wrap('td', content),
220
+ plain: (_, content) => content.replace(/\n/g, ' ')
221
+ };
222
+ const cellRead = { parse: claims('td,th', parseCell) };
223
+ /** The table plugin's forms. */
224
+ export const tableForms = () => ({
225
+ table: { ...tableKinds.table, ...tableWrite, ...tableRead },
226
+ tableRow: { ...tableKinds.tableRow, ...rowWrite, ...rowRead },
227
+ tableCell: { ...tableKinds.tableCell, ...cellWrite, ...cellRead }
228
+ });
229
+ // Images and media --------------------------------------------------------
230
+ /** A media block's HTML: its `figure` (with `mark`), `media` in it, then its caption. */
231
+ const figure = (media, caption, mark = '') => `<figure${mark}>${media}<figcaption>${caption}</figcaption></figure>`;
232
+ /** A link to `href` reading `text` (escaped), or nothing without `href`. */
233
+ const anchor = (href, text) => href ? `<a href="${escapeHtml(href)}">${escapeHtml(text ?? '')}</a>` : '';
234
+ const imageWrite = {
235
+ html: (block, caption) => {
236
+ const src = safeImageSrc(block.data?.src);
237
+ const width = imageWidthOf(block.data);
238
+ const align = imageAlignOf(block.data);
239
+ const img = src
240
+ ? `<img src="${escapeHtml(src)}" alt="${escapeHtml(imageAltOf(block.data))}"${width ? ` width="${Math.round(width)}"` : ''}>`
241
+ : '';
242
+ return figure(img, caption, align === 'center' ? '' : ` data-align="${align}"`);
243
+ }
244
+ };
245
+ const imageRead = {
246
+ // A `figure` holding an `img`, or a bare `img`, with an accepted
247
+ // source; a pasted inline image over the cap is not imported.
248
+ parse: (el) => {
249
+ if (el.localName === 'img')
250
+ return imgData(el) ?? undefined;
251
+ const data = el.localName === 'figure' && imgData(el.querySelector('img'));
252
+ const align = data && el.getAttribute('data-align');
253
+ return data ? (align === 'left' || align === 'right' ? { ...data, align } : data) : undefined;
254
+ }
255
+ };
256
+ /** The image plugin's forms: a `figure`. */
257
+ export const imageForms = () => ({
258
+ image: { ...imageKinds.image, ...imageWrite, ...imageRead }
259
+ });
260
+ /** A bookmark's stored text field: trimmed, capped. */
261
+ export const capped = (value, max) => typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : undefined;
262
+ /** A media block's plain text: its source, then its caption. */
263
+ const sourcePlain = (source, caption) => [source, caption].filter(Boolean).join('\n');
264
+ const webPlain = (block, caption) => sourcePlain(safeWebUrl(block.data?.url), caption);
265
+ const embedWrite = (providers) => ({
266
+ html: (block, caption) => {
267
+ const player = embedSourceOf(block.data?.url, providers);
268
+ const url = safeWebUrl(block.data?.url);
269
+ return figure(player
270
+ ? `<iframe src="${escapeHtml(player.src)}" title="${escapeHtml(player.provider.name)}" sandbox="${EMBED_SANDBOX}" allowfullscreen></iframe>`
271
+ : anchor(url, url), caption);
272
+ },
273
+ plain: webPlain
274
+ });
275
+ const embedRead = (providers) => ({
276
+ // An allowlisted `iframe` (bare, or in a `figure`): its `src` is the link.
277
+ parse: (el) => {
278
+ const src = claimed(el, 'iframe')?.getAttribute('src');
279
+ const url = embedSourceOf(src, providers) && safeWebUrl(src);
280
+ return url ? { url } : undefined;
281
+ }
282
+ });
283
+ const bookmarkWrite = {
284
+ html: (block, caption) => {
285
+ const url = safeWebUrl(block.data?.url);
286
+ return figure(anchor(url, capped(block.data?.title, 300) ?? url), caption, ' data-edytor-bookmark');
287
+ },
288
+ plain: webPlain
289
+ };
290
+ // Its own export: a `figure[data-edytor-bookmark]` holding the link.
291
+ const bookmarkRead = {
292
+ parse: claims('figure[data-edytor-bookmark]', (el) => {
293
+ const link = el.querySelector('a[href]');
294
+ const url = safeWebUrl(link?.getAttribute('href'));
295
+ const title = capped(link?.textContent, 300);
296
+ return url ? { url, ...(title && title !== url ? { title } : {}) } : undefined;
297
+ })
298
+ };
299
+ const fileWrite = {
300
+ html: (block, caption) => {
301
+ const src = safeMediaSrc(block.data?.src);
302
+ return figure(anchor(src, fileNameFor(block.data, src)), caption, ' data-edytor-file');
303
+ },
304
+ plain: (block, caption) => sourcePlain(safeMediaSrc(block.data?.src), caption)
305
+ };
306
+ // Its own export: a `figure[data-edytor-file]` holding the link.
307
+ const fileRead = {
308
+ parse: claims('figure[data-edytor-file]', (el) => {
309
+ const link = el.querySelector('a[href]');
310
+ const src = safeMediaSrc(link?.getAttribute('href'));
311
+ const name = link?.textContent?.trim().slice(0, 300);
312
+ return src ? { src, name: name || fileNameOf(src) } : undefined;
313
+ })
314
+ };
315
+ /** A `video`'s or an `audio`'s HTML: `<video src="…" controls></video>` in its `figure`. */
316
+ const sourceWrite = (kind) => ({
317
+ html: (block, caption) => {
318
+ const src = safeMediaSrc(block.data?.src);
319
+ return figure(src ? `<${kind} src="${escapeHtml(src)}" controls></${kind}>` : '', caption);
320
+ }
321
+ });
322
+ const sourceRead = (kind) => ({
323
+ parse: (el) => {
324
+ const src = mediaSourceOf(claimed(el, kind));
325
+ return src ? { src } : undefined;
326
+ }
327
+ });
328
+ /** The media plugins' forms; `providers` are the embed players the view allows. */
329
+ export const mediaForms = (providers = EMBED_PROVIDERS) => ({
330
+ embed: { ...mediaKinds.embed, ...embedWrite(providers), ...embedRead(providers) },
331
+ bookmark: { ...mediaKinds.bookmark, ...bookmarkWrite, ...bookmarkRead },
332
+ file: { ...mediaKinds.file, ...fileWrite, ...fileRead },
333
+ video: { ...mediaKinds.video, ...sourceWrite('video'), ...sourceRead('video') },
334
+ audio: { ...mediaKinds.audio, ...sourceWrite('audio'), ...sourceRead('audio') }
335
+ });
336
+ // Pages, contents, equations, columns -------------------------------------
337
+ const pageWrite = (options = {}, untitled = 'Untitled') => ({
338
+ html: (block) => pageHtml(block.data, options, untitled),
339
+ plain: (block) => pageTitleOf(block.data?.title) ?? untitled
340
+ });
341
+ // Its own export: a `p[data-edytor-page]` naming the page.
342
+ const pageRead = {
343
+ parse: claims('p[data-edytor-page]', (el) => {
344
+ const pageId = pageIdOf(el.getAttribute('data-edytor-page'));
345
+ const title = pageTitleOf(el.textContent);
346
+ return pageId ? { pageId, ...(title && { title }) } : undefined;
347
+ })
348
+ };
349
+ /** The page plugin's forms; `untitled` names a page without a title. */
350
+ export const pageForms = (options = {}, untitled = 'Untitled') => ({
351
+ page: { ...pageKinds.page, ...pageWrite(options, untitled), ...pageRead }
352
+ });
353
+ const tocWrite = { html: () => '<nav data-edytor-toc></nav>', plain: () => '' };
354
+ const tocRead = { parse: claims('nav[data-edytor-toc]') };
355
+ /** The table of contents plugin's forms: drawn from the document, never carried. */
356
+ export const tocForms = () => ({ toc: { ...tocKinds.toc, ...tocWrite, ...tocRead } });
357
+ const equationWrite = (mathml) => ({
358
+ html: (block) => `<div data-edytor-equation>${mathml(expressionOf(block.data), true)}</div>`,
359
+ plain: (block) => expressionOf(block.data)
360
+ });
361
+ const equationRead = { parse: (el) => parseEquation(el, true) };
362
+ const inlineEquationWrite = (mathml) => ({
363
+ plain: expressionOf,
364
+ html: (data) => mathml(expressionOf(data), false)
365
+ });
366
+ const inlineEquationRead = { parse: (el) => parseEquation(el, false) };
367
+ /** The equation plugin's forms. */
368
+ export const equationForms = (mathml = sourceMathml) => ({
369
+ blocks: { equation: { ...equationKinds.equation, ...equationWrite(mathml), ...equationRead } },
370
+ inlineBlocks: { inlineEquation: { ...inlineEquationWrite(mathml), ...inlineEquationRead } }
371
+ });
372
+ /** A column's weight: `data.width`, a positive number, else 1. */
373
+ export const weightOf = (data) => {
374
+ const width = data?.width;
375
+ return typeof width === 'number' && Number.isFinite(width) && width > 0 ? width : 1;
376
+ };
377
+ const columnsWrite = {
378
+ html: (_, __, inner) => `<div data-edytor-columns>${inner}</div>`,
379
+ plain: children
380
+ };
381
+ const columnsRead = { parse: claims('[data-edytor-columns]') };
382
+ const columnWrite = {
383
+ html: (block, _, inner) => `<div data-edytor-column data-width="${weightOf(block.data)}">${inner}</div>`,
384
+ plain: children
385
+ };
386
+ const columnRead = {
387
+ parse: claims('[data-edytor-column]', (el) => {
388
+ // A weight read back from `data-width`, when it is one.
389
+ const width = Number(el.getAttribute('data-width'));
390
+ return Number.isFinite(width) && width > 0 ? { width } : {};
391
+ })
392
+ };
393
+ /** The columns plugin's forms. */
394
+ export const columnsForms = () => ({
395
+ columns: { ...layoutKinds.columns, ...columnsWrite, ...columnsRead },
396
+ column: { ...layoutKinds.column, ...columnWrite, ...columnRead }
397
+ });
398
+ // Atoms -------------------------------------------------------------------
399
+ /** A string field of an atom's data, else `''`. */
400
+ const field = (data, key) => {
401
+ const value = data?.[key];
402
+ return typeof value === 'string' ? value : '';
403
+ };
404
+ const mentionWrite = (kind, char) => ({
405
+ plain: (data) => char + field(data, 'label'),
406
+ html: (data) => {
407
+ const id = data?.id;
408
+ return (`<span data-edytor-mention="${escapeHtml(kind)}"` +
409
+ (typeof id === 'string' ? ` data-mention-id="${escapeHtml(id)}"` : '') +
410
+ `>${escapeHtml(char + field(data, 'label'))}</span>`);
411
+ }
412
+ });
413
+ const mentionRead = (kind, char) => ({
414
+ parse: (element) => {
415
+ // A bare `data-edytor-mention` (an older export) is a `mention`.
416
+ const own = element.getAttribute('data-edytor-mention');
417
+ if (own === null || (own || 'mention') !== kind)
418
+ return undefined;
419
+ const text = element.textContent ?? '';
420
+ const label = text.startsWith(char) ? text.slice(char.length) : text;
421
+ return { id: element.getAttribute('data-mention-id') ?? '', label };
422
+ }
423
+ });
424
+ /** A mention atom's forms (`@Ada`): `kind` is its atom type, `char` its trigger. */
425
+ export const mentionForms = (kind = 'mention', char = '@') => ({
426
+ ...mentionWrite(kind, char),
427
+ ...mentionRead(kind, char)
428
+ });
429
+ /** A page link atom's forms: its title. */
430
+ export const pageLinkForms = () => ({ plain: (data) => field(data, 'title') });
431
+ // The export's own forms -----------------------------------------------------
432
+ /** A child block's text joined (a code line's): marks and atoms aside. */
433
+ const textOf = (block) => (block.content ?? []).map((part) => ('text' in part ? part.text : '')).join('');
434
+ /** A code block's lines. */
435
+ const linesOf = (block) => (block.children ?? []).map(textOf).join('\n');
436
+ /**
437
+ * The HTML forms a view's clipboard does without, filled in by `toHTML` for
438
+ * the records that lack them: a toggle's `<details>`, a code block's one
439
+ * `<pre>` naming its language.
440
+ */
441
+ export const htmlExtras = () => ({
442
+ code: {
443
+ html: (block) => {
444
+ const language = languageOf(block.data?.language);
445
+ const named = language ? escapeHtml(language) : '';
446
+ return `<pre${named && ` data-language="${named}"`}><code${named && ` class="language-${named}"`}>${escapeHtml(linesOf(block))}</code></pre>`;
447
+ },
448
+ plain: linesOf
449
+ },
450
+ toggle: { html: (_, content, children) => disclosure(content, children) }
451
+ });
452
+ /** The import hooks a view's paste does without, filled in by `fromHTML`. */
453
+ export const parseExtras = () => ({
454
+ // A `pre`, its language from its own or its `code`'s class or attribute.
455
+ code: {
456
+ parse: claims('pre', (el) => {
457
+ const code = [...el.children].find((child) => child.localName === 'code');
458
+ const named = [el, code].map((at) => at?.getAttribute('data-language') ??
459
+ /(?:^|\s)(?:language|lang)-([\w#+.-]+)/.exec(at?.getAttribute('class') ?? '')?.[1]);
460
+ const language = languageOf(named.find(Boolean));
461
+ return language ? { language } : {};
462
+ })
463
+ },
464
+ // Its own export, and a GitHub task list's item.
465
+ 'todo-item': {
466
+ parse: claims('li', (el) => {
467
+ const box = [...el.children].find((child) => child.localName === 'input' && child.getAttribute('type') === 'checkbox');
468
+ return box ? { checked: box.hasAttribute('checked') } : undefined;
469
+ })
470
+ }
471
+ });
472
+ /** The records `toHTML` reads by default: every bundled kind's, mark's and atom's HTML writing. */
473
+ export const bundledWrite = () => ({
474
+ blocks: {
475
+ heading: headingWrite,
476
+ 'bulleted-list-item': itemWrite,
477
+ 'numbered-list-item': itemWrite,
478
+ 'todo-item': todoWrite,
479
+ 'toggle-heading': toggleHeadingWrite,
480
+ callout: calloutWrite(iconOf),
481
+ quote: quoteWrite,
482
+ divider: dividerWrite,
483
+ 'ordered-list': orderedWrite,
484
+ 'unordered-list': unorderedWrite,
485
+ 'list-item': itemWrite,
486
+ codeLine: codeLineWrite,
487
+ table: tableWrite,
488
+ tableRow: rowWrite,
489
+ tableCell: cellWrite,
490
+ image: imageWrite,
491
+ embed: embedWrite(EMBED_PROVIDERS),
492
+ bookmark: bookmarkWrite,
493
+ file: fileWrite,
494
+ video: sourceWrite('video'),
495
+ audio: sourceWrite('audio'),
496
+ page: pageWrite(),
497
+ toc: tocWrite,
498
+ equation: equationWrite(sourceMathml),
499
+ columns: columnsWrite,
500
+ column: columnWrite,
501
+ ...htmlExtras()
502
+ },
503
+ marks: {
504
+ bold: { tag: 'strong' },
505
+ italic: { tag: 'em' },
506
+ underline: { tag: 'u' },
507
+ strike: { tag: 's' },
508
+ code: { tag: 'code' },
509
+ link: { tag: 'a', attributes: linkAttributes },
510
+ superscript: { tag: 'sup' },
511
+ subscript: { tag: 'sub' },
512
+ color: { tag: 'span', attributes: styled('color') },
513
+ highlight: { tag: 'span', attributes: styled('background-color') }
514
+ },
515
+ inlineBlocks: {
516
+ mention: mentionWrite('mention', '@'),
517
+ pageLink: pageLinkForms(),
518
+ inlineEquation: inlineEquationWrite(sourceMathml)
519
+ }
520
+ });
521
+ /**
522
+ * The records `fromHTML` reads by default: every bundled kind's roles, its
523
+ * presets' data (HTML import finds a kind by a preset's tag: an `h2`, a
524
+ * `blockquote`) and its HTML reading, in the plugins' order (the first
525
+ * kind claiming an element wins).
526
+ */
527
+ export const bundledRead = () => {
528
+ const levels = { presets: HEADING_LEVELS.map((level) => ({ data: { level } })) };
529
+ const one = { presets: [{}] };
530
+ return {
531
+ blocks: {
532
+ paragraph: {},
533
+ heading: { ...headingRead, ...levels },
534
+ 'bulleted-list-item': { ...itemRead, ...one },
535
+ 'numbered-list-item': { ...numberedRead, ...one },
536
+ 'todo-item': { presets: [{ data: { checked: false } }] },
537
+ toggle: { ...toggleRead, ...one },
538
+ 'toggle-heading': { ...toggleHeadingRead, ...levels },
539
+ callout: { ...calloutRead, ...one },
540
+ quote: { ...quoteRead, ...one },
541
+ divider: { ...richTextKinds.divider, ...dividerRead, ...one },
542
+ 'ordered-list': { ...richTextKinds['ordered-list'], ...orderedRead },
543
+ 'unordered-list': { ...richTextKinds['unordered-list'], ...unorderedRead },
544
+ 'list-item': itemRead,
545
+ code: codeKinds.code,
546
+ codeLine: {},
547
+ table: { ...tableKinds.table, ...tableRead },
548
+ tableRow: { ...tableKinds.tableRow, ...rowRead },
549
+ tableCell: { ...tableKinds.tableCell, ...cellRead },
550
+ image: { ...imageKinds.image, ...imageRead },
551
+ embed: { ...mediaKinds.embed, ...embedRead(EMBED_PROVIDERS) },
552
+ bookmark: { ...mediaKinds.bookmark, ...bookmarkRead },
553
+ file: { ...mediaKinds.file, ...fileRead },
554
+ video: { ...mediaKinds.video, ...sourceRead('video') },
555
+ audio: { ...mediaKinds.audio, ...sourceRead('audio') },
556
+ page: { ...pageKinds.page, ...pageRead },
557
+ toc: { ...tocKinds.toc, ...tocRead },
558
+ equation: { ...equationKinds.equation, ...equationRead },
559
+ columns: { ...layoutKinds.columns, ...columnsRead },
560
+ column: { ...layoutKinds.column, ...columnRead }
561
+ },
562
+ marks: {
563
+ bold: { tag: 'strong', parse: boldRead },
564
+ italic: { tag: 'em', parse: italicRead },
565
+ underline: { tag: 'u', parse: underlineRead },
566
+ strike: { tag: 's', parse: strikeRead },
567
+ code: { tag: 'code' },
568
+ link: { parse: linkRead },
569
+ superscript: { tag: 'sup' },
570
+ subscript: { tag: 'sub' },
571
+ color: { parse: colorRead },
572
+ highlight: { parse: highlightRead }
573
+ },
574
+ inlineBlocks: {
575
+ mention: mentionRead('mention', '@'),
576
+ inlineEquation: inlineEquationRead
577
+ }
578
+ };
579
+ };
580
+ /**
581
+ * Sibling blocks' HTML (`toHTML`): a run of list items no list element
582
+ * holds (a document's items are siblings, `<li>` alone) is wrapped in its
583
+ * list's tag, the one its container kind exports (`ol` for numbered items).
584
+ */
585
+ export const listSiblings = (kinds) => (blocks, html, parent) => {
586
+ let out = '';
587
+ let open = null;
588
+ blocks.forEach((block, i) => {
589
+ let list = null;
590
+ if (parent !== 'ul' && parent !== 'ol' && /^<li[\s>]/.test(html[i])) {
591
+ list = 'ul';
592
+ for (const [, kind] of kinds.blocks)
593
+ if (kind.itemKind === block.type && (kind.html === 'ol' || kind.html === 'ul'))
594
+ list = kind.html;
595
+ }
596
+ if (open && open !== list)
597
+ out += `</${open}>`;
598
+ if (list && open !== list)
599
+ out += `<${list}>`;
600
+ open = list;
601
+ out += html[i];
602
+ });
603
+ return open ? `${out}</${open}>` : out;
604
+ };
605
+ // Markdown -------------------------------------------------------------------
606
+ /** A markdown link destination: `<…>` when it holds a space or a parenthesis. */
607
+ const destination = (href) => /[\s()<>]/.test(href) ? `<${href.replace(/>/g, '%3E')}>` : href;
608
+ /** Text for a markdown link's label: its brackets and backslashes escaped. */
609
+ const label = (text) => text.replace(/[\\[\]]/g, (c) => `\\${c}`);
610
+ /** A markdown link reading `text` (else its href), or nothing without `href`. */
611
+ const link = (text, href) => href ? `[${label(text || href)}](${destination(href)})` : '';
612
+ /** Parts a blank line apart, the empty ones left out. */
613
+ const apart = (...parts) => parts.filter(Boolean).join('\n\n');
614
+ /** A quote's lines, each behind `> `. */
615
+ const quoted = (text) => text.replace(/^(.?)/gm, (_, c) => (c ? `> ${c}` : '>'));
616
+ /** A list item's markdown: its marker and text, its children indented under it. */
617
+ const item = (marker) => (block, content, children, { index, indent }) => {
618
+ const own = marker(block, index);
619
+ return own + content + (children && `\n${indent(children, ' '.repeat(own.length))}`);
620
+ };
621
+ /** A `<details>` disclosure: GitHub and most readers show it folded; `textToBlocks` reads it back. */
622
+ const details = (summary, children) => `<details>\n<summary>${summary}</summary>\n\n${children && `${children}\n\n`}</details>`;
623
+ /** A media block's link line (a bookmark's, a file's) reading `text`, its caption after it. */
624
+ const linkLine = (read) => (block, caption, children) => {
625
+ const [text, href] = read(block.data);
626
+ return apart(link(text, href), caption, children);
627
+ };
628
+ /** Each bundled kind's Markdown form. */
629
+ const markdownForms = () => {
630
+ const media = linkLine((data) => {
631
+ const src = safeMediaSrc(data?.src);
632
+ return [src ?? '', src];
633
+ });
634
+ return {
635
+ heading: (block, content, children) => apart(`${'#'.repeat(Number(levelOf(block.data).slice(1)))} ${content}`, children),
636
+ 'bulleted-list-item': item(() => '- '),
637
+ 'numbered-list-item': item((_, index) => `${index}. `),
638
+ 'todo-item': item((block) => `- [${block.data?.checked === true ? 'x' : ' '}] `),
639
+ 'list-item': item(() => '- '),
640
+ toggle: (_, content, children) => details(content, children),
641
+ 'toggle-heading': (block, content, children) => details(wrap(levelOf(block.data), content), children),
642
+ // A GitHub alert: `> [!NOTE]`, then the icon and the title, then the content.
643
+ callout: (block, content, children) => quoted([
644
+ '[!NOTE]',
645
+ [iconOf(block.data), content].filter(Boolean).join(' '),
646
+ ...(children ? ['', children] : [])
647
+ ].join('\n')),
648
+ quote: (_, content, children) => quoted(apart(content, children)),
649
+ divider: () => '---',
650
+ // Its items are `list-item`s: numbered here.
651
+ 'ordered-list': (_, __, children) => {
652
+ let n = 0;
653
+ return children.replace(/^- /gm, () => `${++n}. `);
654
+ },
655
+ 'unordered-list': children,
656
+ code: (block) => {
657
+ const lines = linesOf(block);
658
+ // A fence longer than any backtick run its lines start with (three at least).
659
+ const fence = '`'.repeat(Math.max(3, ...[...lines.matchAll(/^\s*(`{3,})/gm)].map((m) => m[1].length + 1)));
660
+ return `${fence}${languageOf(block.data?.language) ?? ''}\n${lines}${lines && '\n'}${fence}`;
661
+ },
662
+ codeLine: textOf,
663
+ table: (block, _, __, { inline }) => {
664
+ // The rows as cells in the columns' order (`data.columns`), each row padded to them.
665
+ const listed = block.data?.columns;
666
+ const columns = (Array.isArray(listed) ? listed : [])
667
+ .map((c) => c?.id)
668
+ .filter((id) => typeof id === 'string');
669
+ const rows = (block.children ?? []).map((row) => {
670
+ const cells = row.children ?? [];
671
+ return (columns.length
672
+ ? columns.map((id) => cells.find((cell) => cell.data?.column === id)?.content)
673
+ : cells.map((cell) => cell.content)).map((content) => inline(content ?? [])
674
+ .replace(/\|/g, '\\|')
675
+ .replace(/\\\n/g, '<br>'));
676
+ });
677
+ if (!rows.length)
678
+ return '';
679
+ const width = Math.max(1, ...rows.map((row) => row.length));
680
+ const line = (row) => `| ${Array.from({ length: width }, (_, i) => row[i] ?? '').join(' | ')} |`;
681
+ return [line(rows[0]), line(Array(width).fill('---')), ...rows.slice(1).map(line)].join('\n');
682
+ },
683
+ tableCell: (_, content) => content,
684
+ image: (block, caption, children) => {
685
+ const src = safeImageSrc(block.data?.src);
686
+ const title = caption && ` "${caption.replace(/"/g, '\\"')}"`;
687
+ return src
688
+ ? apart(`![${label(imageAltOf(block.data))}](${destination(src)}${title})`, children)
689
+ : apart(caption, children);
690
+ },
691
+ embed: linkLine((data) => {
692
+ const url = safeWebUrl(data?.url);
693
+ return [url ?? '', url];
694
+ }),
695
+ bookmark: linkLine((data) => {
696
+ const url = safeWebUrl(data?.url);
697
+ return [capped(data?.title, 300) ?? url ?? '', url];
698
+ }),
699
+ file: linkLine((data) => {
700
+ const src = safeMediaSrc(data?.src);
701
+ return [fileNameFor(data, src), src];
702
+ }),
703
+ video: media,
704
+ audio: media,
705
+ page: (block) => {
706
+ const title = pageTitleOf(block.data?.title) ?? 'Untitled';
707
+ return link(title, pageHref({}, pageIdOf(block.data?.pageId))) || label(title);
708
+ },
709
+ toc: () => '',
710
+ equation: (block) => `$$\n${expressionOf(block.data)}\n$$`,
711
+ columns: children,
712
+ column: children
713
+ };
714
+ };
715
+ /** Each rich text mark's delimiters; an unsafe link exports its text alone, as it renders inert. */
716
+ const markMarkdown = () => ({
717
+ bold: ['**', '**'],
718
+ italic: ['*', '*'],
719
+ strike: ['~~', '~~'],
720
+ code: ['`', '`'],
721
+ link: (value) => {
722
+ const href = sanitizeLinkHref(value?.href);
723
+ return href ? ['[', `](${destination(href)})`] : undefined;
724
+ }
725
+ });
726
+ /** Each bundled atom's Markdown form (else its `plain` form). */
727
+ const atomMarkdown = () => ({
728
+ inlineEquation: (data) => `$${expressionOf(data)}$`
729
+ });
730
+ /** Each form of `forms` as a record's `markdown` form. */
731
+ const rowsOf = (forms) => Object.fromEntries(Object.entries(forms).map(([name, markdown]) => [name, { markdown }]));
732
+ /** The Markdown forms by name, filled in by `toMarkdown` for the records that lack them. */
733
+ export const markdownExtras = () => ({
734
+ blocks: rowsOf(markdownForms()),
735
+ marks: rowsOf(markMarkdown()),
736
+ inlineBlocks: rowsOf(atomMarkdown())
737
+ });
738
+ /** The records `toMarkdown` reads by default: the Markdown forms, and the atoms' plain text. */
739
+ export const bundledMarkdown = () => {
740
+ const forms = markdownExtras();
741
+ return {
742
+ ...forms,
743
+ inlineBlocks: {
744
+ mention: { plain: mentionWrite('mention', '@').plain },
745
+ pageLink: pageLinkForms(),
746
+ ...forms.inlineBlocks
747
+ }
748
+ };
749
+ };