edytor 1.0.0 → 3.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 (287) hide show
  1. package/README.md +26 -69
  2. package/dist/block/block.svelte.d.ts +6 -4
  3. package/dist/block/block.svelte.js +12 -8
  4. package/dist/block/inlineBlock.svelte.d.ts +6 -2
  5. package/dist/block/inlineBlock.svelte.js +7 -2
  6. package/dist/clipboard/htmlFlow.d.ts +20 -7
  7. package/dist/clipboard/htmlFlow.js +23 -15
  8. package/dist/clipboard/serializeClipboardFragment.d.ts +37 -4
  9. package/dist/clipboard/serializeClipboardFragment.js +6 -4
  10. package/dist/clipboard/textBlocks.d.ts +8 -0
  11. package/dist/clipboard/textBlocks.js +6 -2
  12. package/dist/cloudflare/room/admission.js +3 -3
  13. package/dist/cloudflare/room/context.d.ts +7 -1
  14. package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
  15. package/dist/cloudflare/routeDocumentSocket.js +53 -0
  16. package/dist/collaboration/awarenessSelection.d.ts +22 -1
  17. package/dist/collaboration/awarenessSelection.js +42 -0
  18. package/dist/collaboration/comments/client.d.ts +1 -1
  19. package/dist/collaboration/comments/client.js +1 -1
  20. package/dist/collaboration/remoteSelection.js +3 -3
  21. package/dist/components/Block.svelte +3 -1
  22. package/dist/components/Edytor.svelte +38 -20
  23. package/dist/components/Edytor.svelte.d.ts +9 -4
  24. package/dist/components/InlineBlock.svelte +1 -1
  25. package/dist/crdt/admission.d.ts +5 -10
  26. package/dist/crdt/admission.js +5 -11
  27. package/dist/crdt/data.d.ts +10 -0
  28. package/dist/crdt/data.js +20 -2
  29. package/dist/crdt/doc/funnel.d.ts +7 -6
  30. package/dist/crdt/doc/funnel.js +10 -9
  31. package/dist/crdt/doc/gate.d.ts +0 -17
  32. package/dist/crdt/doc/gate.js +2 -41
  33. package/dist/crdt/doc/history.js +2 -2
  34. package/dist/crdt/doc/reads.d.ts +7 -0
  35. package/dist/crdt/doc/reads.js +11 -1
  36. package/dist/crdt/document.d.ts +13 -14
  37. package/dist/crdt/document.js +17 -19
  38. package/dist/crdt/edytor-doc.d.ts +22 -4
  39. package/dist/crdt/edytor-doc.js +5 -8
  40. package/dist/crdt/index.d.ts +9 -3
  41. package/dist/crdt/index.js +2 -2
  42. package/dist/crdt/operations.d.ts +1 -1
  43. package/dist/crdt/operations.js +1 -0
  44. package/dist/crdt/props.d.ts +32 -0
  45. package/dist/crdt/props.js +172 -0
  46. package/dist/crdt/protocol.d.ts +2 -2
  47. package/dist/crdt/protocol.js +2 -2
  48. package/dist/crdt/providers/index.d.ts +1 -1
  49. package/dist/crdt/providers/room.d.ts +10 -9
  50. package/dist/crdt/providers/room.js +13 -10
  51. package/dist/crdt/providers/websocket.d.ts +1 -1
  52. package/dist/crdt/providers/websocket.js +28 -29
  53. package/dist/crdt/semantics.js +3 -1
  54. package/dist/dnd/autoScroll.d.ts +15 -7
  55. package/dist/dnd/autoScroll.js +10 -15
  56. package/dist/{plugins/blockHandles → dnd}/dropIndicator.d.ts +2 -2
  57. package/dist/{plugins/blockHandles → dnd}/dropIndicator.js +2 -5
  58. package/dist/dnd/external.d.ts +6 -0
  59. package/dist/dnd/external.js +17 -0
  60. package/dist/{plugins/blockHandles → dnd}/geometry.d.ts +4 -2
  61. package/dist/{plugins/blockHandles → dnd}/geometry.js +3 -4
  62. package/dist/dnd/placement.d.ts +200 -0
  63. package/dist/dnd/placement.js +426 -0
  64. package/dist/dnd/pragmatic.d.ts +3 -1
  65. package/dist/dnd/pragmatic.js +12 -2
  66. package/dist/dnd/vendor/pragmatic/UPSTREAM.md +51 -18
  67. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.d.ts +4 -0
  68. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.js +6 -0
  69. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +0 -2
  70. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +4 -22
  71. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter-types.d.ts +26 -0
  72. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.d.ts +30 -0
  73. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.js +203 -0
  74. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-items.js +13 -0
  75. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-types.js +9 -0
  76. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/is-an-available-type.js +19 -0
  77. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.d.ts +4 -0
  78. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.js +6 -0
  79. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +4 -16
  80. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +1 -8
  81. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +5 -6
  82. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +2 -3
  83. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.d.ts +1 -1
  84. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +6 -24
  85. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.d.ts +2 -0
  86. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.js +5 -0
  87. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.d.ts +3 -0
  88. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.js +12 -0
  89. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/native-types.d.ts +12 -0
  90. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window-in-safari.js +10 -0
  91. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window.js +55 -0
  92. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.d.ts +4 -0
  93. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.js +8 -0
  94. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +0 -4
  95. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +16 -72
  96. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +1 -9
  97. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +4 -29
  98. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +2 -3
  99. package/dist/edytor.svelte.d.ts +19 -9
  100. package/dist/edytor.svelte.js +28 -33
  101. package/dist/events/beforeInputCommands.d.ts +2 -2
  102. package/dist/events/beforeInputCommands.js +8 -4
  103. package/dist/events/onBeforeInput.js +1 -1
  104. package/dist/events/onDrop.js +25 -10
  105. package/dist/events/onInput.d.ts +0 -12
  106. package/dist/events/onInput.js +1 -67
  107. package/dist/events/onPaste.d.ts +2 -1
  108. package/dist/events/onPaste.js +8 -3
  109. package/dist/export/forms.d.ts +627 -0
  110. package/dist/export/forms.js +749 -0
  111. package/dist/export/index.d.ts +170 -0
  112. package/dist/export/index.js +161 -0
  113. package/dist/export/markdown.d.ts +41 -0
  114. package/dist/export/markdown.js +585 -0
  115. package/dist/export/markdownWriter.d.ts +9 -0
  116. package/dist/export/markdownWriter.js +133 -0
  117. package/dist/export/mdx.d.ts +15 -0
  118. package/dist/export/mdx.js +33 -0
  119. package/dist/index.d.ts +4 -4
  120. package/dist/index.js +2 -2
  121. package/dist/kinds.js +1 -1
  122. package/dist/labels.d.ts +2 -0
  123. package/dist/labels.js +2 -1
  124. package/dist/plugins/anchored.svelte.d.ts +21 -25
  125. package/dist/plugins/anchored.svelte.js +23 -39
  126. package/dist/plugins/arrowMove/arrowMove.d.ts +1 -1
  127. package/dist/plugins/arrowMove/arrowMove.js +1 -1
  128. package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +2 -122
  129. package/dist/plugins/blockHandles/BlockHandleController.svelte.js +48 -414
  130. package/dist/plugins/blockHandles/BlockHandles.svelte +14 -32
  131. package/dist/plugins/blockHandles/blockHandlesPlugin.js +2 -1
  132. package/dist/plugins/blockHandles/dragPreview.d.ts +26 -7
  133. package/dist/plugins/blockHandles/dragPreview.js +33 -0
  134. package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +5 -0
  135. package/dist/plugins/blockMenu/BlockMenuController.svelte.js +7 -2
  136. package/dist/plugins/blockMenu/blockMenuPlugin.js +5 -50
  137. package/dist/plugins/chrome.d.ts +27 -5
  138. package/dist/plugins/chrome.js +24 -9
  139. package/dist/plugins/code/CodeHeader.svelte +5 -3
  140. package/dist/plugins/code/CodePlugin.svelte +15 -12
  141. package/dist/plugins/code/header.svelte.js +7 -11
  142. package/dist/plugins/code/languageMenu.svelte.d.ts +5 -2
  143. package/dist/plugins/code/languageMenu.svelte.js +13 -20
  144. package/dist/plugins/code/languages.d.ts +0 -3
  145. package/dist/plugins/code/languages.js +0 -2
  146. package/dist/plugins/columns/ColumnResize.svelte +3 -2
  147. package/dist/plugins/columns/ColumnsPlugin.svelte +8 -21
  148. package/dist/plugins/columns/gaps.d.ts +7 -5
  149. package/dist/plugins/columns/gaps.js +11 -6
  150. package/dist/plugins/columns/resize.svelte.d.ts +5 -6
  151. package/dist/plugins/columns/resize.svelte.js +12 -14
  152. package/dist/plugins/comments/CommentsController.svelte.d.ts +6 -0
  153. package/dist/plugins/comments/CommentsController.svelte.js +20 -0
  154. package/dist/plugins/equation/EquationPlugin.svelte +13 -30
  155. package/dist/plugins/equation/EquationPlugin.svelte.d.ts +1 -0
  156. package/dist/plugins/equation/equation.css +7 -0
  157. package/dist/plugins/equation/equation.svelte.d.ts +7 -15
  158. package/dist/plugins/equation/equation.svelte.js +13 -50
  159. package/dist/plugins/equation/tex.d.ts +17 -0
  160. package/dist/plugins/equation/tex.js +42 -0
  161. package/dist/plugins/fileDrop.d.ts +43 -0
  162. package/dist/plugins/fileDrop.js +122 -0
  163. package/dist/plugins/find/FindController.svelte.d.ts +5 -0
  164. package/dist/plugins/find/FindController.svelte.js +14 -14
  165. package/dist/plugins/find/findPlugin.js +1 -15
  166. package/dist/plugins/image/ImageChrome.svelte +8 -2
  167. package/dist/plugins/image/ImagePlugin.svelte +56 -84
  168. package/dist/plugins/image/ImagePlugin.svelte.d.ts +13 -1
  169. package/dist/plugins/image/controls.svelte.js +16 -2
  170. package/dist/plugins/image/image.css +17 -0
  171. package/dist/plugins/image/toolbar.svelte.d.ts +5 -4
  172. package/dist/plugins/image/toolbar.svelte.js +21 -31
  173. package/dist/plugins/index.d.ts +4 -5
  174. package/dist/plugins/index.js +0 -1
  175. package/dist/plugins/list.svelte.d.ts +9 -0
  176. package/dist/plugins/list.svelte.js +18 -0
  177. package/dist/plugins/markdownShortcuts.js +5 -5
  178. package/dist/plugins/marquee/marqueePlugin.js +0 -4
  179. package/dist/plugins/media/AudioPlugin.svelte +13 -1
  180. package/dist/plugins/media/AudioPlugin.svelte.d.ts +11 -1
  181. package/dist/plugins/media/BookmarkPlugin.svelte +6 -97
  182. package/dist/plugins/media/EmbedPlugin.svelte +5 -90
  183. package/dist/plugins/media/EmptySlot.svelte +36 -20
  184. package/dist/plugins/media/EmptySlot.svelte.d.ts +6 -15
  185. package/dist/plugins/media/FilePlugin.svelte +12 -20
  186. package/dist/plugins/media/FilePlugin.svelte.d.ts +9 -0
  187. package/dist/plugins/media/MediaEmpty.svelte +2 -55
  188. package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
  189. package/dist/plugins/media/SourceBlock.svelte +178 -52
  190. package/dist/plugins/media/SourceBlock.svelte.d.ts +13 -6
  191. package/dist/plugins/media/UploadPreview.svelte +32 -0
  192. package/dist/plugins/media/UploadPreview.svelte.d.ts +11 -0
  193. package/dist/plugins/media/VideoPlugin.svelte +13 -1
  194. package/dist/plugins/media/VideoPlugin.svelte.d.ts +11 -1
  195. package/dist/plugins/media/empty.svelte.d.ts +13 -4
  196. package/dist/plugins/media/empty.svelte.js +32 -26
  197. package/dist/plugins/media/media.d.ts +9 -63
  198. package/dist/plugins/media/media.js +1 -194
  199. package/dist/plugins/media/sources.d.ts +60 -0
  200. package/dist/plugins/media/sources.js +165 -0
  201. package/dist/plugins/media/upload.css +23 -0
  202. package/dist/plugins/media/urlPaste.svelte.d.ts +2 -1
  203. package/dist/plugins/media/urlPaste.svelte.js +12 -21
  204. package/dist/plugins/mention/MentionPlugin.svelte +50 -38
  205. package/dist/plugins/mention/MentionPlugin.svelte.d.ts +11 -2
  206. package/dist/plugins/mention/mention.css +6 -0
  207. package/dist/plugins/page/PagePlugin.svelte +2 -9
  208. package/dist/plugins/page/page.js +1 -1
  209. package/dist/plugins/pageLink/PageLinkPlugin.svelte +126 -28
  210. package/dist/plugins/pageLink/PageLinkPlugin.svelte.d.ts +13 -1
  211. package/dist/plugins/pageLink/pageLink.css +6 -0
  212. package/dist/plugins/richtext/RichTextPlugin.svelte +99 -204
  213. package/dist/plugins/richtext/RichTextPlugin.svelte.d.ts +1 -0
  214. package/dist/plugins/richtext/body.js +1 -1
  215. package/dist/plugins/richtext/calloutIcons.svelte.d.ts +2 -2
  216. package/dist/plugins/richtext/calloutIcons.svelte.js +5 -7
  217. package/dist/plugins/richtext/richTextOperations.d.ts +1 -19
  218. package/dist/plugins/richtext/richTextOperations.js +3 -49
  219. package/dist/plugins/richtext/richtext.css +6 -0
  220. package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +2 -0
  221. package/dist/plugins/slashMenu/SlashMenuController.svelte.js +7 -0
  222. package/dist/plugins/slashMenu/slashMenuPlugin.js +5 -14
  223. package/dist/plugins/table/TableChrome.svelte +2 -3
  224. package/dist/plugins/table/TablePlugin.svelte +20 -54
  225. package/dist/plugins/table/chrome.svelte.d.ts +5 -2
  226. package/dist/plugins/table/chrome.svelte.js +33 -56
  227. package/dist/plugins/table/table.js +3 -3
  228. package/dist/plugins/toc/TocPlugin.svelte +2 -4
  229. package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +7 -0
  230. package/dist/plugins/toolbar/ToolbarController.svelte.js +20 -13
  231. package/dist/plugins/toolbar/toolbarPlugin.js +19 -53
  232. package/dist/plugins/triggers/TriggerController.svelte.d.ts +7 -2
  233. package/dist/plugins/triggers/TriggerController.svelte.js +11 -4
  234. package/dist/plugins/triggers/place.js +1 -2
  235. package/dist/plugins/triggers/triggers.d.ts +6 -1
  236. package/dist/plugins/triggers/triggers.js +12 -13
  237. package/dist/plugins/uploads.svelte.d.ts +48 -32
  238. package/dist/plugins/uploads.svelte.js +119 -43
  239. package/dist/plugins.d.ts +66 -14
  240. package/dist/plugins.js +0 -24
  241. package/dist/selection/replaceSelection.js +2 -2
  242. package/dist/selection/selection.svelte.d.ts +4 -10
  243. package/dist/selection/selection.svelte.js +7 -11
  244. package/dist/session/attempt.d.ts +16 -6
  245. package/dist/session/attempt.js +1 -0
  246. package/dist/session/bindings.js +35 -32
  247. package/dist/session/commands.d.ts +7 -11
  248. package/dist/session/commands.js +15 -19
  249. package/dist/session/history.js +1 -0
  250. package/dist/session/inputRules.d.ts +6 -3
  251. package/dist/session/inputRules.js +10 -6
  252. package/dist/session/moves.d.ts +33 -0
  253. package/dist/session/moves.js +71 -1
  254. package/dist/session/navigation.d.ts +7 -0
  255. package/dist/session/navigation.js +20 -11
  256. package/dist/session/props.d.ts +28 -0
  257. package/dist/session/props.js +65 -0
  258. package/dist/session/suggestions.svelte.d.ts +0 -11
  259. package/dist/session/suggestions.svelte.js +15 -3
  260. package/dist/session/uploads.d.ts +29 -0
  261. package/dist/session/uploads.js +10 -0
  262. package/dist/surface/lines.js +1 -3
  263. package/dist/surface/observer.svelte.d.ts +17 -1
  264. package/dist/surface/observer.svelte.js +72 -17
  265. package/dist/surface/overlay.d.ts +0 -14
  266. package/dist/surface/overlay.js +0 -25
  267. package/dist/surface/projector.svelte.js +1 -1
  268. package/dist/text/text.svelte.js +3 -13
  269. package/dist/themes/notion.css +9 -0
  270. package/dist/utils/sanitize.d.ts +38 -0
  271. package/dist/utils/sanitize.js +82 -0
  272. package/dist/utils.d.ts +2 -0
  273. package/dist/utils.js +5 -0
  274. package/package.json +18 -1
  275. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +0 -3
  276. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +0 -16
  277. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +0 -3
  278. package/dist/plugins/code/LanguageMenu.svelte +0 -25
  279. package/dist/plugins/code/LanguageMenu.svelte.d.ts +0 -9
  280. package/dist/plugins/equation/EquationEditor.svelte +0 -27
  281. package/dist/plugins/equation/EquationEditor.svelte.d.ts +0 -9
  282. package/dist/plugins/image/ImageToolbarHost.svelte +0 -37
  283. package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +0 -9
  284. package/dist/plugins/richtext/CalloutIconPicker.svelte +0 -26
  285. package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +0 -9
  286. /package/dist/{plugins/columns → dnd}/stacking.d.ts +0 -0
  287. /package/dist/{plugins/columns → dnd}/stacking.js +0 -0
@@ -29,7 +29,7 @@ import Announcer from './components/Announcer.svelte';
29
29
  import RemoteSelections from './collaboration/RemoteSelections.svelte';
30
30
  import Ranges from './components/Ranges.svelte';
31
31
  import { Handles } from './session/handles.js';
32
- import { id } from './utils.js';
32
+ import { cleanups, id } from './utils.js';
33
33
  import { Y } from './crdt/engine.js';
34
34
  import { markName } from './crdt/text/marks.js';
35
35
  import { callEach } from './crdt/protocols/observable.js';
@@ -43,6 +43,7 @@ import { Keymap } from './session/keymap.js';
43
43
  import { TRANSACTION } from './constants.js';
44
44
  import { Dispatcher } from './session/commands.js';
45
45
  import { History } from './session/history.js';
46
+ import { DataWrites } from './session/props.js';
46
47
  import { kindCatalogue, kindCommand, UNKNOWN_KIND } from './kinds.js';
47
48
  import { clearDomSelection, getActiveElement, getDomSelection, getDomSelectionSnapshot } from './selection/domSelection.js';
48
49
  /** A consumer that is set (a prop or a plugin hook left out is `undefined`). */
@@ -131,13 +132,18 @@ export class Edytor {
131
132
  void this.#dataRevision;
132
133
  return this.facade.docData();
133
134
  };
135
+ /** @internal The view's property writes (`session/props`): the data proxies' one queue. */
136
+ dataWrites = new DataWrites(this);
137
+ #dataProxy;
134
138
  /**
135
- * The document's properties, read-your-writes and reactive. Write them
136
- * with `edytor.patchData(null, patches)`. Type them by augmenting
137
- * {@link EdytorDocData}.
139
+ * The document's properties, as plain state: reactive at any depth, and
140
+ * writable (`edytor.data.title = 'Notes'`, `edytor.data.tags.push('x')`,
141
+ * `delete edytor.data.draft`). Each write is the `patchData` operation,
142
+ * dispatched; the writes of one task are one undo step. Type them by
143
+ * augmenting {@link EdytorDocData}.
138
144
  */
139
145
  get data() {
140
- return this.docData();
146
+ return (this.#dataProxy ??= this.dataWrites.proxy(null, this.docData));
141
147
  }
142
148
  /** @internal The IME host pin (`surface/pin`): the composing cell's segment list and render, frozen. */
143
149
  pin = new Pin();
@@ -149,6 +155,8 @@ export class Edytor {
149
155
  announcer = new AnnouncerState(this);
150
156
  /** The words the view was given (`<Edytor labels>`); its plugins read their section through `labelsOf`. */
151
157
  labels = {};
158
+ /** Whether users may nest blocks (`allowNesting`, read once; the rule is `session/moves.ts` `nestsInto`). */
159
+ allowNesting = false;
152
160
  off = [];
153
161
  onChange;
154
162
  onDocChange;
@@ -185,11 +193,10 @@ export class Edytor {
185
193
  // CRDT (v14) — the document is the composition owner: it holds the ONE
186
194
  // engine doc, the shared facade (only structural read/write surface),
187
195
  // the default local history and the shared awareness. Injected
188
- // documents are borrowed; a view with no `document` option internally
189
- // owns one (built around `doc`/`awareness` when those are injected).
196
+ // documents are borrowed; a view with no `document` option owns a fresh one.
190
197
  document;
191
- /** @internal `true` when this view created its document — `destroy()` then releases it. */
192
- ownsDocument = false;
198
+ /** `true` when this view created its document — `destroy()` then releases it. */
199
+ #owned = false;
193
200
  get doc() {
194
201
  return this.document.doc;
195
202
  }
@@ -289,29 +296,23 @@ export class Edytor {
289
296
  this.dispatcher.record({ operation: command, status: this.history.replay(command) });
290
297
  };
291
298
  constructor(options) {
292
- const { readonly, hotkeys, plugins, document, doc, awareness, actor, requireHydration, presence, sync, value, onSelectionChange, labels, onChange, onDocChange } = options;
299
+ const { readonly, hotkeys, plugins, document, actor, requireHydration, presence, sync, value, onSelectionChange, labels, onChange, onDocChange, allowNesting } = options;
293
300
  for (const name of DOCUMENT_WRITES)
294
301
  Object.defineProperty(this, name, {
295
302
  value: (...args) => this.dispatcher.operate(name, args)
296
303
  });
297
304
  if (labels)
298
305
  this.labels = labels;
306
+ this.allowNesting = allowNesting === true;
299
307
  if (document !== undefined) {
300
- if (DEV &&
301
- (doc !== undefined ||
302
- awareness !== undefined ||
303
- actor !== undefined ||
304
- requireHydration !== undefined)) {
305
- throw new Error('EdytorOptions: `document` cannot be combined with `doc`/`awareness`/`actor`/`requireHydration` — ' +
306
- 'the document owns them (compose them via attachDocument first).');
308
+ if (DEV && (actor !== undefined || requireHydration !== undefined)) {
309
+ throw new Error('EdytorOptions: `document` cannot be combined with `actor`/`requireHydration`: set them on the document.');
307
310
  }
308
311
  this.document = document;
309
312
  }
310
313
  else {
311
- // Legacy path — the view internally owns a document composed
312
- // around the injected (or a fresh) doc/awareness.
313
- this.document = attachDocument(doc ?? new Y.Doc(), { awareness, actor, requireHydration });
314
- this.ownsDocument = true;
314
+ this.document = attachDocument(new Y.Doc(), { actor, requireHydration });
315
+ this.#owned = true;
315
316
  }
316
317
  this.readonly = readonly || false;
317
318
  this.onChange = onChange;
@@ -379,7 +380,7 @@ export class Edytor {
379
380
  // decision; every other view binds on the one readiness event
380
381
  // (`<Edytor>` decides an injected document at mount, once the
381
382
  // providers of its sibling views attached).
382
- if (this.ownsDocument && !sync) {
383
+ if (this.#owned && !sync) {
383
384
  this.sync(value || { children: [] });
384
385
  }
385
386
  else {
@@ -400,7 +401,7 @@ export class Edytor {
400
401
  // document-lifetime additive declarations, and an injected
401
402
  // document's are left for the views that legitimately share it.
402
403
  this.document.untrackOrigin(this.transaction);
403
- if (this.ownsDocument) {
404
+ if (this.#owned) {
404
405
  this.document.destroy();
405
406
  }
406
407
  throw error;
@@ -602,9 +603,7 @@ export class Edytor {
602
603
  // A failed async handler (a command's hook or write) surfaces
603
604
  // through the platform's error reporting; the DOM ignores
604
605
  // the listener's promise.
605
- void Promise.resolve(result)
606
- .finally(clear)
607
- .catch((error) => typeof reportError === 'function' ? reportError(error) : console.error(error));
606
+ void Promise.resolve(result).finally(clear).catch(reportError);
608
607
  }
609
608
  else {
610
609
  clear();
@@ -821,11 +820,7 @@ export class Edytor {
821
820
  props: { announcer: this.announcer }
822
821
  });
823
822
  this.off.push(() => unmount(presence), () => unmount(ranges), () => unmount(live), detachOverlay);
824
- this.plugins.forEach((plugin) => {
825
- const action = plugin.onEdytorAttached?.({ node });
826
- if (typeof action === 'function')
827
- this.off.push(action);
828
- });
823
+ this.off.push(...cleanups(this.plugins, (plugin) => plugin.onEdytorAttached?.({ node })));
829
824
  return {
830
825
  destroy: () => {
831
826
  if (selectionIsInside(node))
@@ -855,7 +850,7 @@ export class Edytor {
855
850
  * discarded.
856
851
  *
857
852
  * Releases every listener the editor holds on potentially-SHARED objects
858
- * — an injected doc/awareness outlives a single editor, so an
853
+ * — an injected document outlives a single editor, so an
859
854
  * undestroyed mount leaks a facade `update` listener and the undo
860
855
  * manager's doc observers forever.
861
856
  *
@@ -899,7 +894,7 @@ export class Edytor {
899
894
  // awareness, doc) belong to the document — released exactly once,
900
895
  // and only when THIS view created it. A borrowed document keeps
901
896
  // serving its sibling views and headless consumers.
902
- if (this.ownsDocument) {
897
+ if (this.#owned) {
903
898
  this.document.destroy();
904
899
  }
905
900
  };
@@ -9,6 +9,6 @@ import { type Attempt } from '../session/attempt.js';
9
9
  */
10
10
  export declare const insertLineBreak: (edytor: Edytor, snapshot: Attempt, caret?: "after" | "before") => void;
11
11
  /** The model command for a `beforeinput`, run as one user command (undo policy, prevention scope). */
12
- export declare const runBeforeInputCommand: (edytor: Edytor, snapshot: Attempt) => boolean | void | Promise<undefined> | Text | null | undefined;
12
+ export declare const runBeforeInputCommand: (edytor: Edytor, snapshot: Attempt) => boolean | void | Text | Promise<undefined> | null | undefined;
13
13
  /** An editing intent at the current selection (a key binding's command): no event to fabricate. */
14
- export declare const runIntent: (edytor: Edytor, inputType: string) => boolean | void | Promise<undefined> | Text | null | undefined;
14
+ export declare const runIntent: (edytor: Edytor, inputType: string) => boolean | void | Text | Promise<undefined> | null | undefined;
@@ -97,10 +97,10 @@ export const insertLineBreak = (edytor, snapshot, caret = 'after') => {
97
97
  : block.parent?.children[block.index + 1]?.firstText;
98
98
  caretAt(edytor, line, before ? (line?.length ?? 0) : 0);
99
99
  };
100
- const runPastePlugins = (edytor, dataTransfer) => {
100
+ const runPastePlugins = (edytor, dataTransfer, at) => {
101
101
  const event = { clipboardData: dataTransfer };
102
102
  for (const plugin of edytor.plugins)
103
- vetoable((prevent) => plugin.onPaste?.({ prevent, e: event }));
103
+ vetoable((prevent) => plugin.onPaste?.({ prevent, e: event, ...(at && { at }) }));
104
104
  };
105
105
  /** The drop point the browser reported: its text and offset (`null`: not in a text). */
106
106
  const dropPoint = (edytor, { declared: range }) => {
@@ -150,9 +150,13 @@ const insertPayload = async (edytor, snapshot) => {
150
150
  // A paste occurrence is the plain text its handler left to it (Shift-paste too).
151
151
  const fragment = !paste && readEdytorClipboardFragment(dataTransfer);
152
152
  if (!paste && !fragment && dataTransfer) {
153
- // A plugin places the files at the selection: the drop point, never over selected blocks.
154
- if (dataTransfer.files?.length)
153
+ // A plugin places the files at the drop indicator's placement, else at the
154
+ // selection: the drop point, never over selected blocks.
155
+ if (dataTransfer.files?.length) {
156
+ if (snapshot.placement)
157
+ return void runPastePlugins(edytor, dataTransfer, snapshot.placement);
155
158
  return void (land() !== null && runPastePlugins(edytor, dataTransfer));
159
+ }
156
160
  if (dataTransfer.getData('text/html'))
157
161
  runPastePlugins(edytor, dataTransfer);
158
162
  }
@@ -7,7 +7,7 @@ import { isAndroidChromeBrowser } from './events.utils.js';
7
7
  import { isNativeInteractiveControl, isNativeInteractiveEvent, ownsEvent } from './nativeInteractiveControl.js';
8
8
  import { runBeforeInputCommand } from './beforeInputCommands.js';
9
9
  import { deleteInText } from './beforeInputDeleteCommands.js';
10
- import { readDomText } from './onInput.js';
10
+ import { readDomText } from '../surface/observer.svelte.js';
11
11
  /** The Android no-op-Backspace and model-owned drift deadlines (named, counted browser rules). */
12
12
  const NATIVE_INPUT_REPAIR_WINDOW_MS = 150;
13
13
  const dirOf = (inputType) => INTENTS[inputType]?.dir;
@@ -3,15 +3,20 @@ import { runOccurrence } from './onBeforeInput.js';
3
3
  import { createEdytorClipboardFragment, writeEdytorClipboardData } from '../clipboard/clipboard.js';
4
4
  import { endTextDrag, startTextDrag, textDragOf } from '../clipboard/moveText.js';
5
5
  import { caretAtPoint, getDomSelection } from '../selection/domSelection.js';
6
+ import { viewUploads } from '../session/uploads.js';
6
7
  /**
7
8
  * Payloads the editor can consume on drop. Preventing `dragover` is what
8
9
  * enables a drop, so it is prevented only for these types — internal and
9
10
  * unsupported drags keep the browser's not-allowed affordance and can never
10
11
  * produce a native DOM mutation.
11
12
  *
12
- * `Files` is intentionally accepted: file drops route through the plugin
13
- * `onPaste` hook (the Files → image seam: the image plugin with `upload`
14
- * claims image files; unclaimed files insert nothing).
13
+ * `Files` is intentionally accepted (so the browser never opens a dropped
14
+ * file in place of the page): with the view's uploads (a media plugin given
15
+ * an `upload`), file drops route through the plugin `onPaste` hook with the
16
+ * placement the uploads' drop target resolved (`ViewUploads.drop`, Pragmatic's
17
+ * external adapter: a block per file the kinds take there; unclaimed files
18
+ * insert nothing); without them a drop carrying
19
+ * files is ignored, its effect `none`.
15
20
  */
16
21
  const ACCEPTED_FOREIGN_DROP_TYPES = ['Files', 'text/html', 'text/plain', 'text/uri-list'];
17
22
  /**
@@ -121,8 +126,8 @@ const isAcceptedForeignDrop = (root, dataTransfer) => {
121
126
  // Preventing the drop suppresses the browser's own `insertFromDrop`
122
127
  // beforeinput, so the accepted payload is the occurrence itself — the same
123
128
  // routing as `insertFromPaste`, with the drop point as its declared target
124
- // range (`caretAtPoint`).
125
- const insertFromDrop = (edytor, root, event) => {
129
+ // range (`caretAtPoint`); files carry the indicator's placement (`at`).
130
+ const insertFromDrop = (edytor, root, event, placement = null) => {
126
131
  const at = caretAtPoint(root.ownerDocument, event.clientX, event.clientY);
127
132
  const point = at &&
128
133
  new StaticRange({
@@ -135,6 +140,7 @@ const insertFromDrop = (edytor, root, event) => {
135
140
  inputType: 'insertFromDrop',
136
141
  dataTransfer: event.dataTransfer,
137
142
  declared: point && root.contains(point.startContainer) ? point : null,
143
+ placement,
138
144
  cancelable: true
139
145
  });
140
146
  };
@@ -176,22 +182,31 @@ export const preventUnsupportedDrop = (event, edytor) => {
176
182
  drag.dropping = true;
177
183
  return insertFromDrop(edytor, root, event);
178
184
  }
185
+ const files = typesOf(event.dataTransfer).includes('Files');
186
+ // The view's uploads take dropped files (a media plugin given an `upload`); none: ignored.
187
+ const drop = files ? viewUploads(edytor)?.drop : undefined;
179
188
  if (event.type === 'dragover') {
180
189
  if (isAcceptedForeignDrop(root, event.dataTransfer)) {
181
190
  event.preventDefault();
182
191
  event.stopPropagation();
183
- if (event.dataTransfer) {
184
- event.dataTransfer.dropEffect = 'copy';
185
- }
192
+ // Where files land shows from the uploads' drop target (Pragmatic's
193
+ // external adapter); a read-only view or one without uploads takes no
194
+ // file drop, and the browser must not open the file.
195
+ if (event.dataTransfer)
196
+ event.dataTransfer.dropEffect = files && (edytor.readonly || !drop) ? 'none' : 'copy';
186
197
  }
187
198
  return;
188
199
  }
189
200
  const accepted = isAcceptedForeignDrop(root, event.dataTransfer);
201
+ // Where the files land: the placement the uploads' drop target resolved at
202
+ // the drop point (its window listener captures, so it ran first).
203
+ const placed = drop?.take() ?? null;
204
+ const at = accepted && !ownsEvent(root, event) ? placed : null;
190
205
  if (accepted && isControlDrop(root, event))
191
206
  return;
192
207
  event.preventDefault();
193
208
  event.stopPropagation();
194
- if (!accepted) {
209
+ if (!accepted || (files && (edytor.readonly || !drop))) {
195
210
  return;
196
211
  }
197
212
  if (ownsEvent(root, event) || isNativeInteractiveControl(event.target)) {
@@ -201,5 +216,5 @@ export const preventUnsupportedDrop = (event, edytor) => {
201
216
  }
202
217
  // Mid-composition drops are swallowed: the preview owns the write path.
203
218
  if (!edytor.isComposing)
204
- return insertFromDrop(edytor, root, event);
219
+ return insertFromDrop(edytor, root, event, at);
205
220
  };
@@ -1,16 +1,4 @@
1
1
  import type { Edytor } from '../edytor.svelte.js';
2
- import type { Text } from '../text/text.svelte.js';
3
- /**
4
- * What the browser shows for `text`: its element's text nodes in order (WebKit's
5
- * converted-space wrappers read as plain spaces), the empty filler and the
6
- * trailing-newline marker stripped.
7
- */
8
- export declare const readDomText: (text: Text) => string;
9
- export declare const getCollapsedDomTextSelection: (edytor: Edytor) => {
10
- text: Text;
11
- offset: number;
12
- } | null;
13
- export declare const handleNativeLineBreakTextValue: (edytor: Edytor, text: Text, value: string) => Promise<boolean>;
14
2
  /**
15
3
  * An `input` event. Browser-made text is never adopted here: the mutation
16
4
  * queue is the only adopter (a native line break there runs as its
@@ -1,76 +1,10 @@
1
- import { diffText } from '../utils/diffText.js';
2
- import { getDomSelectionSnapshot } from '../selection/domSelection.js';
3
- import { domOffsetIn } from '../selection/selection.utils.js';
4
1
  import { isControlEvent } from './nativeInteractiveControl.js';
5
- import { runOccurrence } from './onBeforeInput.js';
6
2
  import { attemptOf, kindOf } from '../session/attempt.js';
7
- const ZERO_WIDTH_SPACE = '\u200B';
8
- /**
9
- * What the browser shows for `text`: its element's text nodes in order (WebKit's
10
- * converted-space wrappers read as plain spaces), the empty filler and the
11
- * trailing-newline marker stripped.
12
- */
13
- export const readDomText = (text) => {
14
- let value = '';
15
- const node = text.node;
16
- if (node) {
17
- const walker = (node.ownerDocument ?? document).createTreeWalker(node, NodeFilter.SHOW_TEXT);
18
- while (walker.nextNode()) {
19
- const leaf = walker.currentNode;
20
- value += leaf.parentElement?.closest('span.Apple-converted-space')
21
- ? leaf.data.replaceAll('\u00A0', ' ')
22
- : leaf.data;
23
- }
24
- }
25
- // An empty text's filler shares its node with what the browser typed, on either side.
26
- if (text.isEmpty)
27
- return value.replaceAll(ZERO_WIDTH_SPACE, '');
28
- if (text.endsWithNewline && value.endsWith(ZERO_WIDTH_SPACE)) {
29
- value = value.slice(0, -ZERO_WIDTH_SPACE.length);
30
- }
31
- return value;
32
- };
33
- export const getCollapsedDomTextSelection = (edytor) => {
34
- const selection = getDomSelectionSnapshot(edytor.node);
35
- if (!selection?.anchorNode || !selection.isCollapsed) {
36
- return null;
37
- }
38
- if (!edytor.node?.contains(selection.anchorNode)) {
39
- return null;
40
- }
41
- const text = edytor.selection.getTextOfNode(selection.anchorNode);
42
- if (!text?.node) {
43
- return null;
44
- }
45
- // No model rule here: the DOM caret is read BEFORE the reconcile writes
46
- // the model, so a native insertion legitimately sits past `text.length`
47
- // (`selectText` clamps the final write).
48
- return { text, offset: domOffsetIn(text, selection.anchorNode, selection.anchorOffset) };
49
- };
3
+ import { getCollapsedDomTextSelection } from '../surface/observer.svelte.js';
50
4
  const getInputType = (event) => {
51
5
  const inputType = event.inputType;
52
6
  return typeof inputType === 'string' ? inputType : '';
53
7
  };
54
- const getNativeLineBreakInsertionIndexFromValue = (text, value) => {
55
- const change = diffText(text.stringContent, value);
56
- return change && !change.remove && /^[\r\n]+$/.test(change.insert) ? change.at : null;
57
- };
58
- export const handleNativeLineBreakTextValue = async (edytor, text, value) => {
59
- if (!text.isInDocument)
60
- return false;
61
- const insertionIndex = getNativeLineBreakInsertionIndexFromValue(text, value);
62
- if (insertionIndex === null) {
63
- return false;
64
- }
65
- // The native line break is the key's occurrence: its intent, run by the model.
66
- const inputType = edytor.attempts.confirm()?.inputType === 'insertLineBreak' || value.includes('\n')
67
- ? 'insertLineBreak'
68
- : 'insertParagraph';
69
- text.refreshFromModel();
70
- edytor.selection.selectText(text, insertionIndex);
71
- await runOccurrence(edytor, { inputType, cancelable: false });
72
- return true;
73
- };
74
8
  /**
75
9
  * An `input` no attempt expected (its engine sent no `beforeinput`): the
76
10
  * browser changed the text under its caret — a browser-owned attempt of its own.
@@ -6,7 +6,8 @@ import type { Edytor } from '../edytor.svelte.js';
6
6
  * the document AND cancel its paste) or under a live composition (its
7
7
  * preview owns the write path: the browser pastes, the observer reconciles
8
8
  * it after the end, as PM input.ts:656-660). Else, unless Shift asks for
9
- * plain text: an Edytor fragment; what an extension's `onPaste` claims; a
9
+ * plain text: an Edytor fragment; what an extension's `onPaste` claims (the
10
+ * `pasteFirst` ones first, then list order); a
10
11
  * file nothing claimed inserts nothing (the Files → image seam), never its
11
12
  * name; external HTML (HTML that carries nothing falls through); markdown
12
13
  * text as its blocks (`paste.markdown`), except in a code block's lines.
@@ -54,7 +54,8 @@ const syncCollapsedDomCaretForPaste = (edytor) => {
54
54
  * the document AND cancel its paste) or under a live composition (its
55
55
  * preview owns the write path: the browser pastes, the observer reconciles
56
56
  * it after the end, as PM input.ts:656-660). Else, unless Shift asks for
57
- * plain text: an Edytor fragment; what an extension's `onPaste` claims; a
57
+ * plain text: an Edytor fragment; what an extension's `onPaste` claims (the
58
+ * `pasteFirst` ones first, then list order); a
58
59
  * file nothing claimed inserts nothing (the Files → image seam), never its
59
60
  * name; external HTML (HTML that carries nothing falls through); markdown
60
61
  * text as its blocks (`paste.markdown`), except in a code block's lines.
@@ -70,8 +71,12 @@ export async function onPaste(e) {
70
71
  if (!(shiftPasteModifierHeld && plain)) {
71
72
  const fragment = readEdytorClipboardFragment(data);
72
73
  if (!fragment) {
73
- const claimed = (plugin) => vetoable((prevent) => plugin.onPaste?.({ prevent, e }));
74
- if (this.dispatcher.intercept(claimed, () => e.preventDefault()))
74
+ // The `pasteFirst` hooks (a pasted reference: page link's `fromPaste`), then the
75
+ // others in list order; the first that prevents claims the paste.
76
+ const claimed = (first) => (plugin) => !!plugin.pasteFirst === first && vetoable((prevent) => plugin.onPaste?.({ prevent, e }));
77
+ const stop = () => e.preventDefault();
78
+ if (this.dispatcher.intercept(claimed(true), stop) ||
79
+ this.dispatcher.intercept(claimed(false), stop))
75
80
  return;
76
81
  if (data?.files?.length)
77
82
  return e.preventDefault();