edytor 1.0.0-rc.2 → 1.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 (653) hide show
  1. package/README.md +65 -25
  2. package/dist/block/block.svelte.d.ts +10 -85
  3. package/dist/block/block.svelte.js +47 -141
  4. package/dist/block/colors.d.ts +0 -9
  5. package/dist/block/colors.js +1 -18
  6. package/dist/block/contentRange.d.ts +5 -1
  7. package/dist/block/contentRange.js +15 -16
  8. package/dist/block/inlineBlock.svelte.d.ts +8 -10
  9. package/dist/block/inlineBlock.svelte.js +14 -22
  10. package/dist/clipboard/createClipboardFragment.js +22 -64
  11. package/dist/clipboard/fragmentData.js +17 -42
  12. package/dist/clipboard/htmlFlow.d.ts +6 -1
  13. package/dist/clipboard/htmlFlow.js +26 -13
  14. package/dist/clipboard/insertClipboardFragment.js +5 -4
  15. package/dist/clipboard/moveText.js +16 -8
  16. package/dist/clipboard/serializeClipboardFragment.d.ts +2 -0
  17. package/dist/clipboard/serializeClipboardFragment.js +32 -28
  18. package/dist/cloudflare/DocumentRoom.d.ts +22 -17
  19. package/dist/cloudflare/DocumentRoom.js +10 -5
  20. package/dist/cloudflare/index.d.ts +1 -1
  21. package/dist/cloudflare/index.js +1 -1
  22. package/dist/cloudflare/move.d.ts +1 -18
  23. package/dist/cloudflare/move.js +2 -48
  24. package/dist/cloudflare/room/admission.d.ts +1 -60
  25. package/dist/cloudflare/room/admission.js +25 -29
  26. package/dist/cloudflare/room/comments.d.ts +1 -3
  27. package/dist/cloudflare/room/comments.js +6 -6
  28. package/dist/cloudflare/room/context.d.ts +20 -82
  29. package/dist/cloudflare/room/context.js +8 -8
  30. package/dist/cloudflare/room/forged.d.ts +1 -1
  31. package/dist/cloudflare/room/forged.js +2 -12
  32. package/dist/cloudflare/room/frames.d.ts +1 -1
  33. package/dist/cloudflare/room/frames.js +1 -1
  34. package/dist/cloudflare/room/history.d.ts +1 -25
  35. package/dist/cloudflare/room/history.js +39 -54
  36. package/dist/cloudflare/room/moves.d.ts +1 -13
  37. package/dist/cloudflare/room/moves.js +33 -57
  38. package/dist/cloudflare/room/presence.d.ts +1 -11
  39. package/dist/cloudflare/room/presence.js +4 -4
  40. package/dist/cloudflare/room/purge.d.ts +1 -2
  41. package/dist/cloudflare/room/purge.js +2 -2
  42. package/dist/cloudflare/room/replicas.d.ts +1 -7
  43. package/dist/cloudflare/room/replicas.js +2 -2
  44. package/dist/cloudflare/room/storage.d.ts +9 -30
  45. package/dist/cloudflare/room/storage.js +52 -104
  46. package/dist/cloudflare/room/updates.d.ts +4 -4
  47. package/dist/cloudflare/room/updates.js +17 -24
  48. package/dist/cloudflare/room/validation.d.ts +1 -5
  49. package/dist/cloudflare/room/validation.js +16 -37
  50. package/dist/collaboration/RemoteSelections.svelte +13 -17
  51. package/dist/collaboration/comments/client.d.ts +2 -2
  52. package/dist/collaboration/comments/client.js +1 -1
  53. package/dist/collaboration/history/index.d.ts +5 -5
  54. package/dist/collaboration/history/index.js +5 -5
  55. package/dist/collaboration/history/panel.svelte.d.ts +91 -0
  56. package/dist/collaboration/history/panel.svelte.js +223 -0
  57. package/dist/collaboration/providers.d.ts +12 -3
  58. package/dist/collaboration/providers.js +5 -4
  59. package/dist/components/Block.svelte +160 -119
  60. package/dist/components/Block.svelte.d.ts +8 -3
  61. package/dist/components/Content.svelte +91 -44
  62. package/dist/components/CustomMark.svelte +46 -0
  63. package/dist/components/CustomMark.svelte.d.ts +11 -0
  64. package/dist/components/Edytor.svelte +80 -95
  65. package/dist/components/Edytor.svelte.d.ts +8 -12
  66. package/dist/components/InlineBlock.svelte +4 -9
  67. package/dist/components/Mark.svelte +46 -35
  68. package/dist/components/Mark.svelte.d.ts +7 -0
  69. package/dist/components/Ranges.svelte +96 -0
  70. package/dist/components/Ranges.svelte.d.ts +19 -0
  71. package/dist/components/Suggestion.svelte +3 -2
  72. package/dist/components/Text.svelte +48 -14
  73. package/dist/components/Text.svelte.d.ts +1 -1
  74. package/dist/crdt/admission.d.ts +2 -9
  75. package/dist/crdt/admission.js +3 -9
  76. package/dist/crdt/attribution/attribution.d.ts +11 -72
  77. package/dist/crdt/attribution/attribution.js +5 -52
  78. package/dist/crdt/attribution/block.d.ts +0 -78
  79. package/dist/crdt/attribution/block.js +1 -144
  80. package/dist/crdt/data.d.ts +8 -13
  81. package/dist/crdt/data.js +30 -70
  82. package/dist/crdt/doc/anchors.js +4 -4
  83. package/dist/crdt/doc/content.d.ts +5 -1
  84. package/dist/crdt/doc/content.js +3 -1
  85. package/dist/crdt/doc/delete.d.ts +1 -0
  86. package/dist/crdt/doc/funnel.d.ts +0 -23
  87. package/dist/crdt/doc/funnel.js +34 -85
  88. package/dist/crdt/doc/gate.d.ts +8 -9
  89. package/dist/crdt/doc/gate.js +7 -17
  90. package/dist/crdt/doc/history.d.ts +87 -14
  91. package/dist/crdt/doc/history.js +334 -149
  92. package/dist/crdt/doc/layout.d.ts +0 -1
  93. package/dist/crdt/doc/layout.js +2 -3
  94. package/dist/crdt/doc/meta.d.ts +4 -4
  95. package/dist/crdt/doc/meta.js +10 -21
  96. package/dist/crdt/doc/moves.d.ts +4 -4
  97. package/dist/crdt/doc/moves.js +5 -5
  98. package/dist/crdt/doc/plan.d.ts +2 -0
  99. package/dist/crdt/doc/plan.js +2 -0
  100. package/dist/crdt/doc/reads.d.ts +1 -5
  101. package/dist/crdt/doc/reads.js +38 -49
  102. package/dist/crdt/doc/seed.d.ts +15 -6
  103. package/dist/crdt/doc/seed.js +27 -26
  104. package/dist/crdt/doc/split-merge.d.ts +1 -3
  105. package/dist/crdt/doc/split-merge.js +4 -5
  106. package/dist/crdt/doc/steps.d.ts +3 -7
  107. package/dist/crdt/doc/steps.js +20 -9
  108. package/dist/crdt/doc/types.d.ts +0 -10
  109. package/dist/crdt/document.d.ts +24 -115
  110. package/dist/crdt/document.js +207 -305
  111. package/dist/crdt/edytor-doc.d.ts +671 -90
  112. package/dist/crdt/edytor-doc.js +73 -149
  113. package/dist/crdt/engine-api.d.ts +15 -47
  114. package/dist/crdt/engine.d.ts +7 -17
  115. package/dist/crdt/engine.js +9 -19
  116. package/dist/crdt/flow.d.ts +9 -37
  117. package/dist/crdt/flow.js +260 -238
  118. package/dist/crdt/index.d.ts +26 -85
  119. package/dist/crdt/index.js +21 -47
  120. package/dist/crdt/operations.d.ts +24 -33
  121. package/dist/crdt/operations.js +7 -17
  122. package/dist/crdt/placement/display.d.ts +0 -2
  123. package/dist/crdt/placement/display.js +0 -15
  124. package/dist/crdt/placement/model.d.ts +160 -12
  125. package/dist/crdt/placement/model.js +413 -414
  126. package/dist/crdt/placement/rank.d.ts +1 -5
  127. package/dist/crdt/placement/rank.js +28 -38
  128. package/dist/crdt/protocol.d.ts +4 -5
  129. package/dist/crdt/protocol.js +3 -4
  130. package/dist/crdt/protocols/awareness.d.ts +3 -7
  131. package/dist/crdt/protocols/awareness.js +17 -33
  132. package/dist/crdt/protocols/envelope.d.ts +9 -29
  133. package/dist/crdt/protocols/envelope.js +8 -25
  134. package/dist/crdt/protocols/sync.d.ts +15 -30
  135. package/dist/crdt/protocols/sync.js +27 -89
  136. package/dist/crdt/providers/bound.d.ts +3 -1
  137. package/dist/crdt/providers/bound.js +3 -1
  138. package/dist/crdt/providers/container.d.ts +11 -17
  139. package/dist/crdt/providers/container.js +39 -27
  140. package/dist/crdt/providers/index.d.ts +34 -6
  141. package/dist/crdt/providers/index.js +106 -111
  142. package/dist/crdt/providers/indexeddb.d.ts +2 -12
  143. package/dist/crdt/providers/indexeddb.js +29 -90
  144. package/dist/crdt/providers/room.d.ts +58 -2
  145. package/dist/crdt/providers/room.js +38 -21
  146. package/dist/crdt/providers/websocket.d.ts +1 -1
  147. package/dist/crdt/providers/websocket.js +3 -4
  148. package/dist/crdt/purge.d.ts +2 -2
  149. package/dist/crdt/purge.js +69 -6
  150. package/dist/crdt/rangeDelete.d.ts +4 -42
  151. package/dist/crdt/rangeDelete.js +7 -6
  152. package/dist/crdt/restore.d.ts +21 -1
  153. package/dist/crdt/restore.js +54 -59
  154. package/dist/crdt/schema.d.ts +2 -2
  155. package/dist/crdt/schema.js +4 -5
  156. package/dist/crdt/semantics.d.ts +10 -15
  157. package/dist/crdt/semantics.js +22 -21
  158. package/dist/crdt/storage.d.ts +7 -13
  159. package/dist/crdt/storage.js +10 -24
  160. package/dist/crdt/structs.d.ts +42 -47
  161. package/dist/crdt/structs.js +76 -67
  162. package/dist/crdt/tables.d.ts +9 -0
  163. package/dist/crdt/tables.js +20 -0
  164. package/dist/crdt/text/delete-index.d.ts +0 -13
  165. package/dist/crdt/text/delete-index.js +7 -14
  166. package/dist/crdt/text/deletes.d.ts +9 -22
  167. package/dist/crdt/text/deletes.js +11 -74
  168. package/dist/crdt/text/index/cache.js +3 -3
  169. package/dist/crdt/text/index/fold.d.ts +1 -2
  170. package/dist/crdt/text/index/fold.js +15 -10
  171. package/dist/crdt/text/index/layout.d.ts +0 -10
  172. package/dist/crdt/text/index/layout.js +6 -20
  173. package/dist/crdt/text/index/records.js +2 -1
  174. package/dist/crdt/text/index/report.js +5 -6
  175. package/dist/crdt/text/index/shared.d.ts +11 -4
  176. package/dist/crdt/text/index/shared.js +8 -7
  177. package/dist/crdt/text/index/state.d.ts +3 -33
  178. package/dist/crdt/text/index/state.js +2 -4
  179. package/dist/crdt/text/model.d.ts +82 -2
  180. package/dist/crdt/text/model.js +344 -354
  181. package/dist/crdt/text/rows.d.ts +0 -2
  182. package/dist/crdt/text/rows.js +0 -11
  183. package/dist/crdt/text/runs.d.ts +6 -3
  184. package/dist/crdt/text/runs.js +132 -137
  185. package/dist/crdt/text/streams.d.ts +1 -1
  186. package/dist/crdt/text/streams.js +1 -1
  187. package/dist/crdt/vendor/yjs/API-NOTES.md +25 -8
  188. package/dist/crdt/vendor/yjs/UPSTREAM.md +288 -8
  189. package/dist/crdt/vendor/yjs/dts/global.d.ts +5 -26
  190. package/dist/crdt/vendor/yjs/dts/index.d.ts +10 -15
  191. package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +2 -2
  192. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +14 -219
  193. package/dist/crdt/vendor/yjs/dts/structs/Skip.d.ts +2 -2
  194. package/dist/crdt/vendor/yjs/dts/utils/BlockSet.d.ts +2 -2
  195. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +12 -123
  196. package/dist/crdt/vendor/yjs/dts/utils/ID.d.ts +1 -1
  197. package/dist/crdt/vendor/yjs/dts/utils/RangeCursor.d.ts +37 -58
  198. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +3 -78
  199. package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +2 -81
  200. package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +2 -74
  201. package/dist/crdt/vendor/yjs/dts/utils/encoding-helpers.d.ts +4 -4
  202. package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +6 -18
  203. package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +12 -237
  204. package/dist/crdt/vendor/yjs/dts/utils/positions.d.ts +5 -0
  205. package/dist/crdt/vendor/yjs/dts/utils/redo.d.ts +3 -0
  206. package/dist/crdt/vendor/yjs/dts/utils/transaction-helpers.d.ts +1 -3
  207. package/dist/crdt/vendor/yjs/dts/utils/updates.d.ts +6 -23
  208. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +73 -498
  209. package/dist/crdt/vendor/yjs/global.d.ts +5 -26
  210. package/dist/crdt/vendor/yjs/src/index.js +11 -25
  211. package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +1 -1
  212. package/dist/crdt/vendor/yjs/src/structs/GC.js +1 -1
  213. package/dist/crdt/vendor/yjs/src/structs/Item.js +10 -372
  214. package/dist/crdt/vendor/yjs/src/structs/Skip.js +1 -1
  215. package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +2 -2
  216. package/dist/crdt/vendor/yjs/src/utils/Doc.js +14 -170
  217. package/dist/crdt/vendor/yjs/src/utils/ID.js +1 -1
  218. package/dist/crdt/vendor/yjs/src/utils/RangeCursor.js +61 -128
  219. package/dist/crdt/vendor/yjs/src/utils/StructStore.js +0 -22
  220. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +17 -282
  221. package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +3 -130
  222. package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +2 -132
  223. package/dist/crdt/vendor/yjs/src/utils/encoding-helpers.js +4 -4
  224. package/dist/crdt/vendor/yjs/src/utils/encoding.js +49 -146
  225. package/dist/crdt/vendor/yjs/src/utils/ids.js +23 -900
  226. package/dist/crdt/vendor/yjs/src/utils/positions.js +115 -0
  227. package/dist/crdt/vendor/yjs/src/utils/redo.js +143 -0
  228. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +10 -127
  229. package/dist/crdt/vendor/yjs/src/utils/updates.js +7 -50
  230. package/dist/crdt/vendor/yjs/src/ynode.js +261 -2226
  231. package/dist/{plugins/blockHandles → dnd}/autoScroll.d.ts +2 -1
  232. package/dist/{plugins/blockHandles → dnd}/autoScroll.js +3 -3
  233. package/dist/dnd/pragmatic.d.ts +4 -22
  234. package/dist/dnd/pragmatic.js +7 -26
  235. package/dist/dnd/track.d.ts +14 -0
  236. package/dist/dnd/track.js +55 -0
  237. package/dist/dnd/vendor/pragmatic/UPSTREAM.md +103 -0
  238. package/dist/dnd/vendor/pragmatic/bind-event-listener/LICENSE +21 -0
  239. package/dist/dnd/vendor/pragmatic/bind-event-listener/bind-all.js +46 -0
  240. package/dist/dnd/vendor/pragmatic/bind-event-listener/bind.js +7 -0
  241. package/dist/dnd/vendor/pragmatic/bind-event-listener/index.js +2 -0
  242. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/LICENSE.md +11 -0
  243. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter-native-data-key.js +6 -0
  244. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +57 -0
  245. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +306 -0
  246. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/entry-point/element/adapter.d.ts +8 -0
  247. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/entry-point/element/adapter.js +10 -0
  248. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/entry-point/element/set-custom-native-drag-preview.d.ts +4 -0
  249. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/entry-point/element/set-custom-native-drag-preview.js +6 -0
  250. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/entry-point/types.d.ts +1 -0
  251. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/get-element-from-point-without-honey-pot.js +12 -0
  252. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/honey-pot-data-attribute.js +3 -0
  253. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/is-honey-pot-element.js +4 -0
  254. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +333 -0
  255. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +350 -0
  256. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/ledger/dispatch-consumer-event.js +128 -0
  257. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/ledger/lifecycle-manager.js +340 -0
  258. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/ledger/usage-ledger.js +32 -0
  259. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-adapter.js +59 -0
  260. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +288 -0
  261. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +85 -0
  262. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/combine.js +6 -0
  263. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.d.ts +36 -0
  264. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +153 -0
  265. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/types.d.ts +16 -0
  266. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/once.js +13 -0
  267. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/add-attribute.js +7 -0
  268. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/android.js +10 -0
  269. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/count-events-for-safari.js +104 -0
  270. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-from-another-window.js +16 -0
  271. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-leaving-window-in-safari.js +12 -0
  272. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-leaving-window.js +55 -0
  273. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/detect-broken-drag.js +48 -0
  274. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/get-input.js +14 -0
  275. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/is-firefox.js +13 -0
  276. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/is-safari.js +19 -0
  277. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +3 -0
  278. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/media-types/text-media-type.js +6 -0
  279. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/media-types/url-media-type.js +6 -0
  280. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/popover-reset-styles.js +40 -0
  281. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +16 -0
  282. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/LICENSE.md +11 -0
  283. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/element.d.ts +4 -0
  284. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/element.js +8 -0
  285. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +50 -0
  286. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/data-attributes.js +9 -0
  287. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +117 -0
  288. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/make-api.js +90 -0
  289. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +183 -0
  290. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/axis.js +22 -0
  291. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/can-scroll-on-edge.js +17 -0
  292. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +49 -0
  293. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/edges.js +9 -0
  294. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/engagement-history.js +43 -0
  295. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-over-element-hitbox.js +50 -0
  296. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-percentage-in-range.js +19 -0
  297. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +106 -0
  298. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +3 -0
  299. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-within.js +11 -0
  300. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/scheduler.js +104 -0
  301. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/side.js +6 -0
  302. package/dist/dnd/vendor/pragmatic/raf-schd/LICENSE +21 -0
  303. package/dist/dnd/vendor/pragmatic/raf-schd/raf-schd.esm.js +34 -0
  304. package/dist/edytor.svelte.d.ts +34 -64
  305. package/dist/edytor.svelte.js +118 -180
  306. package/dist/events/beforeInputCommands.d.ts +6 -11
  307. package/dist/events/beforeInputCommands.js +189 -184
  308. package/dist/events/beforeInputDeleteCommands.d.ts +20 -1
  309. package/dist/events/beforeInputDeleteCommands.js +153 -204
  310. package/dist/events/domTextOffset.d.ts +9 -9
  311. package/dist/events/domTextOffset.js +20 -48
  312. package/dist/events/events.utils.d.ts +0 -2
  313. package/dist/events/events.utils.js +0 -4
  314. package/dist/events/nativeInteractiveControl.d.ts +13 -13
  315. package/dist/events/nativeInteractiveControl.js +28 -56
  316. package/dist/events/onBeforeInput.d.ts +6 -1
  317. package/dist/events/onBeforeInput.js +207 -331
  318. package/dist/events/onCopy.js +1 -5
  319. package/dist/events/onDrop.d.ts +7 -1
  320. package/dist/events/onDrop.js +33 -72
  321. package/dist/events/onFocus.d.ts +9 -4
  322. package/dist/events/onFocus.js +92 -72
  323. package/dist/events/onInput.d.ts +8 -4
  324. package/dist/events/onInput.js +34 -147
  325. package/dist/events/onKeyDown.js +55 -122
  326. package/dist/events/onPaste.d.ts +13 -1
  327. package/dist/events/onPaste.js +46 -76
  328. package/dist/index.d.ts +10 -13
  329. package/dist/index.js +3 -3
  330. package/dist/kinds.d.ts +4 -6
  331. package/dist/kinds.js +28 -27
  332. package/dist/labels.d.ts +91 -314
  333. package/dist/labels.js +180 -396
  334. package/dist/plugins/Chrome.svelte +29 -0
  335. package/dist/plugins/Chrome.svelte.d.ts +32 -0
  336. package/dist/plugins/Control.svelte +23 -0
  337. package/dist/plugins/Control.svelte.d.ts +30 -0
  338. package/dist/plugins/anchored.svelte.d.ts +47 -0
  339. package/dist/plugins/anchored.svelte.js +73 -0
  340. package/dist/plugins/arrowMove/arrowMove.d.ts +2 -1
  341. package/dist/plugins/arrowMove/arrowMove.js +9 -5
  342. package/dist/plugins/bands.svelte.d.ts +57 -0
  343. package/dist/plugins/bands.svelte.js +95 -0
  344. package/dist/plugins/blockHandles/BlockHandle.svelte +14 -151
  345. package/dist/plugins/blockHandles/BlockHandle.svelte.d.ts +2 -2
  346. package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +63 -97
  347. package/dist/plugins/blockHandles/BlockHandleController.svelte.js +247 -268
  348. package/dist/plugins/blockHandles/BlockHandles.svelte +46 -29
  349. package/dist/plugins/blockHandles/BlockHandles.svelte.d.ts +2 -2
  350. package/dist/plugins/blockHandles/blockHandlesPlugin.d.ts +29 -23
  351. package/dist/plugins/blockHandles/blockHandlesPlugin.js +130 -162
  352. package/dist/plugins/blockHandles/dragPreview.d.ts +8 -6
  353. package/dist/plugins/blockHandles/dragPreview.js +20 -17
  354. package/dist/plugins/blockHandles/dropIndicator.js +52 -60
  355. package/dist/plugins/blockHandles/geometry.d.ts +46 -1
  356. package/dist/plugins/blockHandles/geometry.js +51 -4
  357. package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +59 -28
  358. package/dist/plugins/blockMenu/BlockMenuController.svelte.js +180 -105
  359. package/dist/plugins/blockMenu/blockMenuPlugin.d.ts +1 -3
  360. package/dist/plugins/blockMenu/blockMenuPlugin.js +12 -29
  361. package/dist/plugins/brand.d.ts +8 -0
  362. package/dist/plugins/brand.js +8 -0
  363. package/dist/plugins/chrome.d.ts +79 -0
  364. package/dist/plugins/chrome.js +87 -0
  365. package/dist/plugins/code/CodeHeader.svelte +12 -58
  366. package/dist/plugins/code/CodeHeader.svelte.d.ts +1 -0
  367. package/dist/plugins/code/CodePlugin.svelte +75 -56
  368. package/dist/plugins/code/CodePlugin.svelte.d.ts +0 -2
  369. package/dist/plugins/code/LanguageMenu.svelte +25 -0
  370. package/dist/plugins/code/LanguageMenu.svelte.d.ts +9 -0
  371. package/dist/plugins/code/code.css +5 -100
  372. package/dist/plugins/code/header.svelte.d.ts +47 -0
  373. package/dist/plugins/code/header.svelte.js +141 -0
  374. package/dist/plugins/code/languageMenu.svelte.d.ts +84 -0
  375. package/dist/plugins/code/languageMenu.svelte.js +199 -0
  376. package/dist/plugins/code/languages.d.ts +21 -10
  377. package/dist/plugins/code/languages.js +39 -126
  378. package/dist/plugins/columns/ColumnResize.svelte +13 -25
  379. package/dist/plugins/columns/ColumnResize.svelte.d.ts +1 -1
  380. package/dist/plugins/columns/ColumnsPlugin.svelte +46 -78
  381. package/dist/plugins/columns/ColumnsPlugin.svelte.d.ts +0 -12
  382. package/dist/plugins/columns/columns.css +7 -26
  383. package/dist/plugins/columns/resize.svelte.d.ts +14 -51
  384. package/dist/plugins/columns/resize.svelte.js +70 -164
  385. package/dist/plugins/comments/CommentsController.svelte.d.ts +1 -4
  386. package/dist/plugins/comments/CommentsController.svelte.js +14 -25
  387. package/dist/plugins/comments/CommentsSidebar.svelte +82 -430
  388. package/dist/plugins/comments/CommentsSidebar.svelte.d.ts +6 -7
  389. package/dist/plugins/comments/commentsPlugin.d.ts +31 -10
  390. package/dist/plugins/comments/commentsPlugin.js +12 -10
  391. package/dist/plugins/equation/EquationEditor.svelte +18 -129
  392. package/dist/plugins/equation/EquationEditor.svelte.d.ts +2 -1
  393. package/dist/plugins/equation/EquationPlugin.svelte +123 -168
  394. package/dist/plugins/equation/EquationPlugin.svelte.d.ts +5 -3
  395. package/dist/plugins/equation/EquationView.svelte +20 -11
  396. package/dist/plugins/equation/equation.svelte.d.ts +74 -36
  397. package/dist/plugins/equation/equation.svelte.js +74 -66
  398. package/dist/plugins/equation/importUrl.d.ts +6 -0
  399. package/dist/plugins/equation/importUrl.js +6 -0
  400. package/dist/plugins/equation/katex.d.ts +8 -0
  401. package/dist/plugins/equation/katex.js +77 -0
  402. package/dist/plugins/find/FindController.svelte.d.ts +8 -13
  403. package/dist/plugins/find/FindController.svelte.js +34 -55
  404. package/dist/plugins/find/findPlugin.d.ts +4 -8
  405. package/dist/plugins/find/findPlugin.js +29 -9
  406. package/dist/plugins/image/ImageChrome.svelte +5 -151
  407. package/dist/plugins/image/ImagePlugin.svelte +49 -117
  408. package/dist/plugins/image/ImagePlugin.svelte.d.ts +1 -24
  409. package/dist/plugins/image/ImageToolbarHost.svelte +37 -0
  410. package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +9 -0
  411. package/dist/plugins/image/controls.svelte.d.ts +6 -57
  412. package/dist/plugins/image/controls.svelte.js +51 -174
  413. package/dist/plugins/image/image.d.ts +0 -5
  414. package/dist/plugins/image/image.js +0 -3
  415. package/dist/plugins/image/toolbar.svelte.d.ts +69 -0
  416. package/dist/plugins/image/toolbar.svelte.js +145 -0
  417. package/dist/plugins/index.d.ts +39 -24
  418. package/dist/plugins/index.js +30 -22
  419. package/dist/plugins/list.svelte.d.ts +128 -0
  420. package/dist/plugins/list.svelte.js +217 -0
  421. package/dist/plugins/markdownShortcuts.d.ts +1 -1
  422. package/dist/plugins/markdownShortcuts.js +6 -5
  423. package/dist/plugins/marquee/MarqueeBox.svelte +15 -0
  424. package/dist/plugins/marquee/MarqueeBox.svelte.d.ts +9 -0
  425. package/dist/plugins/marquee/MarqueeController.svelte.d.ts +46 -0
  426. package/dist/plugins/marquee/MarqueeController.svelte.js +243 -0
  427. package/dist/plugins/marquee/marquee.d.ts +33 -0
  428. package/dist/plugins/marquee/marquee.js +89 -0
  429. package/dist/plugins/marquee/marqueePlugin.d.ts +32 -0
  430. package/dist/plugins/marquee/marqueePlugin.js +68 -0
  431. package/dist/plugins/media/AudioPlugin.svelte +3 -88
  432. package/dist/plugins/media/AudioPlugin.svelte.d.ts +1 -4
  433. package/dist/plugins/media/BookmarkPlugin.svelte +124 -112
  434. package/dist/plugins/media/BookmarkPlugin.svelte.d.ts +20 -7
  435. package/dist/plugins/media/EmbedPlugin.svelte +93 -77
  436. package/dist/plugins/media/EmbedPlugin.svelte.d.ts +21 -9
  437. package/dist/plugins/media/EmptySlot.svelte +44 -0
  438. package/dist/plugins/media/EmptySlot.svelte.d.ts +25 -0
  439. package/dist/plugins/media/FilePlugin.svelte +35 -111
  440. package/dist/plugins/media/FilePlugin.svelte.d.ts +12 -4
  441. package/dist/plugins/media/MediaEmpty.svelte +55 -112
  442. package/dist/plugins/media/MediaEmpty.svelte.d.ts +26 -10
  443. package/dist/plugins/media/SourceBlock.svelte +164 -0
  444. package/dist/plugins/media/SourceBlock.svelte.d.ts +33 -0
  445. package/dist/plugins/media/VideoPlugin.svelte +4 -97
  446. package/dist/plugins/media/VideoPlugin.svelte.d.ts +2 -5
  447. package/dist/plugins/media/empty.svelte.d.ts +69 -0
  448. package/dist/plugins/media/empty.svelte.js +111 -0
  449. package/dist/plugins/media/media.d.ts +93 -30
  450. package/dist/plugins/media/media.js +79 -86
  451. package/dist/plugins/media/urlPaste.svelte.d.ts +9 -32
  452. package/dist/plugins/media/urlPaste.svelte.js +46 -75
  453. package/dist/plugins/mention/MentionPlugin.svelte +47 -124
  454. package/dist/plugins/mention/MentionPlugin.svelte.d.ts +11 -23
  455. package/dist/plugins/page/PageLink.svelte +54 -38
  456. package/dist/plugins/page/PagePlugin.svelte +20 -18
  457. package/dist/plugins/page/PagePlugin.svelte.d.ts +2 -4
  458. package/dist/plugins/page/page.d.ts +35 -9
  459. package/dist/plugins/page/page.js +3 -4
  460. package/dist/plugins/pageLink/PageLinkPlugin.svelte +52 -108
  461. package/dist/plugins/pageLink/PageLinkPlugin.svelte.d.ts +11 -23
  462. package/dist/plugins/planned.d.ts +10 -0
  463. package/dist/plugins/planned.js +12 -0
  464. package/dist/plugins/richtext/CalloutIcon.svelte +25 -0
  465. package/dist/plugins/richtext/CalloutIcon.svelte.d.ts +9 -0
  466. package/dist/plugins/richtext/CalloutIconPicker.svelte +26 -0
  467. package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +9 -0
  468. package/dist/plugins/richtext/EmptyBody.svelte +25 -0
  469. package/dist/plugins/richtext/EmptyBody.svelte.d.ts +8 -0
  470. package/dist/plugins/richtext/RichTextControl.svelte +39 -0
  471. package/dist/plugins/richtext/RichTextControl.svelte.d.ts +8 -0
  472. package/dist/plugins/richtext/RichTextPlugin.svelte +272 -148
  473. package/dist/plugins/richtext/RichTextPlugin.svelte.d.ts +37 -18
  474. package/dist/plugins/richtext/TodoCheckbox.svelte +10 -21
  475. package/dist/plugins/richtext/TodoCheckbox.svelte.d.ts +0 -2
  476. package/dist/plugins/richtext/body.d.ts +9 -0
  477. package/dist/plugins/richtext/body.js +16 -0
  478. package/dist/plugins/richtext/calloutIcons.svelte.d.ts +97 -0
  479. package/dist/plugins/richtext/calloutIcons.svelte.js +214 -0
  480. package/dist/plugins/richtext/callouts.d.ts +15 -0
  481. package/dist/plugins/richtext/callouts.js +23 -0
  482. package/dist/plugins/richtext/controls.svelte.d.ts +110 -0
  483. package/dist/plugins/richtext/controls.svelte.js +126 -0
  484. package/dist/plugins/richtext/richTextOperations.js +37 -28
  485. package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +18 -11
  486. package/dist/plugins/slashMenu/SlashMenuController.svelte.js +93 -35
  487. package/dist/plugins/slashMenu/slashMenuPlugin.d.ts +11 -24
  488. package/dist/plugins/slashMenu/slashMenuPlugin.js +27 -68
  489. package/dist/plugins/suggestions/SuggestionBars.svelte +32 -84
  490. package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +9 -3
  491. package/dist/plugins/suggestions/suggestionsPlugin.d.ts +10 -16
  492. package/dist/plugins/suggestions/suggestionsPlugin.js +9 -24
  493. package/dist/plugins/table/TableChrome.svelte +96 -127
  494. package/dist/plugins/table/TableChrome.svelte.d.ts +5 -1
  495. package/dist/plugins/table/TablePlugin.svelte +116 -171
  496. package/dist/plugins/table/TablePlugin.svelte.d.ts +0 -4
  497. package/dist/plugins/table/chrome.svelte.d.ts +110 -117
  498. package/dist/plugins/table/chrome.svelte.js +643 -289
  499. package/dist/plugins/table/ops.d.ts +47 -0
  500. package/dist/plugins/table/ops.js +233 -0
  501. package/dist/plugins/table/table.css +55 -134
  502. package/dist/plugins/table/table.d.ts +26 -7
  503. package/dist/plugins/table/table.js +23 -49
  504. package/dist/plugins/toc/TableOfContents.svelte +32 -38
  505. package/dist/plugins/toc/TableOfContents.svelte.d.ts +1 -5
  506. package/dist/plugins/toc/TocPlugin.svelte +22 -22
  507. package/dist/plugins/toc/TocPlugin.svelte.d.ts +4 -6
  508. package/dist/plugins/toc/toc.d.ts +41 -10
  509. package/dist/plugins/toc/toc.js +2 -3
  510. package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +29 -66
  511. package/dist/plugins/toolbar/ToolbarController.svelte.js +151 -76
  512. package/dist/plugins/toolbar/toolbarPlugin.d.ts +14 -13
  513. package/dist/plugins/toolbar/toolbarPlugin.js +28 -43
  514. package/dist/plugins/triggers/TriggerController.svelte.d.ts +6 -45
  515. package/dist/plugins/triggers/TriggerController.svelte.js +51 -96
  516. package/dist/plugins/triggers/place.d.ts +0 -9
  517. package/dist/plugins/triggers/place.js +1 -17
  518. package/dist/plugins/triggers/triggers.d.ts +31 -9
  519. package/dist/plugins/triggers/triggers.js +82 -62
  520. package/dist/plugins/uploads.svelte.d.ts +38 -20
  521. package/dist/plugins/uploads.svelte.js +39 -32
  522. package/dist/plugins.d.ts +167 -60
  523. package/dist/selection/domSelection.d.ts +13 -4
  524. package/dist/selection/domSelection.js +110 -183
  525. package/dist/selection/pointer.svelte.d.ts +12 -10
  526. package/dist/selection/pointer.svelte.js +154 -71
  527. package/dist/selection/replaceSelection.d.ts +21 -22
  528. package/dist/selection/replaceSelection.js +60 -69
  529. package/dist/selection/selection.svelte.d.ts +5 -73
  530. package/dist/selection/selection.svelte.js +263 -542
  531. package/dist/selection/selection.utils.d.ts +34 -11
  532. package/dist/selection/selection.utils.js +128 -247
  533. package/dist/selection/visibility.d.ts +11 -8
  534. package/dist/selection/visibility.js +23 -8
  535. package/dist/session/announcer.svelte.d.ts +2 -1
  536. package/dist/session/announcer.svelte.js +7 -3
  537. package/dist/session/attempt.d.ts +33 -39
  538. package/dist/session/attempt.js +61 -53
  539. package/dist/session/bindings.js +108 -139
  540. package/dist/session/commands.d.ts +47 -22
  541. package/dist/session/commands.js +206 -208
  542. package/dist/session/composition.svelte.d.ts +2 -4
  543. package/dist/session/composition.svelte.js +55 -65
  544. package/dist/session/editing/text.d.ts +6 -0
  545. package/dist/session/editing/text.js +17 -3
  546. package/dist/session/handles.d.ts +10 -1
  547. package/dist/session/handles.js +28 -0
  548. package/dist/session/history.d.ts +0 -4
  549. package/dist/session/history.js +15 -21
  550. package/dist/session/inputRules.js +75 -84
  551. package/dist/session/keymap.d.ts +1 -6
  552. package/dist/session/keymap.js +17 -20
  553. package/dist/session/moves.d.ts +10 -10
  554. package/dist/session/moves.js +31 -59
  555. package/dist/session/navigation.d.ts +18 -3
  556. package/dist/session/navigation.js +194 -153
  557. package/dist/session/ports.d.ts +7 -0
  558. package/dist/session/selection.d.ts +4 -3
  559. package/dist/session/selection.js +8 -0
  560. package/dist/session/suggestions.svelte.d.ts +1 -1
  561. package/dist/session/suggestions.svelte.js +64 -47
  562. package/dist/session/virtual.d.ts +3 -3
  563. package/dist/session/virtual.js +18 -40
  564. package/dist/surface/attributes.d.ts +1 -0
  565. package/dist/surface/attributes.js +3 -26
  566. package/dist/surface/cells.d.ts +5 -0
  567. package/dist/surface/cells.js +5 -0
  568. package/dist/surface/lines.d.ts +32 -0
  569. package/dist/surface/lines.js +43 -0
  570. package/dist/surface/observer.svelte.d.ts +3 -20
  571. package/dist/surface/observer.svelte.js +73 -77
  572. package/dist/surface/overlay.d.ts +48 -6
  573. package/dist/surface/overlay.js +133 -23
  574. package/dist/surface/pin.svelte.d.ts +5 -0
  575. package/dist/surface/pin.svelte.js +5 -0
  576. package/dist/surface/projector.svelte.d.ts +4 -6
  577. package/dist/surface/projector.svelte.js +42 -69
  578. package/dist/text/text.svelte.d.ts +14 -40
  579. package/dist/text/text.svelte.js +35 -51
  580. package/dist/text/wordBoundary.d.ts +19 -0
  581. package/dist/text/wordBoundary.js +29 -44
  582. package/dist/themes/notion.css +315 -126
  583. package/dist/utils/json.d.ts +3 -1
  584. package/dist/utils/json.js +52 -51
  585. package/dist/utils/listen.d.ts +8 -0
  586. package/dist/utils/listen.js +19 -0
  587. package/package.json +4 -5
  588. package/dist/block/block.utils.d.ts +0 -340
  589. package/dist/block/block.utils.js +0 -356
  590. package/dist/collaboration/history/HistoryPanel.svelte +0 -525
  591. package/dist/collaboration/history/HistoryPanel.svelte.d.ts +0 -7
  592. package/dist/collaboration/history/panel.d.ts +0 -43
  593. package/dist/collaboration/history/panel.js +0 -3
  594. package/dist/components/SuggestionRanges.svelte +0 -58
  595. package/dist/components/SuggestionRanges.svelte.d.ts +0 -7
  596. package/dist/crdt/doc/table.d.ts +0 -21
  597. package/dist/crdt/doc/table.js +0 -288
  598. package/dist/crdt/migration/generation.d.ts +0 -58
  599. package/dist/crdt/migration/generation.js +0 -70
  600. package/dist/crdt/migration/legacy-schema.d.ts +0 -61
  601. package/dist/crdt/migration/legacy-schema.js +0 -151
  602. package/dist/crdt/migration/migrate.d.ts +0 -56
  603. package/dist/crdt/migration/migrate.js +0 -227
  604. package/dist/crdt/nodes.d.ts +0 -121
  605. package/dist/crdt/nodes.js +0 -56
  606. package/dist/crdt/text/index/checks.d.ts +0 -15
  607. package/dist/crdt/text/index/checks.js +0 -156
  608. package/dist/crdt/vendor/yjs/dts/utils/EventHandler.d.ts +0 -17
  609. package/dist/crdt/vendor/yjs/dts/utils/RelativePosition.d.ts +0 -80
  610. package/dist/crdt/vendor/yjs/dts/utils/UndoManager.d.ts +0 -205
  611. package/dist/crdt/vendor/yjs/dts/utils/YEvent.d.ts +0 -93
  612. package/dist/crdt/vendor/yjs/dts/utils/meta.d.ts +0 -25
  613. package/dist/crdt/vendor/yjs/dts/utils/renderer-helpers.d.ts +0 -98
  614. package/dist/crdt/vendor/yjs/dts/utils/schemas.d.ts +0 -2
  615. package/dist/crdt/vendor/yjs/src/utils/EventHandler.js +0 -75
  616. package/dist/crdt/vendor/yjs/src/utils/RelativePosition.js +0 -301
  617. package/dist/crdt/vendor/yjs/src/utils/UndoManager.js +0 -554
  618. package/dist/crdt/vendor/yjs/src/utils/YEvent.js +0 -158
  619. package/dist/crdt/vendor/yjs/src/utils/meta.js +0 -74
  620. package/dist/crdt/vendor/yjs/src/utils/renderer-helpers.js +0 -186
  621. package/dist/crdt/vendor/yjs/src/utils/schemas.js +0 -3
  622. package/dist/edytor.utils.d.ts +0 -50
  623. package/dist/edytor.utils.js +0 -67
  624. package/dist/plugins/blockMenu/BlockMenu.svelte +0 -447
  625. package/dist/plugins/blockMenu/BlockMenu.svelte.d.ts +0 -9
  626. package/dist/plugins/find/FindBar.svelte +0 -170
  627. package/dist/plugins/find/FindBar.svelte.d.ts +0 -9
  628. package/dist/plugins/find/FindHighlights.svelte +0 -75
  629. package/dist/plugins/find/FindHighlights.svelte.d.ts +0 -9
  630. package/dist/plugins/icons.d.ts +0 -2
  631. package/dist/plugins/icons.js +0 -63
  632. package/dist/plugins/image/ImageEmpty.svelte +0 -116
  633. package/dist/plugins/image/ImageEmpty.svelte.d.ts +0 -12
  634. package/dist/plugins/keepInView.d.ts +0 -4
  635. package/dist/plugins/keepInView.js +0 -6
  636. package/dist/plugins/media/UrlPasteMenu.svelte +0 -83
  637. package/dist/plugins/media/UrlPasteMenu.svelte.d.ts +0 -7
  638. package/dist/plugins/richtext/labels.d.ts +0 -5
  639. package/dist/plugins/richtext/labels.js +0 -3
  640. package/dist/plugins/slashMenu/SlashMenu.svelte +0 -360
  641. package/dist/plugins/slashMenu/SlashMenu.svelte.d.ts +0 -11
  642. package/dist/plugins/toolbar/LinkCard.svelte +0 -118
  643. package/dist/plugins/toolbar/LinkCard.svelte.d.ts +0 -7
  644. package/dist/plugins/toolbar/Toolbar.svelte +0 -400
  645. package/dist/plugins/toolbar/Toolbar.svelte.d.ts +0 -9
  646. package/dist/plugins/triggers/TriggerMenu.svelte +0 -144
  647. package/dist/plugins/triggers/TriggerMenu.svelte.d.ts +0 -7
  648. package/dist/session/props.d.ts +0 -28
  649. package/dist/session/props.js +0 -162
  650. package/dist/text/deltas.d.ts +0 -22
  651. package/dist/text/deltas.js +0 -46
  652. package/dist/text/text.utils.d.ts +0 -45
  653. package/dist/text/text.utils.js +0 -199
@@ -1,92 +1,30 @@
1
1
  import * as binary from 'lib0-v14/binary'
2
- import * as array from 'lib0-v14/array'
3
- import * as delta from 'lib0-v14/delta'
4
2
  import * as error from 'lib0-v14/error'
5
- import * as iterator from 'lib0-v14/iterator'
6
- import * as log from 'lib0-v14/logging'
7
3
  import * as map from 'lib0-v14/map'
8
- import * as set from 'lib0-v14/set'
9
4
  import * as math from 'lib0-v14/math'
10
- import * as object from 'lib0-v14/object'
11
- import * as s from 'lib0-v14/schema'
12
- import * as traits from 'lib0-v14/traits'
13
- import { ObservableV2 } from 'lib0-v14/observable'
14
5
  import {
15
6
  Item,
16
7
  ContentAny,
17
8
  ContentBinary,
18
9
  ContentDeleted,
19
- ContentEmbed,
20
10
  ContentFormat,
21
- ContentJSON,
22
11
  ContentString,
23
12
  ContentType,
24
13
  YXmlFragmentRefID,
25
14
  YXmlElementRefID,
26
- YXmlHookRefID,
27
- ContentDoc,
28
- createContentDocFromDoc
15
+ YXmlHookRefID
29
16
  } from './structs/Item.js'
30
- import { AttributedContent, rendererContentLength, readItemPieces } from './utils/renderer-helpers.js'
31
- import { removeEventHandlerListener, callEventHandlerListeners, addEventHandlerListener, createEventHandler } from './utils/EventHandler.js'
32
17
  import { createID } from './utils/ID.js'
33
- import { createIdSet, iterateStructsByIdSetWithoutSplits } from './utils/ids.js'
34
- import { getItemCleanStart, cleanupFormattingGap } from './utils/transaction-helpers.js'
18
+ import { getItemCleanStart, liveLength, updateCurrentFormats } from './utils/transaction-helpers.js'
35
19
  import { transact } from './utils/Transaction.js'
36
- import { YEvent } from './utils/YEvent.js'
37
- import { $doc } from './utils/schemas.js'
38
- import { foldPaired, formatValue, pairedRole, markClass, MARK_START, MARK_END, SIDE_LEFT } from './utils/marks.js' // YP13
20
+ import { markClass, MARK_START, MARK_END, SIDE_LEFT } from './utils/marks.js' // YP13
39
21
 
40
22
  /**
41
- * @typedef {Object<string,any>|Array<any>|number|null|string|Uint8Array|BigInt|YNode<any>} YValue
23
+ * @typedef {Object<string,any>|Array<any>|number|null|string|Uint8Array|BigInt|YNode} YValue
42
24
  */
43
25
 
44
- /**
45
- * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats
46
- */
47
- export const warnPrematureAccess = () => { log.warn('Invalid access: Add Yjs type to a document before reading data.') }
48
-
49
26
  const maxSearchMarker = 80
50
27
 
51
- /**
52
- * @todo SHOULD NOT RETURN AN OBJECT!
53
- * @param {Array<ContentAttribute<any>>?} attrs
54
- * @param {boolean} deleted - whether the attributed item is deleted
55
- * @return {Attribution|undefined} `undefined` when there is no attribution — under lib0's tri-state
56
- * that means "skip / inherit the builder's attribution context" (NOT `null`, which would clear it).
57
- */
58
- export const createAttributionFromAttributionItems = (attrs, deleted) => {
59
- if (attrs == null) {
60
- return undefined
61
- }
62
- /**
63
- * @type {Attribution}
64
- */
65
- const attribution = {}
66
- if (deleted) {
67
- attribution.delete = []
68
- } else {
69
- attribution.insert = []
70
- }
71
- attrs.forEach(attr => {
72
- switch (attr.name) {
73
- // eslint-disable-next-line no-fallthrough
74
- case 'insert':
75
- case 'delete': {
76
- // needs to be non-ambiguous: don't add existing attr if it doesn't match the actual status
77
- attribution[attr.name]?.push(attr.val)
78
- break
79
- }
80
- default: {
81
- if (attr.name[0] !== '_') {
82
- /** @type {any} */ (attribution)[attr.name] = attr.val
83
- }
84
- }
85
- }
86
- })
87
- return attribution
88
- }
89
-
90
28
  /**
91
29
  * A unique timestamp that identifies each marker.
92
30
  *
@@ -102,14 +40,12 @@ export class ItemTextListPosition {
102
40
  * @param {Item|null} right
103
41
  * @param {number} index
104
42
  * @param {Map<string,any>} currentFormats
105
- * @param {AbstractRenderer?} renderer
106
43
  */
107
- constructor (left, right, index, currentFormats, renderer) {
44
+ constructor (left, right, index, currentFormats) {
108
45
  this.left = left
109
46
  this.right = right
110
47
  this.index = index
111
48
  this.currentFormats = currentFormats
112
- this.renderer = renderer
113
49
  }
114
50
 
115
51
  /**
@@ -126,7 +62,7 @@ export class ItemTextListPosition {
126
62
  }
127
63
  break
128
64
  default:
129
- this.index += rendererContentLength(this.renderer, this.right)
65
+ this.index += liveLength(this.right)
130
66
  break
131
67
  }
132
68
  this.left = this.right
@@ -134,213 +70,42 @@ export class ItemTextListPosition {
134
70
  }
135
71
 
136
72
  /**
73
+ * Move past `length` live units, splitting the item the last one ends in,
74
+ * and past the deleted items around them (YP19: the positioning of every
75
+ * write; mark items are folded, never rewritten).
76
+ *
137
77
  * @param {Transaction} transaction
138
- * @param {YNode} parent
139
78
  * @param {number} length
140
- * @param {Object<string,any>} formats
141
- *
142
- * @function
143
79
  */
144
- formatText (transaction, parent, length, formats) {
145
- minimizeFormatChanges(this, formats)
146
- const negatedFormats = insertFormats(transaction, parent, this, formats)
147
- // iterate until first non-format or null is found
148
- // delete all formats with formats[format.key] != null
149
- // also check the formats after the first non-format as we do not want to insert redundant negated formats there
150
- // eslint-disable-next-line no-labels
151
- iterationLoop: while (
152
- this.right !== null &&
153
- (length > 0 ||
154
- (
155
- negatedFormats.size > 0 &&
156
- ((this.right.deleted && rendererContentLength(this.renderer, this.right) === 0) || this.right.content.constructor === ContentFormat)
157
- )
158
- )
159
- ) {
160
- switch (this.right.content.constructor) {
161
- case ContentFormat: {
162
- if (!this.right.deleted && pairedRole(/** @type {ContentFormat} */ (this.right.content)) >= 0) {
163
- // YP13: a paired mark is folded, never rewritten by a plain format
164
- updateCurrentFormats(this.currentFormats, /** @type {ContentFormat} */ (this.right.content))
165
- } else if (!this.right.deleted) {
166
- const { key, value } = /** @type {ContentFormat} */ (this.right.content)
167
- const attr = formats[key]
168
- if (attr !== undefined) {
169
- if (equalFormats(attr, value)) {
170
- negatedFormats.delete(key)
171
- } else {
172
- if (length === 0) {
173
- // no need to further extend negatedFormats
174
- // eslint-disable-next-line no-labels
175
- break iterationLoop
176
- }
177
- negatedFormats.set(key, value)
178
- }
179
- this.right.delete(transaction)
180
- } else {
181
- this.currentFormats.set(key, value)
182
- }
183
- }
184
- break
185
- }
186
- default: {
187
- const item = this.right
188
- const rightLen = rendererContentLength(this.renderer, item)
189
- if (length < rightLen) {
190
- if (this.renderer !== null && this.renderer.hasItem(item)) {
191
- /**
192
- * @type {Array<AttributedContent<any>>}
193
- */
194
- const contents = []
195
- this.renderer.readContent(contents, item.id.client, item.id.clock, item.deleted, item.content, 0)
196
- let i = 0
197
- for (; i < contents.length && length > 0; i++) {
198
- const c = contents[i]
199
- if ((!c.deleted || c.attrs != null) && c.content.isCountable()) {
200
- length -= c.content.getLength()
201
- }
202
- }
203
- if (length < 0 || (length === 0 && i !== contents.length)) {
204
- const c = contents[--i]
205
- getItemCleanStart(transaction, createID(item.id.client, c.clock + c.content.getLength() + length))
206
- }
207
- } else {
208
- // plain content: split directly at the offset
209
- getItemCleanStart(transaction, createID(item.id.client, item.id.clock + length))
210
- length = 0
211
- }
212
- } else {
213
- length -= rightLen
214
- }
215
- break
216
- }
80
+ advance (transaction, length) {
81
+ while (this.right !== null && this.right.deleted) this.forward()
82
+ while (this.right !== null && length > 0) {
83
+ const item = this.right
84
+ const len = liveLength(item)
85
+ if (length < len) {
86
+ getItemCleanStart(transaction, createID(item.id.client, item.id.clock + length))
87
+ length = 0
88
+ } else {
89
+ length -= len
217
90
  }
218
91
  this.forward()
219
92
  }
220
93
  if (length > 0) {
221
94
  throw new Error('Exceeded content range')
222
95
  }
223
- insertNegatedFormats(transaction, parent, this, negatedFormats)
224
- }
225
- }
226
-
227
- /**
228
- * Negate applied formats
229
- *
230
- * @param {Transaction} transaction
231
- * @param {YNode} parent
232
- * @param {ItemTextListPosition} currPos
233
- * @param {Map<string,any>} negatedFormats
234
- *
235
- * @private
236
- * @function
237
- */
238
- const insertNegatedFormats = (transaction, parent, currPos, negatedFormats) => {
239
- // check if we really need to remove formats
240
- while (
241
- currPos.right !== null && (
242
- (currPos.right.deleted && rendererContentLength(currPos.renderer, currPos.right) === 0) || (
243
- currPos.right.content.constructor === ContentFormat &&
244
- equalFormats(negatedFormats.get(/** @type {ContentFormat} */ (currPos.right.content).key), /** @type {ContentFormat} */ (currPos.right.content).value)
245
- )
246
- )
247
- ) {
248
- if (!currPos.right.deleted) {
249
- negatedFormats.delete(/** @type {ContentFormat} */ (currPos.right.content).key)
250
- }
251
- currPos.forward()
252
- }
253
- const doc = transaction.doc
254
- const ownClientId = doc.clientID
255
- negatedFormats.forEach((val, key) => {
256
- const left = currPos.left
257
- const right = currPos.right
258
- const nextFormat = new Item(createID(ownClientId, doc.store.getClock(ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val))
259
- nextFormat.integrate(transaction, 0)
260
- currPos.right = nextFormat
261
- currPos.forward()
262
- updateMarkerFormats(parent, currPos, /** @type {ContentFormat} */ (nextFormat.content))
263
- })
264
- }
265
-
266
- /**
267
- * @param {Map<string,any>} currentFormats
268
- * @param {ContentFormat} format
269
- *
270
- * @private
271
- * @function
272
- */
273
- const updateCurrentFormats = (currentFormats, format) => {
274
- if (foldPaired(currentFormats, format)) return // YP13
275
- const { key, value } = format
276
- if (value === null) {
277
- currentFormats.delete(key)
278
- } else {
279
- currentFormats.set(key, value)
280
- }
281
- }
282
-
283
- /**
284
- * @param {ItemTextListPosition} currPos
285
- * @param {Object<string,any>} formats
286
- *
287
- * @private
288
- * @function
289
- */
290
- const minimizeFormatChanges = (currPos, formats) => {
291
- // go right while formats[right.key] === right.value (or right is deleted)
292
- while (true) {
293
- if (currPos.right === null) {
294
- break
295
- } else if (currPos.right.deleted ? (rendererContentLength(currPos.renderer, currPos.right) === 0) : (!currPos.right.deleted && currPos.right.content.constructor === ContentFormat && equalFormats(formats[(/** @type {ContentFormat} */ (currPos.right.content)).key] ?? null, /** @type {ContentFormat} */ (currPos.right.content).value))) {
296
- //
297
- } else {
298
- break
299
- }
300
- currPos.forward()
301
- }
302
- }
303
-
304
- /**
305
- * @param {Transaction} transaction
306
- * @param {YNode} parent
307
- * @param {ItemTextListPosition} currPos
308
- * @param {Object<string,any>} formats
309
- * @return {Map<string,any>}
310
- *
311
- * @private
312
- * @function
313
- **/
314
- const insertFormats = (transaction, parent, currPos, formats) => {
315
- const doc = transaction.doc
316
- const ownClientId = doc.clientID
317
- const negatedFormats = new Map()
318
- // insert format-start items
319
- for (const key in formats) {
320
- const val = formats[key]
321
- const currentVal = currPos.currentFormats.get(key) ?? null
322
- if (!equalFormats(currentVal, val)) {
323
- // save negated format (set null if currentVal undefined)
324
- negatedFormats.set(key, currentVal)
325
- const { left, right } = currPos
326
- currPos.right = new Item(createID(ownClientId, doc.store.getClock(ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val))
327
- currPos.right.integrate(transaction, 0)
328
- currPos.forward()
329
- updateMarkerFormats(parent, currPos, /** @type {ContentFormat} */ ((/** @type {Item} */ (currPos.left)).content))
330
- }
96
+ while (this.right !== null && this.right.deleted) this.forward()
331
97
  }
332
- return negatedFormats
333
98
  }
334
99
 
335
100
  // YP13 begin (edytor fork: paired marks — see UPSTREAM.md YP13)
336
101
  /**
337
- * The contents an array insert writes (as `insertContentHelper` splits it).
102
+ * The contents an insert writes: a string, or an array split into its nodes and its runs of JSON values.
338
103
  *
339
- * @param {Array<any>} insert
104
+ * @param {Array<any>|string} insert
340
105
  * @return {Array<import('./structs/Item.js').AbstractContent>}
341
106
  */
342
107
  const contentsOf = insert => {
343
- insert = insert.map(ins => delta.$deltaAny.check(ins) ? YNode.from(ins) : ins)
108
+ if (typeof insert === 'string') return [new ContentString(insert)]
344
109
  /** @type {Array<import('./structs/Item.js').AbstractContent>} */
345
110
  const out = []
346
111
  for (let i = 0; i < insert.length;) {
@@ -348,12 +113,9 @@ const contentsOf = insert => {
348
113
  if (first instanceof YNode) {
349
114
  out.push(new ContentType(first))
350
115
  i++
351
- } else if ($doc.check(first)) {
352
- out.push(createContentDocFromDoc(first))
353
- i++
354
116
  } else {
355
117
  let j = i + 1
356
- for (; j < insert.length && !(insert[j] instanceof YNode || $doc.check(insert[j])); j++) { /* nop */ }
118
+ for (; j < insert.length && !(insert[j] instanceof YNode); j++) { /* nop */ }
357
119
  out.push(new ContentAny((i === 0 && j === insert.length) ? insert : insert.slice(i, j)))
358
120
  i = j
359
121
  }
@@ -494,77 +256,13 @@ const markHelper = (transaction, parent, index, length, mark, value, startSide,
494
256
  }
495
257
  // YP13 end
496
258
 
497
- /**
498
- * @param {Transaction} transaction
499
- * @param {YNode} parent
500
- * @param {ItemTextListPosition} currPos
501
- * @param {import('./structs/Item.js').AbstractContent} content
502
- * @param {Object<string,any>} formats
503
- *
504
- * @private
505
- * @function
506
- **/
507
- export const insertContent = (transaction, parent, currPos, content, formats) => {
508
- currPos.currentFormats.forEach((_val, key) => {
509
- if (formats[key] === undefined) {
510
- formats[key] = null
511
- }
512
- })
513
- const doc = transaction.doc
514
- const ownClientId = doc.clientID
515
- minimizeFormatChanges(currPos, formats)
516
- const negatedFormats = insertFormats(transaction, parent, currPos, formats)
517
- let { left, right, index } = currPos
518
- if (parent._searchMarker) {
519
- updateMarkerChanges(parent._searchMarker, currPos.index, content.getLength())
520
- }
521
- right = new Item(createID(ownClientId, doc.store.getClock(ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, content)
522
- right.integrate(transaction, 0)
523
- currPos.right = right
524
- currPos.index = index
525
- currPos.forward()
526
- insertNegatedFormats(transaction, parent, currPos, negatedFormats)
527
- }
528
-
529
- /**
530
- * @param {Transaction} transaction
531
- * @param {YNode} parent
532
- * @param {ItemTextListPosition} currPos
533
- * @param {Array<any>|string} insert
534
- * @param {Object<string,any>} formats
535
- */
536
- export const insertContentHelper = (transaction, parent, currPos, insert, formats) => {
537
- if (s.$string.check(insert)) {
538
- insertContent(transaction, parent, currPos, new ContentString(insert), formats)
539
- } else {
540
- insert = insert.map(ins => delta.$deltaAny.check(ins) ? YNode.from(ins) : ins)
541
- for (let i = 0; i < insert.length;) {
542
- const first = insert[i]
543
- if (first instanceof YNode) {
544
- insertContent(transaction, parent, currPos, new ContentType(first), formats)
545
- i++
546
- } else if ($doc.check(first)) {
547
- insertContent(transaction, parent, currPos, createContentDocFromDoc(first), formats)
548
- i++
549
- } else {
550
- // insert "any" content
551
- // compute slice len
552
- let j = i + 1
553
- for (; j < insert.length && !(insert[j] instanceof YNode || $doc.check(insert[j])); j++) { /* nop */ }
554
- insertContent(transaction, parent, currPos, new ContentAny((i === 0 && j === insert.length) ? insert : insert.slice(i, j)), formats)
555
- i = j
556
- }
557
- }
558
- }
559
- }
560
-
561
259
  // YP7 begin (edytor fork: insertAtGapEnd — see UPSTREAM.md YP7)
562
260
  /**
563
261
  * Insert `content` at the END of the gap at live index `index`: walk `index`
564
262
  * countable live units, then pass every deleted item and every format item
565
263
  * before the next live content item, and integrate `content` there. The
566
264
  * content takes exactly the formats in effect at that point — no format item
567
- * is added. The walk ignores any renderer: indices are live-content space.
265
+ * is added. Indices are live-content space.
568
266
  *
569
267
  * @param {Transaction} transaction
570
268
  * @param {YNode} parent
@@ -572,7 +270,7 @@ export const insertContentHelper = (transaction, parent, currPos, insert, format
572
270
  * @param {import('./structs/Item.js').AbstractContent} content
573
271
  */
574
272
  export const insertAtGapEndHelper = (transaction, parent, index, content) => {
575
- const currPos = new ItemTextListPosition(null, parent._start, 0, new Map(), null)
273
+ const currPos = new ItemTextListPosition(null, parent._start, 0, new Map())
576
274
  let n = index
577
275
  while (n > 0 && currPos.right !== null) {
578
276
  const item = currPos.right
@@ -605,8 +303,6 @@ export const insertAtGapEndHelper = (transaction, parent, index, content) => {
605
303
  */
606
304
  export const deleteText = (transaction, currPos, length) => {
607
305
  const startLength = length
608
- const startFormats = map.copy(currPos.currentFormats)
609
- const start = currPos.right
610
306
  while (length > 0 && currPos.right !== null) {
611
307
  const item = currPos.right
612
308
  if (!item.deleted && item.countable) {
@@ -615,52 +311,10 @@ export const deleteText = (transaction, currPos, length) => {
615
311
  }
616
312
  length -= item.length
617
313
  item.delete(transaction)
618
- } else if (currPos.renderer !== null && currPos.renderer.hasItem(item)) {
619
- /**
620
- * @type {Array<AttributedContent<any>>}
621
- */
622
- const contents = []
623
- currPos.renderer.readContent(contents, item.id.client, item.id.clock, true, item.content, 0)
624
- let splitClock = -1
625
- for (let i = 0; i < contents.length; i++) {
626
- const c = contents[i]
627
- if (c.content.isCountable() && c.attrs != null) {
628
- if (length === 0) {
629
- // the delete is exhausted but this item renders more content — split so the cursor
630
- // advances only past the consumed part (mirrors formatText's renderer branch);
631
- // otherwise every following op of the same delta targets too far right
632
- splitClock = c.clock
633
- break
634
- }
635
- // deleting already deleted content. store that information in a meta property, but do
636
- // nothing
637
- const pieceLen = c.content.getLength()
638
- const contentLen = math.min(pieceLen, length)
639
- map.setIfUndefined(transaction.meta, 'attributedDeletes', createIdSet).add(item.id.client, c.clock, contentLen)
640
- length -= contentLen
641
- if (contentLen < pieceLen) {
642
- splitClock = c.clock + contentLen
643
- break
644
- }
645
- }
646
- }
647
- if (splitClock >= 0) {
648
- getItemCleanStart(transaction, createID(item.id.client, splitClock))
649
- } else {
650
- const lastContent = contents.length > 0 ? contents[contents.length - 1] : null
651
- const nextItemClock = item.id.clock + item.length
652
- const nextContentClock = lastContent != null ? lastContent.clock + lastContent.content.getLength() : nextItemClock
653
- if (nextContentClock < nextItemClock) {
654
- getItemCleanStart(transaction, createID(item.id.client, nextContentClock))
655
- }
656
- }
657
314
  }
658
315
  currPos.forward()
659
316
  }
660
- if (start) {
661
- cleanupFormattingGap(transaction, start, currPos.right, startFormats, currPos.currentFormats)
662
- }
663
- const parent = /** @type {YNode<any>} */ (/** @type {Item} */ (currPos.left || currPos.right).parent)
317
+ const parent = /** @type {YNode} */ (/** @type {Item} */ (currPos.left || currPos.right).parent)
664
318
  if (parent._searchMarker) {
665
319
  updateMarkerChanges(parent._searchMarker, currPos.index, -startLength + length)
666
320
  }
@@ -711,101 +365,6 @@ export class ArraySearchMarker {
711
365
  */
712
366
  const refreshMarkerTimestamp = marker => { marker.timestamp = globalSearchMarkerTimestamp++ }
713
367
 
714
- /**
715
- * After a format marker item is integrated at `currPos` (it is `currPos.left`;
716
- * `currPos.right` is the item right of it), fold it into the format snapshot of
717
- * every search marker positioned at-or-right of the insertion — exactly what a
718
- * cursor passing the marker would do.
719
- *
720
- * Positions compare in *list order*, not just index space: markers anchored
721
- * at items sharing `currPos.index` but sitting left of the insertion (a
722
- * same-index run of non-countable/deleted items ending at `currPos.left`)
723
- * must NOT observe the new format — index equality alone is ambiguous there.
724
- *
725
- * Same-key boundary (R1 repair): a marker's `formats` snapshot is the fold of
726
- * the live format items left of its anchor. The new item changes that fold
727
- * only while it is the LAST live item setting its key before the anchor — a
728
- * later live format item that sets the same key again shadows it, so the
729
- * snapshot's value for that key still comes from that later marker and must
730
- * not be overwritten. The fold therefore walks right from the insertion and
731
- * stops at the first live same-key `ContentFormat`: markers anchored
732
- * at-or-before it are updated, everything past it keeps its (still valid)
733
- * snapshot.
734
- *
735
- * Candidates whose anchor the walk never reaches (stale record — anchor
736
- * unlinked/merged, or index lying about the real position) are invalidated
737
- * (`formats = null`) instead of trusting a position that can't be verified.
738
- *
739
- * @param {YNode<any>} parent
740
- * @param {ItemTextListPosition} currPos
741
- * @param {ContentFormat} format
742
- */
743
- const updateMarkerFormats = (parent, currPos, format) => {
744
- const ms = parent._searchMarker
745
- if (ms === null) return
746
- /**
747
- * Anchors of the same-index run ending at `currPos.left`: deleted or
748
- * non-countable items sharing `currPos.index` but sitting left of the
749
- * insertion (the run always starts with the new format item itself, which
750
- * is non-countable). Built lazily — only needed when a marker shares the
751
- * insertion index.
752
- *
753
- * @type {Set<Item>?}
754
- */
755
- let leftRun = null
756
- /**
757
- * Snapshots at-or-right of the insertion — candidates for the fold.
758
- *
759
- * @type {Array<ArraySearchMarker>?}
760
- */
761
- let cands = null
762
- let maxIndex = -1
763
- for (let i = 0; i < ms.length; i++) {
764
- const m = ms[i]
765
- if (m.formats === null || m.index < currPos.index) continue
766
- if (m.index === currPos.index) {
767
- if (leftRun === null) {
768
- leftRun = new Set()
769
- for (let p = currPos.left; p !== null && (p.deleted || !p.countable); p = p.left) {
770
- leftRun.add(p)
771
- }
772
- }
773
- // fold only if m.p is at-or-right of currPos.right — i.e. not part of
774
- // the same-index run terminating at currPos.left
775
- if (leftRun.has(m.p)) continue
776
- }
777
- ;(cands ?? (cands = [])).push(m)
778
- if (m.index > maxIndex) maxIndex = m.index
779
- }
780
- if (cands === null) return
781
- let pindex = currPos.index
782
- for (let p = currPos.right; p !== null; p = p.right) {
783
- if (p.marker) {
784
- for (let i = cands.length - 1; i >= 0; i--) {
785
- if (cands[i].p === p) {
786
- updateCurrentFormats(/** @type {Map<string,any>} */ (cands[i].formats), format)
787
- cands.splice(i, 1)
788
- }
789
- }
790
- if (cands.length === 0) return
791
- }
792
- if (!p.deleted && p.content.constructor === ContentFormat && /** @type {ContentFormat} */ (p.content).key === format.key) {
793
- // Same-key boundary: snapshots past this item still draw this key from
794
- // it — the new marker's reach ends here. (Markers anchored ON it were
795
- // already updated: their snapshot is the state at their left edge,
796
- // which precedes the item's own effect.)
797
- return
798
- }
799
- if (!p.deleted && p.countable) {
800
- pindex += p.length
801
- if (pindex > maxIndex) break // every remaining anchor lies behind us — stale records
802
- }
803
- }
804
- // Candidates never reached by the walk can't be verified at-or-right of the
805
- // insertion — drop their snapshots rather than risk a stale read.
806
- for (let i = 0; i < cands.length; i++) cands[i].formats = null
807
- }
808
-
809
368
  /**
810
369
  * This is rather complex so this function is the only thing that should overwrite a marker
811
370
  *
@@ -850,10 +409,10 @@ const markPosition = (searchMarker, p, index) => {
850
409
  * of `currPos.right`. Never mid-mutation: in-flight `currentFormats` is
851
410
  * transient.
852
411
  *
853
- * @param {YNode<any>} parent
412
+ * @param {YNode} parent
854
413
  * @param {ItemTextListPosition} currPos
855
414
  */
856
- const plantMarker = (parent, currPos) => {
415
+ export const plantMarker = (parent, currPos) => {
857
416
  const ms = parent._searchMarker
858
417
  if (ms === null) return
859
418
  let p = currPos.right
@@ -892,9 +451,9 @@ const plantMarker = (parent, currPos) => {
892
451
  *
893
452
  * The marker's `index` is `p`'s left edge in the raw countable space — the
894
453
  * same space every marker consumer records — so callers must only plant
895
- * while walking the generic (`renderer === null`) interpretation.
454
+ * while walking the live (countable, non-deleted) interpretation.
896
455
  *
897
- * @param {YNode<any>} parent
456
+ * @param {YNode} parent
898
457
  * @param {Item} p - item the marker anchors on (the marker's `index` is its left edge)
899
458
  * @param {number} index
900
459
  * @param {Map<string,any>} formats - format state at `p`'s left edge (copied)
@@ -1038,61 +597,162 @@ export const updateMarkerChanges = (searchMarker, index, len) => {
1038
597
  }
1039
598
  }
1040
599
 
600
+ // YP16 begin (edytor fork: writes without the delta layer — see UPSTREAM.md YP16)
1041
601
  /**
1042
- * Call event listeners with an event. This will also add an event to all
1043
- * parents (for `.observeDeep` handlers).
602
+ * What a detached node was given, replayed when it integrates: its content
603
+ * (adjacent strings, and adjacent arrays, merged) and its attrs in
604
+ * first-write order (`null`: removed).
1044
605
  *
1045
- * @param {YNode} type
1046
- * @param {Transaction} transaction
1047
- * @param {YEvent<any>} event
606
+ * @typedef {{ runs: Array<string|Array<any>>, attrs: Object<string,{ value: any }|null> }} Prelim
607
+ */
608
+
609
+ /**
610
+ * @param {YNode} node
611
+ * @return {Prelim}
612
+ */
613
+ const prelimOf = node => node._prelim ?? (node._prelim = { runs: [], attrs: Object.create(null) })
614
+
615
+ /**
616
+ * Split `runs` so that a run starts at `index`; returns that run's position.
617
+ *
618
+ * @param {Array<string|Array<any>>} runs
619
+ * @param {number} index
620
+ * @return {number}
1048
621
  */
1049
- export const callTypeObservers = (type, transaction, event) => {
1050
- const changedType = type
1051
- const changedParentTypes = transaction.changedParentTypes
1052
- // track the event unconditionally — also for deleted types: the walk carries changes inside a
1053
- // deleted type (e.g. a suggestion-deleted tombstone that a custom renderer still renders) up to
1054
- // the deep-event / 'delta' / cache consumers on live ancestors.
1055
- while (true) {
1056
- // @ts-ignore
1057
- map.setIfUndefined(changedParentTypes, type, () => []).push(event)
1058
- if (type._item === null) {
1059
- break
622
+ const splitRuns = (runs, index) => {
623
+ let i = 0
624
+ for (; i < runs.length && index > 0; i++) {
625
+ const run = runs[i]
626
+ if (index < run.length) {
627
+ runs.splice(i, 1, run.slice(0, index), run.slice(index))
628
+ return i + 1
1060
629
  }
1061
- type = /** @type {YNode} */ (type._item.parent)
630
+ index -= run.length
1062
631
  }
1063
- // a deleted type's own observers stay silent (deleted content is invisible) — unless a
1064
- // renderer is attached to it, which may still render the type (mirrors the fire-time rule for
1065
- // `changedParentTypes` targets in `cleanupTransactions`).
1066
- if (changedType._item === null || !changedType._item.deleted || changedType._renderer !== null) {
1067
- callEventHandlerListeners(/** @type {any} */ (changedType._eH), event, transaction)
632
+ if (index > 0) throw new Error('Exceeded content range')
633
+ return i
634
+ }
635
+
636
+ /**
637
+ * Drop empty runs and merge adjacent runs of one kind.
638
+ *
639
+ * @param {Array<string|Array<any>>} runs
640
+ */
641
+ const mergeRuns = runs => {
642
+ for (let i = runs.length - 1; i >= 0; i--) {
643
+ if (runs[i].length === 0) runs.splice(i, 1)
644
+ }
645
+ for (let i = runs.length - 1; i > 0; i--) {
646
+ const a = runs[i - 1]
647
+ const b = runs[i]
648
+ if (typeof a === typeof b) {
649
+ runs.splice(i - 1, 2, typeof a === 'string' ? a + b : a.concat(b))
650
+ }
1068
651
  }
1069
652
  }
1070
653
 
1071
654
  /**
1072
- * Abstract Yjs Type class.
655
+ * A cursor at live index `index` of `node` — the positioning step of a delete. A distant index
656
+ * seeds the cursor from a format-aware search marker (UPSTREAM.md YP4): a marker at-or-left of the
657
+ * target carrying a formats snapshot starts the walk there, provided its anchor is the first item
658
+ * at its index (the walk from `_start` stops on the first unprocessed item at the target, format
659
+ * markers and deleted items included).
1073
660
  *
1074
- * A `YNode` is a {@link https://github.com/dmonad/lib0 lib0} `RDT` ("replicated data type", see
1075
- * `lib0-v14/delta/rdt.js`): it emits a `'delta'` event whenever its state changes (carrying the change
1076
- * and the origin of the transaction that caused it), accepts foreign
1077
- * changes via {@link YNode#applyDelta}, exposes its delta {@link YNode#$delta schema}, and can be
1078
- * torn down via {@link YNode#destroy}. This lets a `YNode` be `bind()`-ed to any other RDT (another
1079
- * `YNode`, an in-memory delta, a DOM subtree, …). The legacy {@link YNode#observe `observe`} /
1080
- * {@link YNode#observeDeep `observeDeep`} `YEvent` API continues to work alongside the `'delta'`
1081
- * channel.
661
+ * @param {Transaction} transaction
662
+ * @param {YNode} node
663
+ * @param {number} index
664
+ * @return {ItemTextListPosition}
665
+ */
666
+ export const cursorAt = (transaction, node, index) => {
667
+ const currPos = new ItemTextListPosition(null, node._start, 0, new Map())
668
+ if (!(index > 0)) return currPos
669
+ let adjust = 0
670
+ const ms = node._searchMarker
671
+ if (ms !== null) {
672
+ // A marker anchored right of the target can still seed when the gap is crossed by stepping
673
+ // left over countable/non-deleted items only — format state does not change across pure
674
+ // content (a format marker or tombstone in the gap stops the walk and rejects the marker).
675
+ // Anchor linkage is verified because records can outlive their items.
676
+ let best = null
677
+ /** @type {Item|null} */
678
+ let bestP = null
679
+ let bestIndex = 0
680
+ for (let i = 0; i < ms.length; i++) {
681
+ const m = ms[i]
682
+ if (m.formats === null || m.index <= 0) continue
683
+ let p = m.p
684
+ let idx = m.index
685
+ let steps = 0
686
+ while (idx > index && p.left !== null && !p.left.deleted && p.left.countable && steps < 64) {
687
+ p = p.left
688
+ idx -= p.length
689
+ steps++
690
+ }
691
+ if (
692
+ idx <= index && (best === null || idx > bestIndex) &&
693
+ (p.left === null ? node._start === p : p.left.right === p) &&
694
+ (p.right === null || p.right.left === p) &&
695
+ (p.left === null || (!p.left.deleted && p.left.countable))
696
+ ) {
697
+ best = m
698
+ bestP = p
699
+ bestIndex = idx
700
+ }
701
+ }
702
+ if (best !== null) {
703
+ currPos.left = /** @type {Item} */ (bestP).left
704
+ currPos.right = bestP
705
+ currPos.index = bestIndex
706
+ currPos.currentFormats = map.copy(/** @type {Map<string,any>} */ (best.formats))
707
+ adjust = bestIndex
708
+ }
709
+ }
710
+ currPos.advance(transaction, index - adjust)
711
+ // quiescent point at a positioning boundary — a later write near it seeds from this marker
712
+ plantMarker(node, currPos)
713
+ return currPos
714
+ }
715
+
716
+ /**
717
+ * Write the content and attrs a detached node was given: the content runs in order, then the
718
+ * attrs.
1082
719
  *
1083
- * @template {delta.DeltaConf} [DConf=any]
1084
- * @extends {ObservableV2<{ delta: (delta: delta.Delta<DConf>, origin: any) => void, destroy: (type: YNode<DConf>) => void }>}
720
+ * @param {Transaction} transaction
721
+ * @param {YNode} node
722
+ * @param {Prelim} prelim
723
+ */
724
+ const writePrelim = (transaction, node, prelim) => {
725
+ let index = 0
726
+ for (const run of prelim.runs) {
727
+ for (const part of contentsOf(run)) {
728
+ insertInGapHelper(transaction, node, index, part)
729
+ index += part.getLength()
730
+ }
731
+ }
732
+ for (const key in prelim.attrs) {
733
+ const attr = prelim.attrs[key]
734
+ if (attr === null) {
735
+ nodeMapDelete(transaction, node, key)
736
+ } else {
737
+ nodeMapSet(transaction, node, key, attr.value)
738
+ }
739
+ }
740
+ }
741
+ // YP16 end
742
+
743
+ /**
744
+ * The engine's node: a sequence (text, elements, nested nodes) and a map of attributes. YP19: the
745
+ * reads and writes edytor calls — no observers, no plain formats, no JSON or string renderings.
1085
746
  */
1086
- export class YNode extends ObservableV2 {
747
+ export class YNode {
1087
748
  /**
1088
- * @param {delta.DeltaConfGetName<DConf>?} name
749
+ * @param {string?} name
1089
750
  */
1090
751
  constructor (name = null) {
1091
- super()
1092
752
  /**
1093
- * @type {delta.DeltaConfGetName<DConf>}
753
+ * @type {string|null}
1094
754
  */
1095
- this.name = /** @type {delta.DeltaConfGetName<DConf>} */ (name)
755
+ this.name = name
1096
756
  /**
1097
757
  * @type {Item|null}
1098
758
  */
@@ -1110,239 +770,45 @@ export class YNode extends ObservableV2 {
1110
770
  */
1111
771
  this.doc = null
1112
772
  this._length = 0
1113
- /**
1114
- * Event handlers
1115
- * @type {EventHandler<YEvent<DeltaToYNode<DConf>>,Transaction>}
1116
- */
1117
- this._eH = createEventHandler()
1118
- /**
1119
- * Deep event handlers
1120
- * @type {EventHandler<YEvent<DConf>,Transaction>}
1121
- */
1122
- this._dEH = createEventHandler()
1123
- /**
1124
- * @type {null | Array<ArraySearchMarker>}
1125
- */
1126
- this._searchMarker = null
1127
- /**
1128
- * Maintained deep-delta cache backing {@link YNode#delta}. `null` until `delta` is first
1129
- * accessed; thereafter kept current on every event of this type (incrementally, by applying the
1130
- * deep change) and re-diffed by {@link YNode#useRenderer}. Cleared by {@link YNode#clearCache}.
1131
- *
1132
- * INVARIANT (fingerprint-memo safety): consumers (e.g. y-sync) diff against this LIVE object
1133
- * via {@link YNode#delta}, which memoizes `_fingerprint` on its delta/op nodes at every depth.
1134
- * Every in-place patch — the per-transaction `apply` in `cleanupTransactions` and the
1135
- * renderer-overlay `apply` in {@link typeApplyRendererChange} — MUST route through the lib0
1136
- * delta builder API (`apply`), which invalidates those memos. NEVER write delta/op fields
1137
- * directly: a direct write leaves stale memos, corrupting every subsequent fingerprint read
1138
- * and diff. Pinned by the `testRdtFingerprintMemo*CacheDrift` tests.
1139
- * @type {delta.DeltaBuilderAny | null}
1140
- */
1141
- this._delta = null
1142
773
  this._legacyTypeRef = this.name == null ? YXmlFragmentRefID : YXmlElementRefID
1143
774
  /**
1144
775
  * @type {Array<ArraySearchMarker>|null}
1145
776
  */
1146
777
  this._searchMarker = []
1147
778
  /**
1148
- * Whether this YText contains formats.
1149
- * This flag is updated when a formatting item is integrated (see ContentFormat.integrate)
1150
- */
1151
- this._hasFormatting = false
1152
- /**
1153
- * The active default renderer. Used by `toDelta`, `applyDelta`, and the events whenever no
1154
- * explicit renderer is passed. `null` = no renderer: content renders as-is via the generic
1155
- * fast path. Change it via {@link YNode#useRenderer}.
1156
- * @type {AbstractRenderer?}
1157
- */
1158
- this._renderer = null
1159
- /**
1160
- * Bound listener on the active renderer's `'change'` event — attribution corrections that
1161
- * happen without a Y transaction on this doc (e.g. a suggestion is accepted and the
1162
- * renderer's attribution overlay updates). `null` while no renderer is active.
1163
- * Managed by {@link YNode#useRenderer}; see {@link typeApplyRendererChange}.
1164
- * @type {((changes: IdSet, origin: any, local: boolean) => void) | null}
779
+ * The writes made while this node is detached, replayed when it integrates (YP16).
780
+ * @type {Prelim|null}
1165
781
  */
1166
- this._rendererChangeHandler = null
1167
- }
1168
-
1169
- /**
1170
- * Schema of the deltas this type produces — part of the lib0 `RDT` interface.
1171
- *
1172
- * @type {s.Schema<delta.Delta<DConf>>}
1173
- */
1174
- get $delta () {
1175
- return /** @type {any} */ (delta.$deltaAny)
1176
- }
1177
-
1178
- /**
1179
- * The deep delta of this type (the full nested content tree, children rendered as their own
1180
- * deltas).
1181
- *
1182
- * The returned value is the type's **live** maintained cache: it is materialized on first access
1183
- * and then kept current on every event fired on this type (and re-diffed by
1184
- * {@link YNode#useRenderer}), so a reference held across edits keeps updating in place. Clone it
1185
- * (e.g. `type.delta.clone()`) if you need a stable snapshot, and call {@link YNode#clearCache} to
1186
- * drop the cache.
1187
- *
1188
- * Consider the returned delta **done** — it must not be edited from the outside. It is
1189
- * deliberately typed as a `Delta` (not a `DeltaBuilder`) so the mutating builder API is not
1190
- * reachable; editing it anyway would corrupt the cache without changing the CRDT. The proper way
1191
- * to change this type is {@link YNode#applyDelta}.
1192
- *
1193
- * @type {delta.Delta<DConf>}
1194
- */
1195
- get delta () {
1196
- if (this._delta === null) {
1197
- this._delta = this._renderDelta()
1198
- }
1199
- return /** @type {any} */ (this._delta)
1200
- }
1201
-
1202
- /**
1203
- * Render the full deep current state into a fresh `isFinal` builder (so subsequent `.apply`s of
1204
- * deep changes update content in place). Uses this type's active renderer.
1205
- *
1206
- * @return {delta.DeltaBuilderAny}
1207
- */
1208
- _renderDelta () {
1209
- const state = /** @type {delta.DeltaBuilderAny} */ (delta.create(this.name))
1210
- state.isFinal = true
1211
- state.apply(this.toDelta({ deep: true }))
1212
- return state
1213
- }
1214
-
1215
- /**
1216
- * Discard the cached deep delta backing {@link YNode#delta}.
1217
- *
1218
- * After `delta` is first accessed, the cache is updated on every event fired on this type (and
1219
- * re-diffed by {@link YNode#useRenderer}). Call this to drop it — e.g. to reclaim memory, or to
1220
- * force an exact recomputation after editing while a non-base renderer is active (the incremental
1221
- * updates can drift from a fresh deep render in that case).
1222
- */
1223
- clearCache () {
1224
- this._delta = null
1225
- }
1226
-
1227
- /**
1228
- * Change the default renderer used by this type. After calling `useRenderer(renderer)`, the
1229
- * `toDelta`, `applyDelta`, and event methods all use `renderer` whenever no explicit renderer is
1230
- * passed (an explicit `{ renderer }` argument still overrides it per call).
1231
- *
1232
- * If the deep-delta cache ({@link YNode#delta}) is being maintained, or a `'delta'` listener is
1233
- * attached, the content is re-rendered with the new renderer and the difference is emitted on the
1234
- * `'delta'` channel only (a renderer switch is not a CRDT change, so no `YEvent` is produced, and
1235
- * the emitted origin is `null` as no transaction is involved).
1236
- *
1237
- * @param {AbstractRenderer?} renderer - `null` detaches: content renders as-is again
1238
- * @return {this}
1239
- */
1240
- useRenderer (renderer) {
1241
- const prev = this._renderer
1242
- if (renderer === prev) return this
1243
- if (this._rendererChangeHandler !== null) {
1244
- /** @type {AbstractRenderer} */ (prev).off('change', this._rendererChangeHandler)
1245
- this._rendererChangeHandler = null
1246
- }
1247
- if (renderer !== null && this._rendererChangeHandler === null) {
1248
- // attribution corrections (e.g. accepting a suggestion) reach the renderer without a Y
1249
- // transaction on this doc — subscribe so the RDT surface (maintained cache + 'delta'
1250
- // channel) stays current. Without a renderer there are no attributions, hence no
1251
- // subscription. Not gated on `renderer !== prev`: after `destroy()` the handler is removed
1252
- // while `_renderer` keeps pointing at the renderer, so re-activating with the same renderer
1253
- // must re-subscribe.
1254
- this._rendererChangeHandler = (changes, origin) => typeApplyRendererChange(this, changes, origin)
1255
- renderer.on('change', this._rendererChangeHandler)
1256
- }
1257
- const hasDeltaListeners = (this._observers.get('delta')?.size ?? 0) > 0
1258
- if (this._delta !== null || hasDeltaListeners) {
1259
- const oldState = this._delta ?? this._renderDelta()
1260
- this._renderer = renderer
1261
- const newState = this._renderDelta()
1262
- if (this._delta !== null) this._delta = newState
1263
- if (hasDeltaListeners) {
1264
- const d = /** @type {any} */ (delta.diff(/** @type {any} */ (oldState), /** @type {any} */ (newState)))
1265
- if (!d.isEmpty()) this.emit('delta', [d, null])
1266
- }
1267
- } else {
1268
- this._renderer = renderer
1269
- }
1270
- return this
1271
- }
1272
-
1273
- /**
1274
- * Tear down this type as an `RDT`: emit the `'destroy'` event, unregister all `'delta'` /
1275
- * `'destroy'` listeners, and reset the RDT surface (active renderer detached, the
1276
- * maintained {@link YNode#delta} cache dropped). The CRDT content and the
1277
- * `observe`/`observeDeep` handlers are left untouched — this only releases the RDT/binding
1278
- * observers. Without the reset, a still-materialized cache would keep being maintained through
1279
- * the transaction path but no longer receive the renderer's attribution corrections — reading
1280
- * `delta` after `destroy()` would silently drift. Re-activating is fully supported:
1281
- * `useRenderer(renderer)` re-subscribes and `delta` re-materializes.
1282
- */
1283
- destroy () {
1284
- if (this._rendererChangeHandler !== null) {
1285
- /** @type {AbstractRenderer} */ (this._renderer).off('change', this._rendererChangeHandler)
1286
- this._rendererChangeHandler = null
1287
- }
1288
- this._renderer = null
1289
- this._delta = null
1290
- this.emit('destroy', [this])
1291
- super.destroy()
782
+ this._prelim = null
1292
783
  }
1293
784
 
1294
785
  /**
1295
- * @template {delta.DeltaConf} DC
1296
- * @param {delta.Delta<DC>} d
1297
- * @return {YNode<DC>}
786
+ * The live length of the sequence (`0` while detached).
1298
787
  */
1299
- static from (d) {
1300
- const yt = new YNode(d.name)
1301
- yt.applyDelta(d)
1302
- return yt
1303
- }
1304
-
1305
788
  get length () {
1306
- this.doc ?? warnPrematureAccess()
1307
789
  return this._length
1308
790
  }
1309
791
 
1310
- /**
1311
- * Returns a fresh delta that can be used to change this YNode.
1312
- * @type {delta.DeltaBuilder<DeltaToYNode<DConf>>}
1313
- */
1314
- get change () {
1315
- return /** @type {any} */ (delta.create())
1316
- }
1317
-
1318
- /**
1319
- * @return {YNode<any>?}
1320
- */
1321
- get parent () {
1322
- return /** @type {YNode<any>?} */ (this._item ? this._item.parent : null)
1323
- }
1324
-
1325
792
  /**
1326
793
  * Integrate this type into the Yjs instance.
1327
794
  *
1328
- * * Save this struct in the os
1329
- * * This type is sent to other client
1330
- * * Observer functions are fired
1331
- *
1332
795
  * @param {Doc} y The Yjs instance
1333
796
  * @param {Item|null} item
1334
797
  */
1335
798
  _integrate (y, item) {
1336
799
  this.doc = y
1337
800
  this._item = item
1338
- if (this._prelim) {
1339
- this.applyDelta(this._prelim)
801
+ const prelim = this._prelim
802
+ if (prelim !== null) {
1340
803
  this._prelim = null
804
+ if ((prelim.runs.length > 0 || Object.keys(prelim.attrs).length > 0) && (item === null || !item.deleted)) {
805
+ transact(y, transaction => writePrelim(transaction, this, prelim))
806
+ }
1341
807
  }
1342
808
  }
1343
809
 
1344
810
  /**
1345
- * @return {YNode<DConf>}
811
+ * @return {YNode}
1346
812
  */
1347
813
  _copy () {
1348
814
  const ynode = new YNode(this.name)
@@ -1351,972 +817,63 @@ export class YNode extends ObservableV2 {
1351
817
  }
1352
818
 
1353
819
  /**
1354
- * Creates YEvent and calls all type observers.
1355
- * Must be implemented by each type.
820
+ * Removes an attribute. A deleted node is left as it is.
1356
821
  *
1357
- * @param {Transaction} transaction
1358
- * @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
822
+ * @param {string} attributeName The attribute name that is to be removed.
1359
823
  */
1360
- _callObserver (transaction, parentSubs) {
1361
- const event = new YEvent(/** @type {any} */ (this), transaction, parentSubs)
1362
- callTypeObservers(/** @type {any} */ (this), transaction, event)
1363
- // Note: the RDT `'delta'` channel (and the deep-delta cache) is driven in the transaction
1364
- // cleanup's `changedParentTypes` loop (see Transaction.js) so it bubbles to ancestors like
1365
- // `observeDeep`, not here where only the directly-changed type is visible.
1366
- if (!transaction.local && this._searchMarker) {
1367
- this._searchMarker.length = 0
1368
- }
1369
- // If a remote change happened, we try to cleanup potential formatting duplicates.
1370
- if (!transaction.local && this._hasFormatting) {
1371
- transaction._needFormattingCleanup = true
824
+ deleteAttr (attributeName) {
825
+ if (this.doc == null) {
826
+ prelimOf(this).attrs[attributeName] = null
827
+ } else if (this._item === null || !this._item.deleted) {
828
+ transact(this.doc, transaction => nodeMapDelete(transaction, this, attributeName))
1372
829
  }
1373
830
  }
1374
831
 
1375
832
  /**
1376
- * Observe all events that are created on this type.
833
+ * Sets or updates an attribute. A deleted node is left as it is.
1377
834
  *
1378
- * @template {(target: YEvent<DeltaToYNode<DConf>>, tr: Transaction) => void} F
1379
- * @param {F} f Observer function
1380
- * @return {F}
1381
- */
1382
- observe (f) {
1383
- addEventHandlerListener(this._eH, f)
1384
- return f
1385
- }
1386
-
1387
- /**
1388
- * Observe all events that are created by this type and its children.
1389
- *
1390
- * @template {function(YEvent<DConf>,Transaction):void} F
1391
- * @param {F} f Observer function
1392
- * @return {F}
1393
- */
1394
- observeDeep (f) {
1395
- addEventHandlerListener(this._dEH, f)
1396
- return f
1397
- }
1398
-
1399
- /**
1400
- * Unregister an observer function.
1401
- *
1402
- * @param {(type:YEvent<DeltaToYNode<DConf>>,tr:Transaction)=>void} f Observer function
1403
- */
1404
- unobserve (f) {
1405
- removeEventHandlerListener(this._eH, f)
1406
- }
1407
-
1408
- /**
1409
- * Unregister an observer function.
1410
- *
1411
- * @param {function(YEvent<DConf>,Transaction):void} f Observer function
1412
- */
1413
- unobserveDeep (f) {
1414
- removeEventHandlerListener(this._dEH, f)
1415
- }
1416
-
1417
- /**
1418
- * Render the difference to another ydoc (which can be empty) and highlight the differences with
1419
- * attributions.
1420
- *
1421
- * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
1422
- * attribution `{ isDeleted: true, .. }`.
1423
- *
1424
- * @template {boolean} [Deep=false]
1425
- *
1426
- * @param {Object} [opts]
1427
- * @param {AbstractRenderer?} [opts.renderer] - renders the content (with attributions); defaults to this type's active renderer (see {@link YNode#useRenderer}), i.e. `null` (render as-is) unless changed
1428
- * @param {IdSet?} [opts.itemsToRender]
1429
- * @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions
1430
- * @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only
1431
- * @param {IdSet?} [opts.insertedItems] - ids inserted by the change being rendered; content that is both in `insertedItems` and deleted renders as a *fresh* insert even under `retainDeletes` (it was never part of the consuming state)
1432
- * @param {Map<YNode,Set<string|null>>|null} [opts.modified] - set of types that should be rendered as modified children
1433
- * @param {Deep} [opts.deep] - render child types as delta
1434
- * @return {Deep extends true ? delta.Delta<DConf> : delta.Delta<DeltaConfDeltaToYNode<DConf>>} The Delta representation of this type.
1435
- *
1436
- * @public
1437
- */
1438
- toDelta (opts = {}) {
1439
- const { renderer = this._renderer, itemsToRender = null, retainInserts = false, retainDeletes = false, insertedItems = null, deep = false } = opts
1440
- const { modified = (deep && itemsToRender) ? computeModifiedFromItems(/** @type {Doc} */ (this.doc).store, itemsToRender) : null } = opts
1441
- const renderAttrs = modified?.get(this) || null
1442
- const renderChildren = modified == null || !modified.has(this) || /** @type {Set<string|null>} */ (modified.get(this)).has(null)
1443
- /**
1444
- * @type {delta.DeltaBuilderAny}
1445
- */
1446
- const d = /** @type {any} */ (delta.create(this.name))
1447
- const optsAll = object.assign({}, opts, { renderer, modified })
1448
- // opts has been re-computed - do not use opts after this point!
1449
- nodeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, renderer, deep, modified, itemsToRender, optsAll, optsAll)
1450
- if (renderChildren) {
1451
- /**
1452
- * @type {delta.Formats}
1453
- */
1454
- let currentFormats = {} // saves all current formats for insert
1455
- /**
1456
- * YP13: the open paired mark operations, by mark.
1457
- * @type {Map<string,any>}
1458
- */
1459
- const pairedFormats = new Map()
1460
- let usingCurrentFormats = false
1461
- /**
1462
- * @type {delta.Formats}
1463
- */
1464
- let changedFormats = {} // saves changed formats for retain
1465
- let usingChangedFormats = false
1466
- /**
1467
- * Logic for format attribution
1468
- * Everything that comes after a format is formatted by the user that created it.
1469
- * Two exceptions:
1470
- * - the user resets formatting to the previously known formatting that is not attributed
1471
- * - the user deletes a format and hence restores the previously known formatting
1472
- * that is not attributed.
1473
- * @type {delta.Formats}
1474
- */
1475
- const previousUnattributedFormats = {} // contains previously known unattributed formatting
1476
- /**
1477
- * @type {delta.Formats}
1478
- */
1479
- const previousFormats = {} // The value before changes
1480
- /**
1481
- * Per-key provenance of `currentFormats`: `true` iff the marker currently governing the key
1482
- * opened a *pending attributed* range (the attributor-push branch below). Consulted only at
1483
- * deleted markers in change renders (`isDeletedFormatClear`) to distinguish a re-exposed
1484
- * committed enclosing value (must clear the stale attribution from the cache) from a
1485
- * re-exposed pending suggested value (must preserve it — the re-bolding case).
1486
- * @type {{ [key: string]: boolean }}
1487
- */
1488
- const currentFormatsAttributed = {}
1489
- /**
1490
- * Attribution argument for a change-render retain whose content lost its *own* attribution.
1491
- * `null` clears everything — correct in an attribution-free context — but inside an ambient
1492
- * format attribution (installed via `useAttribution` by a surrounding format marker) the
1493
- * fresh render still stamps that ambient format on this content. An object argument merges
1494
- * over the ambient context (lib0 `combineInstr`), so emit per-key removals instead: they
1495
- * clear only the op's own insert/delete attribution while re-asserting the ambient format.
1496
- */
1497
- const clearedOwnAttribution = () => d.usedAttribution == null ? null : /** @type {any} */ ({ insert: null, insertAt: null, delete: null, deleteAt: null })
1498
- /**
1499
- * Format keys touched by a *rendered* format marker during an attributing change render. A
1500
- * merely retained attributed marker normally must not emit ops — but when a rendered
1501
- * same-key marker preceded it in this walk, the change is what made the retained boundary
1502
- * reset-to-previous (e.g. a base-doc format arriving under a same-key format suggestion),
1503
- * and the spans it governs must drop their now-stale attribution from the cache. Per-key
1504
- * tracking is required (a plain "any format rendered" flag would over-clear other keys),
1505
- * but the set is only ever consulted at format markers — never per character — and stays
1506
- * `null` on all hot paths: full renders, base-renderer renders, and change renders without
1507
- * format markers never allocate it.
1508
- * @type {Set<string>?}
1509
- */
1510
- let renderedFormatKeys = null
1511
- /**
1512
- * Renderer output for a single item — a renderer may split an item into multiple
1513
- * attributed pieces.
1514
- * @type {Array<AttributedContent<any>>}
1515
- */
1516
- const cs = []
1517
- /**
1518
- * Reusable trivial piece for the `readItemPieces` current-state walk —
1519
- * `cs` is drained per item, so a single allocation (captured lazily
1520
- * from the first emitted piece) serves the whole render.
1521
- * @type {AttributedContent<any>?}
1522
- */
1523
- let csScratch = null
1524
- /**
1525
- * Process one piece of (possibly attributed) content — the shared op-emission and format
1526
- * state machine. The renderer path feeds it every piece produced by `readContent`; the
1527
- * generic path feeds it `ContentFormat` markers only (the state machine must see every
1528
- * marker, attributed or not — a plain marker can e.g. close a formerly attributed range)
1529
- * and emits ops for plain content directly.
1530
- * @param {AttributedContent<any>} c
1531
- */
1532
- const processContent = c => {
1533
- // render (attributed) content even if it was deleted
1534
- const renderContent = c.render && (!c.deleted || c.attrs != null)
1535
- // content that was just deleted. It is not rendered as an insertion, because it doesn't
1536
- // have any formats.
1537
- const renderDelete = c.render && c.deleted
1538
- // existing content that should be retained, only adding changed formats
1539
- const retainContent = !c.render && (!c.deleted || c.attrs != null)
1540
- const attribution = (renderContent || c.content.constructor === ContentFormat) ? createAttributionFromAttributionItems(c.attrs, c.deleted) : undefined
1541
- switch (c.content.constructor) {
1542
- case ContentDeleted: {
1543
- // fresh (mode 3) content renders as nothing here: the consuming state never saw it,
1544
- // and gc'd content cannot render as an insert — a delete op would misapply
1545
- if (renderDelete && !c.fresh) d.delete(c.content.getLength())
1546
- break
1547
- }
1548
- case ContentString:
1549
- if (renderContent) {
1550
- if (c.deleted ? (retainDeletes && !c.fresh) : retainInserts) {
1551
- // a retain expresses the format *diff* against existing (cached) content, so use
1552
- // `changedFormats`: a format removed this change (e.g. its marker was deleted)
1553
- // is present there as a `null` clear, whereas `currentFormats` (absolute) can
1554
- // only re-assert present formats and would silently keep a stale one.
1555
- // (`c.fresh` content is exempt from `retainDeletes`: it was inserted *and*
1556
- // deleted by this very change, so the consuming state holds nothing to retain —
1557
- // it renders as an insert below, carrying its attribution.)
1558
- d.useFormats(changedFormats)
1559
- usingChangedFormats = true
1560
- // change render: a retained item with no attribution means its attribution was
1561
- // removed → emit a clear rather than `{}` (skip). Present attribution merges.
1562
- d.retain(/** @type {ContentString} */ (c.content).str.length, undefined, attribution ?? clearedOwnAttribution())
1563
- } else {
1564
- d.useFormats(currentFormats)
1565
- usingCurrentFormats = true
1566
- d.insert(/** @type {ContentString} */ (c.content).str, undefined, attribution)
1567
- }
1568
- } else if (renderDelete) {
1569
- d.delete(c.content.getLength())
1570
- } else if (retainContent) {
1571
- d.useFormats(changedFormats)
1572
- usingChangedFormats = true
1573
- d.retain(c.content.getLength())
1574
- }
1575
- break
1576
- case ContentEmbed:
1577
- case ContentAny:
1578
- case ContentJSON:
1579
- case ContentType:
1580
- case ContentBinary:
1581
- case ContentDoc:
1582
- if (renderContent) {
1583
- if (c.deleted ? (retainDeletes && !c.fresh) : retainInserts) {
1584
- // a retain expresses the format *diff* → use `changedFormats` (see ContentString)
1585
- d.useFormats(changedFormats)
1586
- usingChangedFormats = true
1587
- if (c.deleted && c.content.constructor === ContentType) {
1588
- // @todo use current transaction instead
1589
- d.modify(/** @type {any} */ (c.content).type.toDelta(optsAll), undefined, attribution ?? null)
1590
- } else if (c.content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (c.content).type)) {
1591
- // a de/re-attributed node the renderer still claims (e.g. a partial accept):
1592
- // merge/clear its own attribution and descend so the nested walk heals exactly
1593
- // the child ids in `itemsToRender` — other children keep their attribution
1594
- d.modify(/** @type {any} */ (c.content).type.toDelta(optsAll), undefined, attribution ?? clearedOwnAttribution())
1595
- } else {
1596
- d.retain(c.content.getLength(), undefined, attribution ?? clearedOwnAttribution())
1597
- }
1598
- } else if (deep && c.content.constructor === ContentType) {
1599
- d.useFormats(currentFormats)
1600
- usingCurrentFormats = true
1601
- d.insert([/** @type {any} */(c.content).type.toDelta(optsAll)], undefined, attribution)
1602
- } else {
1603
- d.useFormats(currentFormats)
1604
- usingCurrentFormats = true
1605
- d.insert(c.content.getContent(), undefined, attribution)
1606
- }
1607
- } else if (renderDelete) {
1608
- d.delete(1)
1609
- } else if (retainContent) {
1610
- if (c.content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (c.content).type)) {
1611
- // @todo use current transaction instead
1612
- d.modify(/** @type {any} */ (c.content).type.toDelta(optsAll))
1613
- } else {
1614
- d.useFormats(changedFormats)
1615
- usingChangedFormats = true
1616
- d.retain(1)
1617
- }
1618
- }
1619
- break
1620
- case ContentFormat: {
1621
- let { key, value } = /** @type {ContentFormat} */ (c.content)
1622
- // YP13: a live paired mark item renders as its mark's winning value there
1623
- if (pairedRole(/** @type {ContentFormat} */ (c.content)) >= 0) {
1624
- if (c.deleted) break
1625
- foldPaired(pairedFormats, /** @type {ContentFormat} */ (c.content))
1626
- key = key.slice(1)
1627
- value = formatValue(pairedFormats.get(key) ?? null)
1628
- }
1629
- const currFormatVal = currentFormats[key] ?? null
1630
- if (attribution != null && (c.deleted || !object.hasProperty(previousUnattributedFormats, key))) {
1631
- previousUnattributedFormats[key] = c.deleted ? value : currFormatVal
1632
- }
1633
- // @todo write a function "updateCurrentFormats" and "updateChangedFormats"
1634
- // # Update Formats
1635
- if (renderContent || renderDelete) {
1636
- // create fresh references
1637
- if (usingCurrentFormats) {
1638
- currentFormats = object.assign({}, currentFormats)
1639
- usingCurrentFormats = false
1640
- }
1641
- if (usingChangedFormats) {
1642
- usingChangedFormats = false
1643
- changedFormats = object.assign({}, changedFormats)
1644
- }
1645
- }
1646
- if (renderContent || renderDelete) {
1647
- if (itemsToRender !== null && renderer !== null) {
1648
- // only attributing change renders consult this (see the reset branch below)
1649
- if (renderedFormatKeys === null) renderedFormatKeys = new Set()
1650
- renderedFormatKeys.add(key)
1651
- }
1652
- // `previousFormats` tracks the value governing the current walk position in the
1653
- // *consuming cache state*, so the retain diff below stays a plain cache → new-state
1654
- // comparison. Contributors: markers deleted by this change in event renders (their
1655
- // value governed the cache until now; heal-rendered deletes are PRE-deleted markers
1656
- // whose removal the cache already rendered — they must not contribute), alive
1657
- // markers re-rendered by heal renders (`retainInserts` — their value was in the
1658
- // cache), and retained alive markers (the branch below). A gated update here is not
1659
- // enough: a marker deleted by the change whose value coincides with the walk's
1660
- // current value would silently drop out of the cache-reference tracking, and a later
1661
- // marker would be misread as a restore-to-previous, swallowing a needed format diff.
1662
- if (c.deleted) {
1663
- if (itemsToRender !== null && !retainInserts) previousFormats[key] = value
1664
- } else { // !c.deleted
1665
- if (retainInserts) previousFormats[key] = value
1666
- if (value == null) {
1667
- delete currentFormats[key]
1668
- } else {
1669
- currentFormats[key] = value
1670
- }
1671
- currentFormatsAttributed[key] = false
1672
- }
1673
- // the retain diff for following spans is exactly cache (previousFormats) → new
1674
- // state (currentFormats), recomputed per key at every rendered marker
1675
- if (equalFormats(currentFormats[key] ?? null, previousFormats[key] ?? null)) {
1676
- delete changedFormats[key]
1677
- } else {
1678
- changedFormats[key] = currentFormats[key] ?? null
1679
- }
1680
- } else if (retainContent && !c.deleted) {
1681
- // fresh reference to currentFormats only
1682
- if (usingCurrentFormats) {
1683
- currentFormats = object.assign({}, currentFormats)
1684
- usingCurrentFormats = false
1685
- }
1686
- if (usingChangedFormats && changedFormats[key] !== undefined) {
1687
- usingChangedFormats = false
1688
- changedFormats = object.assign({}, changedFormats)
1689
- }
1690
- if (value == null) {
1691
- delete currentFormats[key]
1692
- } else {
1693
- currentFormats[key] = value
1694
- }
1695
- currentFormatsAttributed[key] = false
1696
- delete changedFormats[key]
1697
- previousFormats[key] = value
1698
- }
1699
- // # Update Attributions
1700
- // A format marker deleted in a change render under an *attributing* renderer nets to no
1701
- // attribution (its insert+delete suggestion cancels → `attribution == null`), yet it ends
1702
- // the attributed range it opened: the following retained content must drop the stale
1703
- // `{ format: { [key]: [] } }` the marker's insertion wrote to the cache. Emit an explicit
1704
- // `null` leaf for the key (a context-wide `useAttribution(null)` cannot carry a per-key
1705
- // clear). Conditions: only an attributing render (`renderer !== null`; without a
1706
- // renderer there are no attributions to clear), and only when the deletion reverts to
1707
- // a value that is not itself governed by a pending suggestion: either no value at all
1708
- // (`currFormatVal == null`) or a *committed* enclosing value (provenance tracked in
1709
- // `currentFormatsAttributed` — e.g. an accept/reject deleting a marker re-exposes the
1710
- // enclosing committed `bold:true`, and the span's stale suggested-format attribution
1711
- // must be cleared). If it reverts to a still-pending *attributed* value (deleting a
1712
- // `bold:null` marker re-exposes an enclosing attributed `bold:true`, as when
1713
- // re-bolding), the attribution is preserved. Suppressed entirely while an attributed
1714
- // same-key range is open (`previousUnattributedFormats` has the key): there the fresh
1715
- // render skips this marker — its governance belongs to the still-open attributed
1716
- // range, whose ambient context re-stamps the correct attribution on the following
1717
- // retains — mirroring the identical guard on `isAcceptedFormatClear`.
1718
- const isDeletedFormatClear = attribution == null && renderer !== null && renderDelete && c.deleted && itemsToRender != null && (currFormatVal == null || currentFormatsAttributed[key] !== true) && !equalFormats(value, currFormatVal) && !object.hasProperty(previousUnattributedFormats, key)
1719
- // The alive-marker analogue of `isDeletedFormatClear`: a format marker whose
1720
- // *attribution* was removed while the marker survives — its suggestion was accepted
1721
- // (the marker's id arrives via the renderer's `'change'` event). The original change
1722
- // render stamped `{ format: { [key]: [] } }` on the spans this marker governs, so the
1723
- // heal render must emit a per-key `null` leaf over those spans (a blunt
1724
- // `attribution: null` would also wipe unrelated attributions such as a co-located
1725
- // `{ delete: [] }`). Gated on `retainInserts` — the diff-against-existing-content
1726
- // mode used by attribution-heal renders: in a plain event render an unattributed
1727
- // alive marker is just new unattributed content and must not emit spurious clears.
1728
- // When the key is tracked as an open attributed range (`previousUnattributedFormats`)
1729
- // the accepted marker instead defers to the regular branch chain below, which mirrors
1730
- // the fresh render exactly: mid-range it *keeps* the ambient attribution (relative to
1731
- // the base doc the span's format is still a suggested change), and at the range end
1732
- // (`sameAsPreviousAttributions`) it closes the attribution context.
1733
- const isAcceptedFormatClear = attribution == null && renderer !== null && renderContent && !c.deleted && itemsToRender != null && retainInserts && !object.hasProperty(previousUnattributedFormats, key)
1734
- if (attribution != null || isDeletedFormatClear || isAcceptedFormatClear || object.hasProperty(previousUnattributedFormats, key)) {
1735
- /**
1736
- * @type {Attribution}
1737
- */
1738
- const formattingAttribution = object.assign({}, d.usedAttribution)
1739
- const changedAttributedFormats = /** @type {{ [key: string]: Array<any>|null }} */ (formattingAttribution.format = object.assign({}, formattingAttribution.format ?? {}))
1740
- const sameAsPreviousAttributions = equalFormats(previousUnattributedFormats[key], currentFormats[key] ?? null)
1741
- // An *unattributed* marker rendered by this change (e.g. a fresh base-doc marker)
1742
- // that closes an open attributed same-key range by restoring the previous
1743
- // unattributed value: the spans it governs must drop their stale attribution from
1744
- // the cache, exactly like the attributed range-end below. Mirrors the fresh render
1745
- // precisely: there, the reset only closes the ambient attribution when removing the
1746
- // key leaves the format context empty (the `useAttribution(null)` case below) —
1747
- // while another attributed key holds the context open, the removal happens on a
1748
- // copy that is never installed, so the key survives on the following spans and the
1749
- // change render must NOT clear it.
1750
- const isRangeEndClear = attribution == null && sameAsPreviousAttributions && renderContent && !c.deleted && itemsToRender != null && renderer !== null && object.hasProperty(previousUnattributedFormats, key) && object.every(changedAttributedFormats, (_v, k) => k === key)
1751
- if (isDeletedFormatClear || (isAcceptedFormatClear && changedAttributedFormats[key] == null)) {
1752
- // uniformly emit the per-key clear — also for a marker that *ends* a formerly
1753
- // attributed range: the attributed render emits a trailing per-key `null` past
1754
- // the closing marker as well (see the `attribution != null` case below), so the
1755
- // heal render mirrors that exact span; where the cache holds no stale
1756
- // attribution the clear merges as a no-op.
1757
- changedAttributedFormats[key] = null
1758
- delete previousUnattributedFormats[key]
1759
- } else if (isAcceptedFormatClear) {
1760
- // accepted marker inside a *still-attributed* (pending) range for the same key —
1761
- // the ambient context holds an attributor list, not a clear. Relative to the base
1762
- // doc the effective format of this span is still a suggested change, so the fresh
1763
- // render keeps attributing it (the `skip` case below): keep the ambient
1764
- // attribution rather than clearing it.
1765
- } else if (attribution == null && !sameAsPreviousAttributions) {
1766
- // skip
1767
- } else if (attribution == null || sameAsPreviousAttributions) {
1768
- // an unattributed format was found or an attributed format
1769
- // was found that resets to the previous status. When this format item is
1770
- // itself rendered this transaction (`renderContent || renderDelete`) in a change/diff
1771
- // render (`itemsToRender != null`), it is the END of an attributed format range:
1772
- // emit an explicit clear (a `null` leaf) so the retained content drops any stale
1773
- // `{ format: { [key]: [] } }` from the maintained `delta` cache — a bare context-skip
1774
- // (`delete`) would leave it in place. The same applies to a *retained* attributed
1775
- // boundary when a rendered same-key marker preceded it in this walk
1776
- // (`renderedFormatKeys`): the boundary only resets-to-previous *because of* the
1777
- // rendered change (e.g. a base-doc format arriving under an equal same-key format
1778
- // suggestion — which marker comes first depends on client ids), so the spans it
1779
- // governs must drop their now-stale attribution too. For a merely-retained boundary
1780
- // with no rendered same-key marker, or a full insert render (removal is already
1781
- // modeled as absence in `currentFormats`), just drop the key: a change render must
1782
- // not emit ops for unchanged ranges, and inserts must stay free of a spurious
1783
- // `{ format: { [key]: null } }`.
1784
- // `c.fresh`: a marker inserted (and possibly auto-deleted) by this very change —
1785
- // e.g. a base-doc format clear arriving inside a suggestion-deleted paragraph —
1786
- // stays on the retained path (mode 0, the fresh-format exemption), but when it
1787
- // resets-to-previous it is the range END of the attributed run in the new state
1788
- // and must clear the stale attribution from the cache like a rendered marker.
1789
- if (isRangeEndClear || (attribution != null && itemsToRender != null && (renderContent || renderDelete || c.fresh || renderedFormatKeys?.has(key) === true))) {
1790
- changedAttributedFormats[key] = null
1791
- } else {
1792
- delete changedAttributedFormats[key]
1793
- // pending per-key clears force this copy to install (the all-null disjunct
1794
- // below); installing after silently dropping this key would close its
1795
- // governance without the clear the fresh render's context-close implies,
1796
- // leaving a stale `{ format: { [key]: [] } }` on the following retained
1797
- // spans — re-add the key as an explicit clear. Inert outside attributing
1798
- // change renders: null leaves cannot exist in fresh contexts.
1799
- if (itemsToRender != null && !object.isEmpty(changedAttributedFormats) && object.every(changedAttributedFormats, v => v == null)) {
1800
- changedAttributedFormats[key] = null
1801
- }
1802
- }
1803
- delete previousUnattributedFormats[key]
1804
- } else {
1805
- // an attributed marker opens pending governance for the key — a deleted attributed
1806
- // marker never writes `currentFormats`, hence the alive gate; an attributed marker
1807
- // that value-equals the previous unattributed value takes the reset branch above
1808
- // and correctly yields committed governance
1809
- if (!c.deleted) currentFormatsAttributed[key] = true
1810
- const by = changedAttributedFormats[key] = (changedAttributedFormats[key]?.slice() ?? [])
1811
- by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? []))
1812
- const attributedAt = (c.deleted ? attribution.deleteAt : attribution.insertAt)
1813
- if (attributedAt) formattingAttribution.formatAt = attributedAt
1814
- }
1815
- if (object.isEmpty(changedAttributedFormats)) {
1816
- d.useAttribution(null)
1817
- } else if (attribution != null || isDeletedFormatClear || isAcceptedFormatClear || isRangeEndClear || (itemsToRender != null && object.every(changedAttributedFormats, v => v == null))) {
1818
- // the last disjunct: a change-render copy whose remaining leaves are ALL per-key
1819
- // null clears is the analogue of the fresh render's empty copy (fresh contexts
1820
- // never contain null leaves — they are produced only by the change-render-only
1821
- // predicates above) — install it, so an unattributed close marker's key deletion
1822
- // closes the governance while the pending cache clears still propagate; discarding
1823
- // it would re-merge the stale key into the cache from the surviving ambient
1824
- const attributedAt = (c.deleted ? attribution?.deleteAt : attribution?.insertAt)
1825
- if (attributedAt != null) formattingAttribution.formatAt = attributedAt
1826
- d.useAttribution(formattingAttribution)
1827
- }
1828
- }
1829
- break
1830
- }
1831
- }
1832
- }
1833
- for (let item = this._start; item !== null; item = item.right) {
1834
- const content = item.content
1835
- if (itemsToRender === null && !retainInserts) {
1836
- // Current-state render — the same physical-sequence interpretation
1837
- // `RangeCursor`'s bounded reads consume (UPSTREAM.md YP5):
1838
- // `readItemPieces` produces the item's AttributedContent pieces
1839
- // (renderer-claimed → readContent mode 1; ContentFormat → one
1840
- // marker piece; tombstoned → nothing; live → whole item) and the
1841
- // shared state machine folds formats + emits ops for them —
1842
- // op-identical to the inlined fast-path dispatch it replaces.
1843
- cs.length = 0
1844
- readItemPieces(cs, renderer, item, csScratch)
1845
- if (csScratch === null) csScratch = cs[0] ?? null
1846
- for (let i = 0; i < cs.length; i++) {
1847
- processContent(cs[i])
1848
- }
1849
- continue
1850
- }
1851
- if (renderer === null || !renderer.hasItem(item)) {
1852
- // generic fast path: content the renderer doesn't claim renders as-is — no attribution
1853
- // lookups, no AttributedContent wrappers and, in the common full-coverage case, no
1854
- // content slicing
1855
- if (content.constructor === ContentFormat) {
1856
- // format markers always flow through the shared state machine
1857
- processContent(new AttributedContent(content, item.id.clock, item.deleted, null,
1858
- itemsToRender == null ? 1 : (itemsToRender.has(item.id.client, item.id.clock) ? 2 : 0)))
1859
- } else if (item.deleted) {
1860
- // plain deleted content is invisible; in a change render, the ranges deleted by this
1861
- // change emit `delete` ops — position-only, the content itself is not needed
1862
- if (itemsToRender !== null && itemsToRender.intersects(item.id.client, item.id.clock, item.length)) {
1863
- const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length)
1864
- for (let ir = 0; ir < rslice.length; ir++) {
1865
- const idrange = rslice[ir]
1866
- if (idrange.exists) {
1867
- // mirror the piece-wise op sizes of the renderer path: string-ish content
1868
- // deletes by length, other content deletes one element per piece
1869
- d.delete((content.constructor === ContentString || content.constructor === ContentDeleted) ? idrange.len : 1)
1870
- }
1871
- }
1872
- }
1873
- } else if (itemsToRender == null) {
1874
- if (retainInserts) {
1875
- // attribution-overlay render (e.g. `toDelta({ renderer, retainInserts: true })`):
1876
- // existing content is retained, clearing any formerly cached own-attribution
1877
- d.useFormats(changedFormats)
1878
- usingChangedFormats = true
1879
- d.retain(content.getLength(), undefined, clearedOwnAttribution())
1880
- } else {
1881
- // full render: a plain insert of the whole item
1882
- d.useFormats(currentFormats)
1883
- usingCurrentFormats = true
1884
- if (deep && content.constructor === ContentType) {
1885
- d.insert([/** @type {any} */(content).type.toDelta(optsAll)])
1886
- } else if (content.constructor === ContentString) {
1887
- d.insert(/** @type {ContentString} */ (content).str)
1888
- } else {
1889
- d.insert(content.getContent())
1890
- }
1891
- }
1892
- } else {
1893
- // change render on alive plain content: inserted ranges become inserts, everything
1894
- // else retains (position-only). `retainInserts` (attribution-heal renders) retains
1895
- // previously inserted content while clearing its former attribution.
1896
- const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length)
1897
- let itemContent = rslice.length > 1 ? content.copy() : content
1898
- for (let ir = 0; ir < rslice.length; ir++) {
1899
- const idrange = rslice[ir]
1900
- const c = itemContent
1901
- if (ir !== rslice.length - 1) {
1902
- itemContent = itemContent.splice(idrange.len)
1903
- }
1904
- if (!idrange.exists) {
1905
- if (content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (content).type)) {
1906
- // @todo use current transaction instead
1907
- d.modify(/** @type {ContentType} */ (content).type.toDelta(optsAll))
1908
- } else {
1909
- d.useFormats(changedFormats)
1910
- usingChangedFormats = true
1911
- // mirror the piece-wise op sizes of the renderer path (see the delete branch)
1912
- d.retain(content.constructor === ContentString ? idrange.len : 1)
1913
- }
1914
- } else if (retainInserts) {
1915
- if (content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (content).type)) {
1916
- // a de/re-attributed node (e.g. its insert-suggestion was accepted): clear its
1917
- // own attribution and descend so the nested walk heals exactly the child ids
1918
- // present in `itemsToRender` — children outside the change set keep their
1919
- // attribution (id-scoped, never a blanket subtree clear)
1920
- d.modify(/** @type {ContentType} */ (content).type.toDelta(optsAll), undefined, clearedOwnAttribution())
1921
- } else {
1922
- d.useFormats(changedFormats)
1923
- usingChangedFormats = true
1924
- d.retain(idrange.len, undefined, clearedOwnAttribution())
1925
- }
1926
- } else {
1927
- d.useFormats(currentFormats)
1928
- usingCurrentFormats = true
1929
- if (deep && content.constructor === ContentType) {
1930
- d.insert([/** @type {any} */(content).type.toDelta(optsAll)])
1931
- } else if (content.constructor === ContentString) {
1932
- d.insert(/** @type {ContentString} */ (c).str)
1933
- } else {
1934
- d.insert(c.getContent())
1935
- }
1936
- }
1937
- }
1938
- }
1939
- } else {
1940
- cs.length = 0
1941
- if (itemsToRender != null) {
1942
- const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length)
1943
- // fresh content deleted by the very change being rendered gets mode 3: it renders as
1944
- // an insert even under `retainDeletes` — the consuming state (e.g. the maintained
1945
- // `delta` cache) has never seen it, so there is nothing to retain. Freshness is
1946
- // decided per id range, NOT per item: structs merge across cleanups (e.g. a queued
1947
- // transaction's render runs after an earlier finally-block merged its fresh item into
1948
- // an older neighbor), so one item can span fresh and pre-existing ids. ContentFormat
1949
- // markers are exempt and keep mode 0 (the retained-marker path; they never merge, so
1950
- // the whole-item check is exact): the format state machine treats fresh-deleted
1951
- // markers exactly like before this change rendered.
1952
- const checkFresh = item.deleted && insertedItems !== null && content.constructor !== ContentFormat
1953
- const freshFormat = item.deleted && insertedItems !== null && content.constructor === ContentFormat && insertedItems.hasId(item.id)
1954
- let itemContent = rslice.length > 1 ? content.copy() : content
1955
- for (let ir = 0; ir < rslice.length; ir++) {
1956
- const idrange = rslice[ir]
1957
- let c = itemContent
1958
- if (ir !== rslice.length - 1) {
1959
- itemContent = itemContent.splice(idrange.len)
1960
- }
1961
- if (!idrange.exists || !checkFresh) {
1962
- renderer.readContent(cs, item.id.client, idrange.clock, item.deleted, c,
1963
- idrange.exists ? (freshFormat ? 0 : 2) : 0)
1964
- } else {
1965
- // an exists range may itself straddle fresh and pre-existing ids (ranges from
1966
- // different sources merge in `itemsToRender`) — split it against `insertedItems`
1967
- const fslice = insertedItems.slice(item.id.client, idrange.clock, idrange.len)
1968
- let subContent = fslice.length > 1 ? c.copy() : c
1969
- for (let fi = 0; fi < fslice.length; fi++) {
1970
- const frange = fslice[fi]
1971
- c = subContent
1972
- if (fi !== fslice.length - 1) {
1973
- subContent = subContent.splice(frange.len)
1974
- }
1975
- renderer.readContent(cs, item.id.client, frange.clock, item.deleted, c, frange.exists ? 3 : 2)
1976
- }
1977
- }
1978
- }
1979
- if (freshFormat) {
1980
- // fresh format markers stay on the retained path (mode 0, see `checkFresh`), but
1981
- // the format state machine must still see their freshness: a fresh clear marker
1982
- // that resets-to-previous ends an attributed range and must emit the per-key
1983
- // attribution clear (`cs` is reset per item; a freshFormat item is a single
1984
- // len-1 ContentFormat, so this marks exactly its pieces)
1985
- for (let i = 0; i < cs.length; i++) { cs[i].fresh = true }
1986
- }
1987
- } else {
1988
- renderer.readContent(cs, item.id.client, item.id.clock, item.deleted, content, 1)
1989
- }
1990
- for (let i = 0; i < cs.length; i++) {
1991
- processContent(cs[i])
1992
- }
1993
- }
1994
- }
1995
- }
1996
- return /** @type {any} */ (d.done(false))
1997
- }
1998
-
1999
- /**
2000
- * Render the difference to another ydoc (which can be empty) and highlight the differences with
2001
- * attributions.
2002
- *
2003
- * @param {Object} [opts]
2004
- * @param {AbstractRenderer?} [opts.renderer] - renders the content (with attributions); defaults to this type's active renderer (see {@link YNode#useRenderer}), i.e. `null` (render as-is) unless changed
2005
- * @return {delta.Delta<DConf>}
2006
- */
2007
- toDeltaDeep (opts = {}) {
2008
- return /** @type {any} */ (this.toDelta({ ...opts, deep: true }))
2009
- }
2010
-
2011
- /**
2012
- * Apply a {@link Delta} on this shared type.
2013
- *
2014
- * @param {delta.DeltaAny} d The changes to apply on this element.
2015
- * @param {any} [origin] Origin of the transaction that applies this delta (stored on
2016
- * `transaction.origin` and forwarded verbatim on the emitted `'delta'` event, so listeners can
2017
- * recognize — and skip — changes they produced themselves; see the lib0 `RDT` spec). Defaults to `null`.
2018
- * @param {Object} [opts]
2019
- * @param {AbstractRenderer?} [opts.renderer] - renders the content (with attributions); defaults to this type's active renderer (see {@link YNode#useRenderer}), i.e. `null` (render as-is) unless changed
2020
- * @return {delta.DeltaBuilder<any>?} The lib0 `RDT` "fix" of this apply — a change measured against the
2021
- * caller's expected state (`old.apply(d)`) that transforms it into the actual state, or `null`
2022
- * when `d` applied cleanly. A fix is produced when `d` (or a nested `modify`/`modifyAttr`)
2023
- * addresses a *deleted but rendered* node (e.g. a suggestion-deleted paragraph under a
2024
- * DiffRenderer): that part of the change is not applied to the document, and its inverse
2025
- * (`lib0-v14/delta` `inverse` against the node's rendered state) is returned — the change is
2026
- * immediately reverted.
2027
- *
2028
- * @public
2029
- */
2030
- applyDelta (d, origin = null, { renderer = this._renderer } = {}) {
2031
- if (d.isEmpty()) return null
2032
- if (this.doc == null) {
2033
- (this._prelim || (this._prelim = /** @type {any} */ (delta.create()))).apply(d)
2034
- return null
2035
- }
2036
- const titem = this._item
2037
- if (titem !== null && titem.deleted) {
2038
- if (rendererContentLength(renderer, titem) > 0) {
2039
- // deleted, but still rendered (e.g. a suggestion-deleted node): apply nothing — revert the
2040
- // whole change and return its inverse (against the rendered state the caller addressed)
2041
- const inv = delta.inverse(d, /** @type {any} */ (this.toDeltaDeep({ renderer })))
2042
- return inv.isEmpty() ? null : /** @type {any} */ (inv)
2043
- }
2044
- return null // invisible deleted type: the caller's view shows nothing here — silently drop
2045
- }
2046
- // @todo this was moved here from ytext. Make this more generic
2047
- return transact(this.doc, transaction => {
2048
- /**
2049
- * The accumulated fix. Its coordinates live in the caller's *expected* space, so they are
2050
- * tracked from `d`'s own ops (`expectedIndex`) — not `currPos.index`, which also counts
2051
- * content that a delete over attributed-deleted ranges leaves rendered.
2052
- *
2053
- * @type {delta.DeltaBuilder<any>?}
2054
- */
2055
- let fix = null
2056
- let fixLen = 0
2057
- let expectedIndex = 0
2058
- /**
2059
- * @param {delta.DeltaAny?} childFix
2060
- * @param {{ [k:string]: any }} [invFormat]
2061
- */
2062
- const appendModifyFix = (childFix, invFormat) => {
2063
- const f = fix ?? (fix = /** @type {any} */ (delta.create()))
2064
- expectedIndex > fixLen && f.retain(expectedIndex - fixLen)
2065
- f.modify(/** @type {any} */ (childFix ?? delta.create().done(false)), invFormat)
2066
- fixLen = expectedIndex + 1
2067
- }
2068
- const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), renderer)
2069
- /**
2070
- * Seed the cursor from a format-aware search marker when the first op
2071
- * retains a distant position. When a marker at-or-left of the target
2072
- * carries a formats snapshot the cursor can start there directly, and
2073
- * the leading retain is shortened by the seeded index — the only op
2074
- * needing adjustment, since every later op is position-relative anyway.
2075
- * Seeding is restricted to `renderer === null`, where
2076
- * `rendererContentLength` counts countable/non-deleted length — exactly
2077
- * the space `marker.index` records.
2078
- */
2079
- let firstRetainAdjust = 0
2080
- const op0 = d.children[Symbol.iterator]().next().value
2081
- if (
2082
- // `op0.format == null && op0.attribution == null`: only a *pure*
2083
- // positioning retain may be shortened (lib0's `_isPlainRetain`
2084
- // convention) — a retain carrying formats/attribution applies them
2085
- // across its whole traversed range, so skipping its head would
2086
- // silently drop that work for `[0, seededIndex)`.
2087
- renderer === null && op0 !== undefined && delta.$retainOp.check(op0) && op0.retain > 0 &&
2088
- op0.format == null && op0.attribution == null && this._searchMarker !== null
2089
- ) {
2090
- // Best format-aware checkpoint at-or-left of the retained position.
2091
- // A marker anchored *right* of the target can still seed if the gap is
2092
- // crossed by stepping left over countable/non-deleted items only —
2093
- // format state doesn't change across pure content (a format marker or
2094
- // tombstone in the gap stops the walk and rejects the marker). Anchor
2095
- // linkage is verified because records can outlive their items.
2096
- let best = null
2097
- let bestP = null
2098
- let bestIndex = 0
2099
- for (let i = 0; i < this._searchMarker.length; i++) {
2100
- const m = this._searchMarker[i]
2101
- if (m.formats === null || m.index <= 0) continue
2102
- // step left over pure content until at-or-below the target
2103
- let p = m.p
2104
- let idx = m.index
2105
- let steps = 0
2106
- while (idx > op0.retain && p.left !== null && !p.left.deleted && p.left.countable && steps < 64) {
2107
- p = p.left
2108
- idx -= p.length
2109
- steps++
2110
- }
2111
- if (
2112
- idx <= op0.retain && (best === null || idx > bestIndex) &&
2113
- (p.left === null ? this._start === p : p.left.right === p) &&
2114
- (p.right === null || p.right.left === p) &&
2115
- // `p` must be the *first* item at its index: upstream's walk
2116
- // stops on the first unprocessed item at the target — including
2117
- // format markers and deleted items sharing the index — so an
2118
- // anchor after same-index non-countable/deleted items would seed
2119
- // a different list position (correct state, wrong item order).
2120
- (p.left === null || (!p.left.deleted && p.left.countable))
2121
- ) {
2122
- best = m
2123
- bestP = p
2124
- bestIndex = idx
2125
- }
2126
- }
2127
- if (best !== null) {
2128
- currPos.left = (/** @type {Item} */ (bestP)).left
2129
- currPos.right = bestP
2130
- currPos.index = bestIndex
2131
- currPos.currentFormats = map.copy(/** @type {Map<string,any>} */ (best.formats))
2132
- firstRetainAdjust = bestIndex
2133
- }
2134
- }
2135
- for (const op of d.children) {
2136
- if (delta.$textOp.check(op)) {
2137
- insertContent(transaction, /** @type {any} */ (this), currPos, new ContentString(op.insert), op.format || {})
2138
- expectedIndex += op.length
2139
- } else if (delta.$insertOp.check(op)) {
2140
- insertContentHelper(transaction, this, currPos, op.insert, op.format || {})
2141
- expectedIndex += op.length
2142
- } else if (delta.$retainOp.check(op)) {
2143
- currPos.formatText(transaction, /** @type {any} */ (this), op.retain - firstRetainAdjust, op.format || {})
2144
- firstRetainAdjust = 0
2145
- expectedIndex += op.length
2146
- // quiescent point at a positioning boundary — plant a marker so a
2147
- // subsequent delta targeting this position can seed its cursor
2148
- if (renderer === null) plantMarker(/** @type {any} */ (this), currPos)
2149
- } else if (delta.$deleteOp.check(op)) {
2150
- deleteText(transaction, currPos, op.delete)
2151
- } else if (delta.$modifyOp.check(op)) {
2152
- let item = currPos.right
2153
- while (item !== null && rendererContentLength(renderer, item) === 0) { item = item.right }
2154
- if (item == null || item.content.constructor !== ContentType) { error.unexpectedCase() }
2155
- if (item.deleted) {
2156
- // deleted but rendered: revert instead of apply. Advance the cursor first (populating
2157
- // `currentFormats` with any markers up to the node) without applying `op.format`, then
2158
- // recurse — the child's deleted-guard applies nothing and returns the inverse.
2159
- currPos.formatText(transaction, /** @type {any} */ (this), 1, {})
2160
- /** @type {{ [k:string]: any }|undefined} */
2161
- let invFormat
2162
- for (const k in op.format) {
2163
- (invFormat ?? (invFormat = {}))[k] = currPos.currentFormats.get(k) ?? null
2164
- }
2165
- const childFix = /** @type {ContentType} */ (item.content).type.applyDelta(op.value, origin, { renderer })
2166
- if (childFix !== null || invFormat !== undefined) {
2167
- appendModifyFix(childFix, invFormat)
2168
- }
2169
- } else {
2170
- const childFix = /** @type {ContentType} */ (item.content).type.applyDelta(op.value, origin, { renderer })
2171
- currPos.formatText(transaction, /** @type {any} */ (this), 1, op.format || {})
2172
- if (childFix !== null) {
2173
- appendModifyFix(childFix)
2174
- }
2175
- }
2176
- expectedIndex += 1
2177
- } else {
2178
- error.unexpectedCase()
2179
- }
2180
- }
2181
- // Leave a format-aware marker at the deepest position this delta
2182
- // reached — subsequent deltas targeting nearby positions seed their
2183
- // cursor from it.
2184
- if (renderer === null) plantMarker(/** @type {any} */ (this), currPos)
2185
- for (const op of d.attrs) {
2186
- if (delta.$setAttrOp.check(op)) {
2187
- nodeMapSet(transaction, /** @type {any} */ (this), /** @type {any} */ (op.key), op.value)
2188
- } else if (delta.$deleteAttrOp.check(op)) {
2189
- nodeMapDelete(transaction, /** @type {any} */ (this), /** @type {any} */ (op.key))
2190
- } else {
2191
- // modifyAttr — locate the target renderer-aware: a deleted map value may still be rendered
2192
- const mapItem = this._map.get(/** @type {any} */ (op.key))
2193
- const sub = mapItem === undefined
2194
- ? undefined
2195
- : (mapItem.deleted
2196
- ? (mapItem.content.constructor === ContentType && rendererContentLength(renderer, mapItem) > 0
2197
- ? /** @type {ContentType} */ (mapItem.content).type
2198
- : undefined)
2199
- : mapItem.content.getContent()[mapItem.length - 1])
2200
- if (!(sub instanceof YNode)) {
2201
- error.unexpectedCase()
2202
- }
2203
- const subFix = sub.applyDelta(op.value, origin, { renderer })
2204
- if (subFix !== null) {
2205
- const f = fix ?? (fix = /** @type {any} */ (delta.create()))
2206
- f.modifyAttr(/** @type {any} */ (op.key), /** @type {any} */ (subFix))
2207
- }
2208
- }
2209
- }
2210
- return fix !== null && !(/** @type {delta.DeltaBuilder<any>} */ (fix).done(false).isEmpty()) ? fix : null
2211
- }, origin)
2212
- }
2213
-
2214
- /**
2215
- * Makes a copy of this data type that can be included somewhere else.
2216
- *
2217
- * Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
2218
- *
2219
- * @return {YNode<DConf>}
2220
- */
2221
- clone () {
2222
- const cpy = this._copy()
2223
- cpy.applyDelta(this.toDeltaDeep())
2224
- return cpy
2225
- }
2226
-
2227
- /**
2228
- * Removes all elements from this YMap.
2229
- */
2230
- clearAttrs () {
2231
- const d = delta.create()
2232
- this.forEachAttr((_, key) => {
2233
- d.deleteAttr(/** @type {any} */ (key))
2234
- })
2235
- this.applyDelta(d)
2236
- }
2237
-
2238
- /**
2239
- * Removes an attribute from this YXmlElement.
2240
- *
2241
- * @param {string} attributeName The attribute name that is to be removed.
2242
- *
2243
- * @public
2244
- */
2245
- deleteAttr (attributeName) {
2246
- this.applyDelta(delta.create().deleteAttr(attributeName).done())
2247
- }
2248
-
2249
- /**
2250
- * Sets or updates an attribute.
2251
- *
2252
- * @template {Exclude<keyof delta.DeltaConfGetAttrs<DConf>,symbol>} KEY
2253
- * @template {delta.DeltaConfGetAttrs<DConf>[KEY]} VAL
2254
- *
2255
- * @param {KEY} attributeName The attribute name that is to be set.
835
+ * @template VAL
836
+ * @param {string} attributeName The attribute name that is to be set.
2256
837
  * @param {VAL} attributeValue The attribute value that is to be set.
2257
838
  * @return {VAL}
2258
- *
2259
- * @public
2260
839
  */
2261
840
  setAttr (attributeName, attributeValue) {
2262
- this.applyDelta(delta.create().setAttr(attributeName, attributeValue).done())
841
+ if (this.doc == null) {
842
+ prelimOf(this).attrs[attributeName] = { value: attributeValue }
843
+ } else if (this._item === null || !this._item.deleted) {
844
+ transact(this.doc, transaction => nodeMapSet(transaction, this, attributeName, /** @type {any} */ (attributeValue)))
845
+ }
2263
846
  return attributeValue
2264
847
  }
2265
848
 
2266
849
  /**
2267
850
  * Returns an attribute value that belongs to the attribute name.
2268
851
  *
2269
- * @template {Exclude<keyof delta.DeltaConfGetAttrs<DConf>,symbol|number>} KEY
2270
- * @param {KEY} attributeName The attribute name that identifies the queried value.
2271
- * @return {delta.DeltaConfGetAttrs<DConf>[KEY]|undefined} The queried attribute value.
2272
- * @public
852
+ * @param {string} attributeName The attribute name that identifies the queried value.
853
+ * @return {any} The queried attribute value.
2273
854
  */
2274
855
  getAttr (attributeName) {
2275
- return /** @type {any} */ (nodeMapGet(this, attributeName))
2276
- }
2277
-
2278
- /**
2279
- * Returns whether an attribute exists
2280
- *
2281
- * @param {string} attributeName The attribute name to check for existence.
2282
- * @return {boolean} whether the attribute exists.
2283
- *
2284
- * @public
2285
- */
2286
- hasAttr (attributeName) {
2287
- return /** @type {any} */ (nodeMapHas(this, attributeName))
2288
- }
2289
-
2290
- /**
2291
- * Returns all attribute name/value pairs in a JSON Object.
2292
- *
2293
- * @return {{ [Key in Extract<keyof delta.DeltaConfGetAttrs<DConf>,string>]?: delta.DeltaConfGetAttrs<DConf>[Key]}} A JSON Object that describes the attributes.
2294
- *
2295
- * @public
2296
- */
2297
- getAttrs () {
2298
- return /** @type {any} */ (nodeMapGetAll(this))
856
+ const val = this._map.get(attributeName)
857
+ return val !== undefined && !val.deleted ? val.content.getContent()[val.length - 1] : undefined
2299
858
  }
2300
859
 
2301
860
  /**
2302
- * Inserts new content at an index.
2303
- *
2304
- * Important: This function expects an array of content. Not just a content
2305
- * object. The reason for this "weirdness" is that inserting several elements
2306
- * is very efficient when it is done as a single operation.
2307
- *
2308
- * @example
2309
- * // Insert character 'a' at position 0
2310
- * yarray.insert(0, ['a'])
2311
- * // Insert numbers 1, 2 at position 1
2312
- * yarray.insert(1, [1, 2])
861
+ * Inserts new content at an index — a string, or an array of elements (JSON values, nodes) —
862
+ * as `insertInGap` places it (YP19: one sequence write). A deleted node is left as it is.
2313
863
  *
2314
864
  * @param {number} index The index to insert content at.
2315
- * @param {Array<delta.DeltaConfGetChildren<DConf>>|delta.DeltaConfGetText<DConf>} content Array of content to append.
2316
- * @param {delta.Formats} [format]
865
+ * @param {Array<any>|string} content
2317
866
  */
2318
- insert (index, content, format) {
2319
- this.applyDelta(delta.create().retain(index).insert(/** @type {any} */ (content), format).done())
867
+ insert (index, content) {
868
+ if ((typeof content !== 'string' && !Array.isArray(content)) || content.length === 0) return
869
+ if (this.doc == null) {
870
+ const runs = prelimOf(this).runs
871
+ runs.splice(splitRuns(runs, index), 0, content.slice())
872
+ mergeRuns(runs)
873
+ return
874
+ }
875
+ if (this._item !== null && this._item.deleted) return
876
+ this.insertInGap(index, content)
2320
877
  }
2321
878
 
2322
879
  // YP7 begin (edytor fork: insertAtGapEnd — see UPSTREAM.md YP7)
@@ -2350,9 +907,8 @@ export class YNode extends ObservableV2 {
2350
907
  insertInGap (index, content) {
2351
908
  if (this.doc == null) throw new Error('insertInGap: node is not integrated')
2352
909
  transact(this.doc, transaction => {
2353
- const parts = typeof content === 'string' ? [new ContentString(content)] : contentsOf(content)
2354
910
  let at = index
2355
- for (const part of parts) {
911
+ for (const part of contentsOf(content)) {
2356
912
  insertInGapHelper(transaction, this, at, part)
2357
913
  at += part.getLength()
2358
914
  }
@@ -2382,408 +938,73 @@ export class YNode extends ObservableV2 {
2382
938
  // YP13 end
2383
939
 
2384
940
  /**
2385
- * Inserts new content at an index.
2386
- *
2387
- * Important: This function expects an array of content. Not just a content
2388
- * object. The reason for this "weirdness" is that inserting several elements
2389
- * is very efficient when it is done as a single operation.
2390
- *
2391
- * @example
2392
- * // Insert character 'a' at position 0
2393
- * yarray.insert(0, ['a'])
2394
- * // Insert numbers 1, 2 at position 1
2395
- * yarray.insert(1, [1, 2])
2396
- *
2397
- * @param {number} index The index to insert content at.
2398
- * @param {number} length The index to insert content at.
2399
- * @param {delta.Formats} formats
2400
- *
2401
- */
2402
- format (index, length, formats) {
2403
- this.applyDelta(delta.create().retain(index).retain(length, formats))
2404
- }
2405
-
2406
- /**
2407
- * Appends content to this YArray.
2408
- *
2409
- * @param {Array<delta.DeltaConfGetChildren<DConf>>|delta.DeltaConfGetText<DConf>} content Array of content to append.
2410
- *
2411
- * @todo Use the following implementation in all types.
2412
- */
2413
- push (content) {
2414
- this.insert(this.length, content)
2415
- }
2416
-
2417
- /**
2418
- * Prepends content to this YArray.
2419
- *
2420
- * @param {delta.DeltaConfGetText<DConf>} content Array of content to prepend.
2421
- */
2422
- unshift (content) {
2423
- this.insert(0, content)
2424
- }
2425
-
2426
- /**
2427
- * Deletes elements starting from an index.
941
+ * Deletes elements starting from an index. A deleted node is left as it is.
2428
942
  *
2429
943
  * @param {number} index Index at which to start deleting elements
2430
944
  * @param {number} length The number of elements to remove. Defaults to 1.
2431
945
  */
2432
946
  delete (index, length = 1) {
2433
- this.applyDelta(delta.create().retain(index).delete(length))
2434
- }
2435
-
2436
- /**
2437
- * Returns the i-th element from a YArray.
2438
- *
2439
- * @param {number} index The index of the element to return from the YArray
2440
- * @return {delta.DeltaConfGetChildren<DConf>}
2441
- */
2442
- get (index) {
2443
- return typeListGet(this, index)
2444
- }
2445
-
2446
- /**
2447
- * Returns a portion of this YXmlFragment into a JavaScript Array selected
2448
- * from start to end (end not included).
2449
- *
2450
- * @param {number} [start]
2451
- * @param {number} [end]
2452
- * @return {Array<delta.DeltaConfGetChildren<DConf>>}
2453
- */
2454
- slice (start = 0, end = this.length) {
2455
- return typeListSlice(this, start, end)
2456
- }
2457
-
2458
- /**
2459
- * @todo refactor this, this should use getContent only!
2460
- *
2461
- * Transforms this YArray to a JavaScript Array.
2462
- *
2463
- * @return {Array<delta.DeltaConfGetChildren<DConf> | delta.DeltaConfGetText<DConf>>}
2464
- */
2465
- toArray () {
2466
- const dcontent = this.toDelta()
2467
- /**
2468
- * @type {Array<any>}
2469
- */
2470
- const children = []
2471
- for (const child of dcontent.children) {
2472
- if (delta.$insertOp.check(child)) {
2473
- for (let i = 0; i < child.insert.length; i++) {
2474
- children.push(child.insert[i])
2475
- }
2476
- } else if (delta.$textOp.check(child)) {
2477
- children.push(child.insert)
947
+ if (!(length > 0)) return
948
+ if (this.doc == null) {
949
+ const runs = prelimOf(this).runs
950
+ let n = length
951
+ for (let i = splitRuns(runs, index); i < runs.length && n > 0; i++) {
952
+ const cut = Math.min(n, runs[i].length)
953
+ runs[i] = runs[i].slice(cut)
954
+ n -= cut
2478
955
  }
956
+ mergeRuns(runs)
957
+ return
2479
958
  }
2480
- return children
2481
- }
2482
-
2483
- /**
2484
- * Transforms this Shared Type to a JSON object.
2485
- * @return {{ name?: string, attrs?: { [K:string|number]: any }, children?: Array<any> }}
2486
- */
2487
- toJSON () {
2488
- /**
2489
- * @type {{[K:string]:any}}
2490
- */
2491
- const attrs = this.getAttrs()
2492
- for (const k in attrs) {
2493
- const attr = attrs[k]
2494
- attrs[k] = attr instanceof YNode ? attr.toJSON() : attr
2495
- }
2496
- const children = this.toArray().map(child => child instanceof YNode ? /** @type {any} */ (child.toJSON()) : child)
2497
- /**
2498
- * @type {any}
2499
- */
2500
- const res = {}
2501
- if (this.name != null) {
2502
- res.name = this.name
2503
- }
2504
- if (this.length > 0) {
2505
- res.children = children
2506
- }
2507
- if (this.attrSize > 0) {
2508
- res.attrs = attrs
2509
- }
2510
- return res
2511
- }
2512
-
2513
- /**
2514
- * @param {object} opts
2515
- * @param {boolean} [opts.forceTag] enforce creating a surrouning <name /> tag, even if it is null.
2516
- */
2517
- toString ({ forceTag = false } = {}) {
2518
- /**
2519
- * @type {Array<[string|number,string]>}
2520
- */
2521
- const attrs = []
2522
- this.forEachAttr((attr, key) => {
2523
- attrs.push([(key), /** @type {any} */ (attr) instanceof YNode ? attr.toString({ forceTag: true }) : JSON.stringify(attr)])
959
+ if (this._item !== null && this._item.deleted) return
960
+ transact(this.doc, transaction => {
961
+ const currPos = cursorAt(transaction, this, index)
962
+ deleteText(transaction, currPos, length)
963
+ plantMarker(this, currPos)
2524
964
  })
2525
- const attrsString = (attrs.length > 0 ? ' ' : '') + attrs.sort((a, b) => a[0].toString() < b[0].toString() ? -1 : 1).map(attr => attr[0] + '=' + attr[1]).join(' ')
2526
- /**
2527
- * @type {string}
2528
- */
2529
- const children = this.toArray().map(c => s.$string.check(c) ? c : (c instanceof YNode ? c.toString({ forceTag: true }) : JSON.stringify(c))).join('')
2530
- if (this.name == null && !forceTag && attrs.length === 0) {
2531
- return children
2532
- }
2533
- if (this.length === 0) {
2534
- return `<${this.name ?? ''}${attrsString} />`
2535
- }
2536
- return `<${this.name ?? ''}${attrsString}>${children}</${this.name ?? ''}>`
2537
- }
2538
-
2539
- /**
2540
- * Returns an Array with the result of calling a provided function on every
2541
- * child-element.
2542
- *
2543
- * @template M
2544
- * @param {(child:delta.DeltaConfGetChildren<DConf>|delta.DeltaConfGetText<DConf>,index:number)=>M} f Function that produces an element of the new Array
2545
- * @return {Array<M>} A new array with each element being the result of the
2546
- * callback function
2547
- */
2548
- map (f) {
2549
- return this.toArray().map(f)
2550
- }
2551
-
2552
- /**
2553
- * Executes a provided function once on every element of this YArray.
2554
- *
2555
- * @param {(child:delta.DeltaConfGetChildren<DConf>|delta.DeltaConfGetText<DConf>,index:number)=>any} f Function that produces an element of the new Array
2556
- */
2557
- forEach (f) {
2558
- return this.toArray().forEach(f)
2559
965
  }
2560
966
 
2561
967
  /**
2562
968
  * Executes a provided function on once on every key-value pair.
2563
969
  *
2564
- * @param {(val:delta.DeltaConfGetAttrs<DConf>[any],key:Exclude<keyof delta.DeltaConfGetAttrs<DConf>,symbol>,ynode:this)=>any} f
970
+ * @param {(val:any,key:string,ynode:this)=>any} f
2565
971
  */
2566
972
  forEachAttr (f) {
2567
973
  this._map.forEach((item, key) => {
2568
974
  if (!item.deleted) {
2569
- f(item.content.getContent()[item.length - 1], /** @type {any} */ (key), this)
975
+ f(item.content.getContent()[item.length - 1], key, this)
2570
976
  }
2571
977
  })
2572
978
  }
2573
979
 
2574
980
  /**
2575
- * Returns the keys for each element in the YMap Type.
981
+ * The keys of the live attributes.
2576
982
  *
2577
- * @return {IterableIterator<import('lib0-v14/ts').KeyOf<delta.DeltaConfGetAttrs<DConf>>>}
983
+ * @return {IterableIterator<string>}
2578
984
  */
2579
- attrKeys () {
2580
- return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[0])
2581
- }
2582
-
2583
- /**
2584
- * Returns the values for each element in the YMap Type.
2585
- *
2586
- * @return {IterableIterator<delta.DeltaConfGetAttrs<DConf>[any]>}
2587
- */
2588
- attrValues () {
2589
- return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1])
2590
- }
2591
-
2592
- /**
2593
- * Returns an Iterator of [key, value] pairs
2594
- *
2595
- * @return {IterableIterator<{ [K in keyof delta.DeltaConfGetAttrs<DConf>]: [K,delta.DeltaConfGetAttrs<DConf>[K]] }[any]>}
2596
- */
2597
- attrEntries () {
2598
- return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]]))
2599
- }
2600
-
2601
- /**
2602
- * Returns the number of stored attributes (count of key/value pairs)
2603
- *
2604
- * @return {number}
2605
- */
2606
- get attrSize () {
2607
- return [...createMapIterator(this)].length
2608
- }
2609
-
2610
- /**
2611
- * @param {this} other
2612
- */
2613
- [traits.EqualityTraitSymbol] (other) {
2614
- return this.toDelta().equals(other.toDelta())
985
+ * attrKeys () {
986
+ for (const [key, item] of this._map) {
987
+ if (!item.deleted) yield key
988
+ }
2615
989
  }
2616
990
 
2617
991
  /**
2618
- * @todo this doesn't need to live in a method.
2619
- *
2620
- * Transform the properties of this type to binary and write it to an
2621
- * BinaryEncoder.
992
+ * Write this node's type reference (and name), sent with the item that holds it.
2622
993
  *
2623
- * This is called when this Item is sent to a remote peer.
2624
- *
2625
- * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
994
+ * @param {UpdateEncoder} encoder The encoder to write data to.
2626
995
  */
2627
996
  _write (encoder) {
2628
997
  encoder.writeTypeRef(this._legacyTypeRef)
2629
998
  switch (this._legacyTypeRef) {
2630
999
  case YXmlElementRefID:
2631
1000
  case YXmlHookRefID: {
2632
- encoder.writeKey(this.name)
1001
+ encoder.writeKey(/** @type {string} */ (this.name))
2633
1002
  break
2634
1003
  }
2635
1004
  }
2636
1005
  }
2637
1006
  }
2638
1007
 
2639
- /**
2640
- * Schema of a {@link YNode} with any delta configuration. This is where the nominal `y:node` tag
2641
- * lives - {@link $node} builds on it. Mirrors lib0's `$deltaAny` / `$delta` split.
2642
- */
2643
- export const $nodeAny = /** @type {s.Schema<YNode<any>>} */ (YNode.prototype.$type = s.$type('y:node', YNode))
2644
-
2645
- /**
2646
- * @param {StructStore} store
2647
- * @param {IdSet} items
2648
- */
2649
- export const computeModifiedFromItems = (store, items) => {
2650
- /**
2651
- * @type {Map<YNode,Set<null|string>>}
2652
- */
2653
- const modified = new Map()
2654
- iterateStructsByIdSetWithoutSplits(store, items, /** @param {Item | GC | Skip | null} item */ item => {
2655
- while (item instanceof Item) {
2656
- const parent = /** @type {YNode} */ (item.parent)
2657
- const conf = map.setIfUndefined(modified, parent, set.create)
2658
- if (conf.has(item.parentSub)) break // has already been marked as modified
2659
- conf.add(item.parentSub)
2660
- item = parent._item
2661
- }
2662
- })
2663
- return modified
2664
- }
2665
-
2666
- /**
2667
- * The active renderer's attribution overlay changed (e.g. a suggestion was accepted or rejected)
2668
- * without a Y transaction on the type's doc, so no `YEvent` fires. Re-render the affected id
2669
- * ranges and keep the RDT surface current, mirroring the per-transaction upkeep in
2670
- * `cleanupTransactions`: patch the maintained {@link YNode#delta} cache and emit the change on
2671
- * the `'delta'` channel. Wired to the renderer's `'change'` event by {@link YNode#useRenderer}.
2672
- *
2673
- * Ranges in `changes` that the doc has not (yet) integrated — e.g. a base-doc edit whose update
2674
- * flows in only after the renderer event — simply render to nothing here; the later transaction
2675
- * covers them through the regular event path.
2676
- *
2677
- * The cache patch must stay on the builder `apply` — see the {@link YNode#_delta}
2678
- * fingerprint-memo invariant.
2679
- *
2680
- * @param {YNode<any>} type
2681
- * @param {IdSet} changes
2682
- * @param {any} origin
2683
- *
2684
- * @private
2685
- * @function
2686
- */
2687
- export const typeApplyRendererChange = (type, changes, origin) => {
2688
- const hasDeltaListeners = (type._observers.get('delta')?.size ?? 0) > 0
2689
- // no deleted-type guard: this handler only exists while a custom renderer is attached (see
2690
- // `useRenderer`), and a custom renderer may still render a deleted type — its cache and 'delta'
2691
- // channel must stay current through accept/reject overlay changes.
2692
- if ((type._delta === null && !hasDeltaListeners) || type.doc == null) return
2693
- const change = type.toDelta({ renderer: type._renderer, deep: true, itemsToRender: changes, retainInserts: true, retainDeletes: true })
2694
- if (!change.isEmpty()) {
2695
- type._delta?.apply(change)
2696
- if (hasDeltaListeners) type.emit('delta', [change, origin])
2697
- }
2698
- }
2699
-
2700
- /**
2701
- * @param {any} a
2702
- * @param {any} b
2703
- * @return {boolean}
2704
- */
2705
- export const equalFormats = (a, b) => a === b || (typeof a === 'object' && typeof b === 'object' && a && b && object.equalFlat(a, b))
2706
-
2707
- /**
2708
- * @template {delta.DeltaConf} DConf
2709
- * @typedef {delta.DeltaConfOverwrite<DConf, {
2710
- * attrs: { [K in keyof delta.DeltaConfGetAttrs<DConf>]: DeltaToYNode<delta.DeltaConfGetAttrs<DConf>[K]> },
2711
- * children: DeltaToYNode<delta.DeltaConfGetChildren<DConf>>
2712
- * }>
2713
- * } DeltaConfDeltaToYNode
2714
- */
2715
-
2716
- /**
2717
- * @template {any} Data
2718
- * @typedef {Exclude<Data,delta.DeltaAny> | (Extract<Data,delta.DeltaAny> extends delta.Delta<infer DConf> ? (unknown extends DConf ? YNode<DConf> : never) : never)} DeltaToYNode
2719
- */
2720
-
2721
- /**
2722
- * @param {YNode<any>} type
2723
- * @param {number} start
2724
- * @param {number} end
2725
- * @return {Array<any>}
2726
- *
2727
- * @private
2728
- * @function
2729
- */
2730
- export const typeListSlice = (type, start, end) => {
2731
- type.doc ?? warnPrematureAccess()
2732
- if (start < 0) {
2733
- start = type._length + start
2734
- }
2735
- if (end < 0) {
2736
- end = type._length + end
2737
- }
2738
- let len = end - start
2739
- const cs = []
2740
- let n = type._start
2741
- while (n !== null && len > 0) {
2742
- if (n.countable && !n.deleted) {
2743
- const c = n.content.getContent()
2744
- if (c.length <= start) {
2745
- start -= c.length
2746
- } else {
2747
- for (let i = start; i < c.length && len > 0; i++) {
2748
- cs.push(c[i])
2749
- len--
2750
- }
2751
- start = 0
2752
- }
2753
- }
2754
- n = n.right
2755
- }
2756
- return cs
2757
- }
2758
-
2759
- /**
2760
- * @todo remove / inline this
2761
- *
2762
- * @param {YNode} type
2763
- * @param {number} index
2764
- * @return {any}
2765
- *
2766
- * @private
2767
- * @function
2768
- */
2769
- export const typeListGet = (type, index) => {
2770
- type.doc ?? warnPrematureAccess()
2771
- const marker = findMarker(type, index)
2772
- let n = type._start
2773
- if (marker !== null) {
2774
- n = marker.p
2775
- index -= marker.index
2776
- }
2777
- for (; n !== null; n = n.right) {
2778
- if (!n.deleted && n.countable) {
2779
- if (index < n.length) {
2780
- return n.content.getContent()[index]
2781
- }
2782
- index -= n.length
2783
- }
2784
- }
2785
- }
2786
-
2787
1008
  /**
2788
1009
  * @todo inline this code
2789
1010
  *
@@ -2832,9 +1053,7 @@ export const nodeMapSet = (transaction, parent, key, value) => {
2832
1053
  content = new ContentBinary(/** @type {Uint8Array} */ (value))
2833
1054
  break
2834
1055
  default:
2835
- if ($doc.check(value)) {
2836
- content = createContentDocFromDoc(/** @type {Doc} */ (value))
2837
- } else if (value instanceof YNode) {
1056
+ if (value instanceof YNode) {
2838
1057
  content = new ContentType(/** @type {any} */ (value))
2839
1058
  } else {
2840
1059
  throw new Error('Unexpected content type')
@@ -2844,162 +1063,10 @@ export const nodeMapSet = (transaction, parent, key, value) => {
2844
1063
  new Item(createID(ownClientId, doc.store.getClock(ownClientId)), left, left && left.lastId, null, null, parent, key, content).integrate(transaction, 0)
2845
1064
  }
2846
1065
 
2847
- /**
2848
- * @param {YNode<any>} parent
2849
- * @param {string} key
2850
- * @return {Object<string,any>|number|null|Array<any>|string|Uint8Array|YNode<any>|undefined}
2851
- *
2852
- * @private
2853
- * @function
2854
- */
2855
- export const nodeMapGet = (parent, key) => {
2856
- parent.doc ?? warnPrematureAccess()
2857
- const val = parent._map.get(key)
2858
- return val !== undefined && !val.deleted ? val.content.getContent()[val.length - 1] : undefined
2859
- }
2860
-
2861
- /**
2862
- * @param {YNode<any>} parent
2863
- * @return {Object<string,Object<string,any>|number|null|Array<any>|string|Uint8Array|YNode<any>|undefined>}
2864
- *
2865
- * @private
2866
- * @function
2867
- */
2868
- export const nodeMapGetAll = (parent) => {
2869
- /**
2870
- * @type {Object<string,any>}
2871
- */
2872
- const res = {}
2873
- parent.doc ?? warnPrematureAccess()
2874
- parent._map.forEach((value, key) => {
2875
- if (!value.deleted) {
2876
- res[key] = value.content.getContent()[value.length - 1]
2877
- }
2878
- })
2879
- return res
2880
- }
2881
-
2882
- /**
2883
- * @todo move this to getContent/getDelta
2884
- *
2885
- * Render the difference to another ydoc (which can be empty) and highlight the differences with
2886
- * attributions.
2887
- *
2888
- * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
2889
- * attribution `{ isDeleted: true, .. }`.
2890
- *
2891
- * @template {delta.DeltaBuilderAny} TypeDelta
2892
- * @param {TypeDelta} d
2893
- * @param {YNode} parent
2894
- * @param {Set<string|null>?} attrsToRender
2895
- * @param {AbstractRenderer?} renderer
2896
- * @param {boolean} deep
2897
- * @param {Set<YNode>|Map<YNode,any>|null} [modified] - set of types that should be rendered as modified children
2898
- * @param {IdSet?} [itemsToRender]
2899
- * @param {any} [opts]
2900
- * @param {any} [optsAll]
2901
- *
2902
- * @private
2903
- * @function
2904
- */
2905
- export const nodeMapGetDelta = (d, parent, attrsToRender, renderer, deep, modified, itemsToRender, opts, optsAll) => {
2906
- // @todo support modified ops!
2907
- /**
2908
- * @param {Item} item
2909
- * @param {string} key
2910
- */
2911
- const renderAttrs = (item, key) => {
2912
- /**
2913
- * @type {{ deleted: boolean, attrs: Array<ContentAttribute<any>>?, content: AbstractContent }}
2914
- */
2915
- let piece
2916
- if (renderer === null || !renderer.hasItem(item)) {
2917
- // generic fast path — the item's own fields, no renderer involved
2918
- piece = { deleted: item.deleted, attrs: null, content: item.content }
2919
- } else {
2920
- /**
2921
- * @type {Array<AttributedContent<any>>}
2922
- */
2923
- const cs = []
2924
- renderer.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1)
2925
- if (cs.length === 0) return // the renderer surfaces nothing for this attribute (e.g. a diff renderer hiding an unchanged delete)
2926
- piece = cs[cs.length - 1]
2927
- }
2928
- const { deleted, attrs, content } = piece
2929
- const attribution = createAttributionFromAttributionItems(attrs, deleted)
2930
- let c = array.last(content.getContent())
2931
- if (deleted) {
2932
- if (attribution != null) {
2933
- // Item surfaced under attribution (suggestion view / diff renderer, either in snapshot mode
2934
- // or in an event-driven render). The attribute is still observable in the rendered state, so
2935
- // emit a positive `SetAttrOp` carrying the attribution metadata - matching how content
2936
- // children are rendered for the same case (positive `InsertOp` with attribution, never
2937
- // `DeleteOp`).
2938
- // also re-emit when the change happened *inside* the still-rendered value (`modified`
2939
- // contains the value type but the attr's own map item is not part of the change) — the
2940
- // deleted value has no modifyAttr path, and a full-state `setAttr` replace is idempotent
2941
- if (itemsToRender == null || itemsToRender.hasId(item.lastId) || (c instanceof YNode && modified != null && modified.has(c))) {
2942
- if (deep && c instanceof YNode) {
2943
- // full-state value render: a positive `setAttr` *replaces* the attr value on the
2944
- // consuming side, so the nested type must render as its full attributed state
2945
- // (change-scoped opts like `itemsToRender` would render bare retains here)
2946
- c = /** @type {any} */(c).toDelta({ renderer, deep: true })
2947
- }
2948
- d.setAttr(key, c, attribution)
2949
- }
2950
- } else if (itemsToRender != null && itemsToRender.hasId(item.lastId)) {
2951
- // Hard-deleted attribute within a change render: emit the `deleteAttr` op so consumers (the
2952
- // `YEvent` delta, RDT bindings, the maintained `delta` cache) can apply the removal. In
2953
- // full-state mode (`itemsToRender == null`) the attribute is simply omitted (above renders
2954
- // run with `render === false` for such items, so nothing was emitted before either).
2955
- d.deleteAttr(key, attribution)
2956
- }
2957
- } else if (deep && c instanceof YNode && modified?.has(c)) {
2958
- d.modifyAttr(key, c.toDelta(opts))
2959
- } else {
2960
- if (deep && c instanceof YNode) {
2961
- c = /** @type {any} */(c).toDelta(optsAll)
2962
- }
2963
- d.setAttr(key, c, attribution)
2964
- }
2965
- }
2966
- if (attrsToRender == null) {
2967
- parent._map.forEach(renderAttrs)
2968
- } else {
2969
- attrsToRender.forEach(key => key != null && renderAttrs(/** @type {Item} */ (parent._map.get(key)), key))
2970
- }
2971
- }
2972
-
2973
- /**
2974
- * @param {YNode<any>} parent
2975
- * @param {string} key
2976
- * @return {boolean}
2977
- *
2978
- * @private
2979
- * @function
2980
- */
2981
- export const nodeMapHas = (parent, key) => {
2982
- parent.doc ?? warnPrematureAccess()
2983
- const val = parent._map.get(key)
2984
- return val !== undefined && !val.deleted
2985
- }
2986
-
2987
- /**
2988
- * @param {YNode<any> & { _map: Map<string, Item> }} type
2989
- * @return {IterableIterator<Array<any>>}
2990
- *
2991
- * @private
2992
- * @function
2993
- */
2994
- export const createMapIterator = type => {
2995
- type.doc ?? warnPrematureAccess()
2996
- return iterator.iteratorFilter(type._map.entries(), /** @param {any} entry */ entry => !entry[1].deleted)
2997
- }
2998
-
2999
1066
  /**
3000
1067
  * @private
3001
1068
  *
3002
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
1069
+ * @param {UpdateDecoder} decoder
3003
1070
  * @return {ContentType}
3004
1071
  */
3005
1072
  export const readContentType = decoder => new ContentType(readYNode(decoder))
@@ -3007,55 +1074,19 @@ export const readContentType = decoder => new ContentType(readYNode(decoder))
3007
1074
  /**
3008
1075
  * @private
3009
1076
  *
3010
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
1077
+ * @param {UpdateDecoder} decoder
3011
1078
  * @return {ContentString}
3012
1079
  */
3013
1080
  export const readContentString = decoder => new ContentString(decoder.readString())
3014
1081
 
3015
1082
  /**
3016
- * @private
3017
- *
3018
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
3019
- * @return {ContentJSON}
3020
- */
3021
- export const readContentJSON = decoder => {
3022
- const len = decoder.readLen()
3023
- const cs = []
3024
- for (let i = 0; i < len; i++) {
3025
- const c = decoder.readString()
3026
- if (c === 'undefined') {
3027
- cs.push(undefined)
3028
- } else {
3029
- cs.push(JSON.parse(c))
3030
- }
3031
- }
3032
- return new ContentJSON(cs)
3033
- }
3034
-
3035
- /**
3036
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
1083
+ * @param {UpdateDecoder} decoder
3037
1084
  * @return {ContentFormat}
3038
1085
  */
3039
1086
  export const readContentFormat = decoder => new ContentFormat(decoder.readKey(), decoder.readJSON())
3040
1087
 
3041
1088
  /**
3042
- * @private
3043
- *
3044
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
3045
- * @return {ContentEmbed}
3046
- */
3047
- export const readContentEmbed = decoder => new ContentEmbed(decoder.readJSON())
3048
-
3049
- /**
3050
- * @private
3051
- *
3052
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
3053
- * @return {ContentDoc}
3054
- */
3055
- export const readContentDoc = decoder => new ContentDoc(decoder.readString(), decoder.readAny())
3056
-
3057
- /**
3058
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
1089
+ * @param {UpdateDecoder} decoder
3059
1090
  * @return {ContentAny}
3060
1091
  */
3061
1092
  export const readContentAny = decoder => {
@@ -3068,7 +1099,7 @@ export const readContentAny = decoder => {
3068
1099
  }
3069
1100
 
3070
1101
  /**
3071
- * @param {UpdateDecoderV1 | UpdateDecoderV2 } decoder
1102
+ * @param {UpdateDecoder} decoder
3072
1103
  * @return {ContentBinary}
3073
1104
  */
3074
1105
  export const readContentBinary = decoder => new ContentBinary(decoder.readBuf())
@@ -3076,38 +1107,42 @@ export const readContentBinary = decoder => new ContentBinary(decoder.readBuf())
3076
1107
  /**
3077
1108
  * @private
3078
1109
  *
3079
- * @param {UpdateDecoderV1 | UpdateDecoderV2 } decoder
1110
+ * @param {UpdateDecoder} decoder
3080
1111
  * @return {ContentDeleted}
3081
1112
  */
3082
1113
  export const readContentDeleted = decoder => new ContentDeleted(decoder.readLen())
3083
1114
 
1115
+ const refused = () => { error.unexpectedCase() }
1116
+
3084
1117
  /**
3085
- * A lookup map for reading Item content.
1118
+ * A lookup map for reading Item content. YP19: JSON (2), embed (5) and
1119
+ * subdocument (9) contents are refused like GC (0) and Skip (10): edytor
1120
+ * writes none, so an update holding one is malformed.
3086
1121
  *
3087
- * @type {Array<function(UpdateDecoderV1 | UpdateDecoderV2):AbstractContent>}
1122
+ * @type {Array<function(UpdateDecoder):AbstractContent>}
3088
1123
  */
3089
1124
  export const contentRefs = [
3090
- () => { error.unexpectedCase() }, // GC is not ItemContent
1125
+ refused, // 0 - GC is not ItemContent
3091
1126
  readContentDeleted, // 1
3092
- readContentJSON, // 2
1127
+ refused, // 2 - JSON
3093
1128
  readContentBinary, // 3
3094
1129
  readContentString, // 4
3095
- readContentEmbed, // 5
1130
+ refused, // 5 - embed
3096
1131
  readContentFormat, // 6
3097
1132
  readContentType, // 7
3098
1133
  readContentAny, // 8
3099
- readContentDoc, // 9
3100
- () => { error.unexpectedCase() } // 10 - Skip is not ItemContent
1134
+ refused, // 9 - subdocument
1135
+ refused // 10 - Skip is not ItemContent
3101
1136
  ]
3102
1137
 
3103
1138
  /**
3104
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
1139
+ * @param {UpdateDecoder} decoder
3105
1140
  * @param {number} info
3106
1141
  */
3107
1142
  export const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
3108
1143
 
3109
1144
  /**
3110
- * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
1145
+ * @param {UpdateDecoder} decoder
3111
1146
  * @return {YNode}
3112
1147
  *
3113
1148
  * @private