edytor 2.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 (222) hide show
  1. package/README.md +19 -4
  2. package/dist/block/block.svelte.js +2 -4
  3. package/dist/clipboard/htmlFlow.d.ts +20 -7
  4. package/dist/clipboard/htmlFlow.js +23 -15
  5. package/dist/clipboard/serializeClipboardFragment.d.ts +37 -4
  6. package/dist/clipboard/serializeClipboardFragment.js +6 -4
  7. package/dist/clipboard/textBlocks.d.ts +8 -0
  8. package/dist/clipboard/textBlocks.js +6 -2
  9. package/dist/cloudflare/room/admission.js +3 -3
  10. package/dist/cloudflare/room/context.d.ts +1 -1
  11. package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
  12. package/dist/cloudflare/routeDocumentSocket.js +53 -0
  13. package/dist/collaboration/comments/client.d.ts +1 -1
  14. package/dist/collaboration/comments/client.js +1 -1
  15. package/dist/collaboration/remoteSelection.js +3 -3
  16. package/dist/components/Edytor.svelte +2 -10
  17. package/dist/components/Edytor.svelte.d.ts +1 -3
  18. package/dist/crdt/admission.d.ts +5 -10
  19. package/dist/crdt/admission.js +5 -11
  20. package/dist/crdt/doc/funnel.d.ts +7 -6
  21. package/dist/crdt/doc/funnel.js +10 -9
  22. package/dist/crdt/doc/gate.d.ts +0 -17
  23. package/dist/crdt/doc/gate.js +2 -41
  24. package/dist/crdt/doc/history.js +2 -2
  25. package/dist/crdt/document.d.ts +5 -14
  26. package/dist/crdt/document.js +5 -19
  27. package/dist/crdt/edytor-doc.d.ts +4 -4
  28. package/dist/crdt/edytor-doc.js +3 -7
  29. package/dist/crdt/index.d.ts +3 -3
  30. package/dist/crdt/index.js +2 -2
  31. package/dist/crdt/protocol.d.ts +2 -2
  32. package/dist/crdt/protocol.js +2 -2
  33. package/dist/crdt/providers/index.d.ts +1 -1
  34. package/dist/crdt/providers/room.d.ts +10 -9
  35. package/dist/crdt/providers/room.js +13 -10
  36. package/dist/crdt/providers/websocket.d.ts +1 -1
  37. package/dist/crdt/providers/websocket.js +28 -29
  38. package/dist/crdt/semantics.js +3 -1
  39. package/dist/dnd/autoScroll.d.ts +15 -7
  40. package/dist/dnd/autoScroll.js +10 -15
  41. package/dist/dnd/dropIndicator.js +2 -5
  42. package/dist/dnd/external.d.ts +6 -0
  43. package/dist/dnd/external.js +17 -0
  44. package/dist/dnd/geometry.d.ts +2 -0
  45. package/dist/dnd/geometry.js +3 -4
  46. package/dist/dnd/placement.d.ts +1 -1
  47. package/dist/dnd/pragmatic.d.ts +3 -1
  48. package/dist/dnd/pragmatic.js +12 -2
  49. package/dist/dnd/vendor/pragmatic/UPSTREAM.md +51 -18
  50. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.d.ts +4 -0
  51. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.js +6 -0
  52. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +0 -2
  53. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +4 -22
  54. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter-types.d.ts +26 -0
  55. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.d.ts +30 -0
  56. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.js +203 -0
  57. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-items.js +13 -0
  58. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-types.js +9 -0
  59. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/is-an-available-type.js +19 -0
  60. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.d.ts +4 -0
  61. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.js +6 -0
  62. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +4 -16
  63. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +1 -8
  64. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +5 -6
  65. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +2 -3
  66. 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
  67. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +6 -24
  68. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.d.ts +2 -0
  69. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.js +5 -0
  70. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.d.ts +3 -0
  71. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.js +12 -0
  72. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/native-types.d.ts +12 -0
  73. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window-in-safari.js +10 -0
  74. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window.js +55 -0
  75. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.d.ts +4 -0
  76. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.js +8 -0
  77. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +0 -4
  78. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +16 -72
  79. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +1 -9
  80. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +4 -29
  81. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +2 -3
  82. package/dist/edytor.svelte.d.ts +2 -5
  83. package/dist/edytor.svelte.js +17 -31
  84. package/dist/events/onBeforeInput.js +1 -1
  85. package/dist/events/onDrop.d.ts +0 -6
  86. package/dist/events/onDrop.js +10 -23
  87. package/dist/events/onInput.d.ts +0 -12
  88. package/dist/events/onInput.js +1 -67
  89. package/dist/export/forms.d.ts +627 -0
  90. package/dist/export/forms.js +749 -0
  91. package/dist/export/index.d.ts +170 -0
  92. package/dist/export/index.js +161 -0
  93. package/dist/export/markdown.d.ts +41 -0
  94. package/dist/export/markdown.js +585 -0
  95. package/dist/export/markdownWriter.d.ts +9 -0
  96. package/dist/export/markdownWriter.js +133 -0
  97. package/dist/export/mdx.d.ts +15 -0
  98. package/dist/export/mdx.js +33 -0
  99. package/dist/index.d.ts +3 -3
  100. package/dist/index.js +1 -1
  101. package/dist/kinds.js +1 -1
  102. package/dist/plugins/anchored.svelte.d.ts +21 -25
  103. package/dist/plugins/anchored.svelte.js +23 -39
  104. package/dist/plugins/blockHandles/BlockHandleController.svelte.js +24 -32
  105. package/dist/plugins/blockHandles/dragPreview.d.ts +26 -7
  106. package/dist/plugins/blockHandles/dragPreview.js +33 -0
  107. package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +5 -0
  108. package/dist/plugins/blockMenu/BlockMenuController.svelte.js +7 -2
  109. package/dist/plugins/blockMenu/blockMenuPlugin.js +5 -50
  110. package/dist/plugins/chrome.d.ts +27 -5
  111. package/dist/plugins/chrome.js +24 -9
  112. package/dist/plugins/code/CodePlugin.svelte +12 -9
  113. package/dist/plugins/code/header.svelte.js +7 -11
  114. package/dist/plugins/code/languageMenu.svelte.d.ts +5 -2
  115. package/dist/plugins/code/languageMenu.svelte.js +13 -20
  116. package/dist/plugins/columns/ColumnsPlugin.svelte +7 -20
  117. package/dist/plugins/columns/resize.svelte.d.ts +2 -3
  118. package/dist/plugins/columns/resize.svelte.js +2 -5
  119. package/dist/plugins/comments/CommentsController.svelte.d.ts +6 -0
  120. package/dist/plugins/comments/CommentsController.svelte.js +20 -0
  121. package/dist/plugins/equation/EquationPlugin.svelte +12 -22
  122. package/dist/plugins/equation/equation.svelte.d.ts +7 -15
  123. package/dist/plugins/equation/equation.svelte.js +13 -50
  124. package/dist/plugins/equation/tex.d.ts +17 -0
  125. package/dist/plugins/equation/tex.js +42 -0
  126. package/dist/plugins/fileDrop.d.ts +38 -23
  127. package/dist/plugins/fileDrop.js +82 -76
  128. package/dist/plugins/find/FindController.svelte.d.ts +5 -0
  129. package/dist/plugins/find/FindController.svelte.js +14 -14
  130. package/dist/plugins/find/findPlugin.js +1 -15
  131. package/dist/plugins/image/ImageChrome.svelte +8 -2
  132. package/dist/plugins/image/ImagePlugin.svelte +15 -52
  133. package/dist/plugins/image/controls.svelte.js +16 -2
  134. package/dist/plugins/image/toolbar.svelte.d.ts +5 -4
  135. package/dist/plugins/image/toolbar.svelte.js +21 -31
  136. package/dist/plugins/index.d.ts +1 -1
  137. package/dist/plugins/list.svelte.d.ts +9 -0
  138. package/dist/plugins/list.svelte.js +18 -0
  139. package/dist/plugins/marquee/marqueePlugin.js +0 -4
  140. package/dist/plugins/media/AudioPlugin.svelte +1 -1
  141. package/dist/plugins/media/BookmarkPlugin.svelte +6 -97
  142. package/dist/plugins/media/EmbedPlugin.svelte +5 -90
  143. package/dist/plugins/media/EmptySlot.svelte +36 -20
  144. package/dist/plugins/media/EmptySlot.svelte.d.ts +6 -15
  145. package/dist/plugins/media/FilePlugin.svelte +1 -19
  146. package/dist/plugins/media/MediaEmpty.svelte +2 -55
  147. package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
  148. package/dist/plugins/media/SourceBlock.svelte +142 -52
  149. package/dist/plugins/media/SourceBlock.svelte.d.ts +10 -4
  150. package/dist/plugins/media/VideoPlugin.svelte +1 -1
  151. package/dist/plugins/media/empty.svelte.js +2 -7
  152. package/dist/plugins/media/media.d.ts +2 -63
  153. package/dist/plugins/media/media.js +1 -194
  154. package/dist/plugins/media/sources.d.ts +60 -0
  155. package/dist/plugins/media/sources.js +165 -0
  156. package/dist/plugins/media/urlPaste.svelte.d.ts +2 -1
  157. package/dist/plugins/media/urlPaste.svelte.js +12 -21
  158. package/dist/plugins/mention/MentionPlugin.svelte +8 -35
  159. package/dist/plugins/page/PagePlugin.svelte +2 -9
  160. package/dist/plugins/page/page.js +1 -1
  161. package/dist/plugins/pageLink/PageLinkPlugin.svelte +6 -13
  162. package/dist/plugins/richtext/RichTextPlugin.svelte +85 -197
  163. package/dist/plugins/richtext/body.js +1 -1
  164. package/dist/plugins/richtext/calloutIcons.svelte.d.ts +2 -2
  165. package/dist/plugins/richtext/calloutIcons.svelte.js +5 -7
  166. package/dist/plugins/richtext/richTextOperations.d.ts +1 -19
  167. package/dist/plugins/richtext/richTextOperations.js +3 -49
  168. package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +2 -0
  169. package/dist/plugins/slashMenu/SlashMenuController.svelte.js +7 -0
  170. package/dist/plugins/slashMenu/slashMenuPlugin.js +5 -14
  171. package/dist/plugins/table/TableChrome.svelte +2 -3
  172. package/dist/plugins/table/TablePlugin.svelte +20 -54
  173. package/dist/plugins/table/chrome.svelte.d.ts +5 -2
  174. package/dist/plugins/table/chrome.svelte.js +30 -54
  175. package/dist/plugins/table/table.js +3 -3
  176. package/dist/plugins/toc/TocPlugin.svelte +2 -4
  177. package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +7 -0
  178. package/dist/plugins/toolbar/ToolbarController.svelte.js +20 -13
  179. package/dist/plugins/toolbar/toolbarPlugin.js +19 -53
  180. package/dist/plugins/triggers/TriggerController.svelte.d.ts +7 -2
  181. package/dist/plugins/triggers/TriggerController.svelte.js +11 -4
  182. package/dist/plugins/triggers/place.js +1 -2
  183. package/dist/plugins/triggers/triggers.d.ts +6 -1
  184. package/dist/plugins/triggers/triggers.js +12 -13
  185. package/dist/plugins/uploads.svelte.d.ts +5 -2
  186. package/dist/plugins/uploads.svelte.js +25 -15
  187. package/dist/plugins.d.ts +26 -0
  188. package/dist/plugins.js +0 -24
  189. package/dist/selection/replaceSelection.js +2 -2
  190. package/dist/selection/selection.svelte.d.ts +4 -10
  191. package/dist/selection/selection.svelte.js +7 -11
  192. package/dist/session/bindings.js +1 -1
  193. package/dist/session/commands.d.ts +7 -11
  194. package/dist/session/commands.js +13 -19
  195. package/dist/session/inputRules.js +1 -1
  196. package/dist/session/navigation.js +1 -1
  197. package/dist/session/suggestions.svelte.d.ts +0 -11
  198. package/dist/session/suggestions.svelte.js +15 -3
  199. package/dist/session/uploads.d.ts +8 -8
  200. package/dist/surface/lines.js +1 -3
  201. package/dist/surface/observer.svelte.d.ts +17 -1
  202. package/dist/surface/observer.svelte.js +72 -17
  203. package/dist/surface/overlay.d.ts +0 -14
  204. package/dist/surface/overlay.js +0 -25
  205. package/dist/surface/projector.svelte.js +1 -1
  206. package/dist/text/text.svelte.js +3 -13
  207. package/dist/utils/sanitize.d.ts +38 -0
  208. package/dist/utils/sanitize.js +82 -0
  209. package/dist/utils.d.ts +2 -0
  210. package/dist/utils.js +5 -0
  211. package/package.json +18 -1
  212. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +0 -3
  213. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +0 -16
  214. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +0 -3
  215. package/dist/plugins/code/LanguageMenu.svelte +0 -25
  216. package/dist/plugins/code/LanguageMenu.svelte.d.ts +0 -9
  217. package/dist/plugins/equation/EquationEditor.svelte +0 -27
  218. package/dist/plugins/equation/EquationEditor.svelte.d.ts +0 -9
  219. package/dist/plugins/image/ImageToolbarHost.svelte +0 -37
  220. package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +0 -9
  221. package/dist/plugins/richtext/CalloutIconPicker.svelte +0 -26
  222. package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +0 -9
@@ -9,9 +9,14 @@
9
9
  * none of it.
10
10
  */
11
11
  import type { Edytor } from '../../edytor.svelte.js';
12
- import type { InitializedPlugin, InputRule, Trigger } from '../../plugins.js';
12
+ import type { InitializedPlugin, InputRule, TextRuleContext, Trigger } from '../../plugins.js';
13
13
  import { type TextTriggerController } from './TriggerController.svelte.js';
14
14
  import { type HeldList } from '../list.svelte.js';
15
+ /**
16
+ * Write `atom` where the matched text starts (`ctx.from`: a trigger's pick,
17
+ * an input rule's replacement), the caret after it; whether it was written.
18
+ */
19
+ export declare const placeAtom: ({ edytor, block, from, caret }: TextRuleContext, atom: Parameters<Edytor["insertInline"]>[2]) => boolean;
15
20
  /**
16
21
  * `plugin` with `inputRules` (text typed at the caret that a rule
17
22
  * replaces: markdown, `:smile:` → 😄) and `triggers` (characters that open
@@ -1,7 +1,5 @@
1
1
  import { inputRulesHook } from '../../session/inputRules.js';
2
2
  import { TriggerMenuController } from './TriggerController.svelte.js';
3
- import { caretRect } from './place.js';
4
- import { placeNear } from '../../surface/overlay.js';
5
3
  import { listHotkeys } from '../list.svelte.js';
6
4
  import { mountChrome } from '../chrome.js';
7
5
  /** Two bindings of one chord, `first`'s claim ending the key. */
@@ -11,6 +9,16 @@ const both = (first, then) => (payload) => {
11
9
  };
12
10
  /** The triggers each view numbered so far: a trigger's popup owner name. */
13
11
  const counts = new WeakMap();
12
+ /**
13
+ * Write `atom` where the matched text starts (`ctx.from`: a trigger's pick,
14
+ * an input rule's replacement), the caret after it; whether it was written.
15
+ */
16
+ export const placeAtom = ({ edytor, block, from, caret }, atom) => {
17
+ const written = edytor.insertInline(block.id, from, atom).status === 'applied';
18
+ if (written)
19
+ caret(from + 1);
20
+ return written;
21
+ };
14
22
  /**
15
23
  * `plugin` with `inputRules` (text typed at the caret that a rule
16
24
  * replaces: markdown, `:smile:` → 😄) and `triggers` (characters that open
@@ -47,17 +55,8 @@ export const withRulesAndTriggers = (edytor, plugin, { inputRules = [], triggers
47
55
  : undefined,
48
56
  close: () => controller.close()
49
57
  }),
50
- // Below the caret (above it when there is no room), kept in the viewport.
51
- (controller) => mountChrome(edytor, controller.trigger.menu, controller, () => controller.isOpen, {
52
- name: 'edytor-trigger-menu-host',
53
- zIndex: 50,
54
- measure: (host) => {
55
- if (!controller.isOpen)
56
- return;
57
- const rect = caretRect(edytor);
58
- return rect && placeNear(host, rect, [280, 240]);
59
- }
60
- }, () => controller.close()));
58
+ // Its markup places itself by the controller's `anchor` (the caret).
59
+ (controller) => mountChrome(edytor, controller.trigger.menu, controller, () => controller.isOpen, { name: 'edytor-trigger-menu-host', zIndex: 50 }, () => controller.close()));
61
60
  };
62
61
  /**
63
62
  * `plugin` with trigger menus wired into its hooks (a plugin's `triggers`,
@@ -1,7 +1,8 @@
1
1
  import type { Edytor } from '../edytor.svelte.js';
2
2
  import type { PluginDefinitions, PluginOperations } from '../plugins.js';
3
3
  import type { DropAt } from '../session/attempt.js';
4
- import { type FileDropPort, type ViewUploads } from '../session/uploads.js';
4
+ import { type ViewUploads } from '../session/uploads.js';
5
+ import { FileDrag } from './fileDrop.js';
5
6
  /** What an `upload` may report while it runs. */
6
7
  export type UploadReport = {
7
8
  /** The share sent so far, `0` to `1`: the block shows it as a bar. */
@@ -77,8 +78,10 @@ export declare class FileUploads implements ViewUploads {
77
78
  constructor(edytor: Edytor);
78
79
  /** The kind that takes `file`, if any. */
79
80
  kindOf: (file: File) => string | undefined;
81
+ /** The types of the kinds that take files. */
82
+ get types(): string[];
80
83
  /** Files dragged from outside: the drop indicator and the placement (`fileDrop.ts`). */
81
- readonly drop: FileDropPort;
84
+ readonly drop: FileDrag;
82
85
  /** The upload of block `id` in this view (reactive), if any. */
83
86
  of: (id: string) => FileUpload | undefined;
84
87
  /**
@@ -4,7 +4,12 @@ import { jsonBlockToSpec } from '../utils/json.js';
4
4
  import { id as mint } from '../utils.js';
5
5
  import { placeNew } from '../session/moves.js';
6
6
  import { claimUploads, viewUploads } from '../session/uploads.js';
7
- import { fileDragEnd, fileDragOver, fileDropAt } from './fileDrop.js';
7
+ import { FileDrag } from './fileDrop.js';
8
+ /** An upload's object URL, while it uploads, released. */
9
+ const revoke = (upload) => {
10
+ if (upload?.status === 'uploading' && upload.preview)
11
+ URL.revokeObjectURL(upload.preview);
12
+ };
8
13
  /**
9
14
  * The view's uploads (`media.files`), one per view, claimed by the first
10
15
  * media plugin listed with an `upload`: the uploads of pasted, dropped and
@@ -29,6 +34,7 @@ export class FileUploads {
29
34
  #uploads = new SvelteMap();
30
35
  constructor(edytor) {
31
36
  this.edytor = edytor;
37
+ this.drop = new FileDrag(edytor, this);
32
38
  }
33
39
  /** The kinds that take files (their records' `upload`), in registration order. */
34
40
  get #kinds() {
@@ -39,12 +45,12 @@ export class FileUploads {
39
45
  this.#kinds.find((kind) => kind.fallback && kind.accepts(file));
40
46
  /** The kind that takes `file`, if any. */
41
47
  kindOf = (file) => this.#kindOf(file)?.type;
48
+ /** The types of the kinds that take files. */
49
+ get types() {
50
+ return this.#kinds.map((kind) => kind.type);
51
+ }
42
52
  /** Files dragged from outside: the drop indicator and the placement (`fileDrop.ts`). */
43
- drop = {
44
- over: (event) => fileDragOver(this.edytor, event) !== null,
45
- at: (event) => fileDropAt(this.edytor, event),
46
- end: () => fileDragEnd(this.edytor)
47
- };
53
+ drop;
48
54
  /** The upload of block `id` in this view (reactive), if any. */
49
55
  of = (id) => this.#uploads.get(id);
50
56
  /**
@@ -133,9 +139,7 @@ export class FileUploads {
133
139
  };
134
140
  /** Release every preview. */
135
141
  destroy = () => {
136
- for (const upload of this.#uploads.values())
137
- if (upload.status === 'uploading' && upload.preview)
138
- URL.revokeObjectURL(upload.preview);
142
+ this.#uploads.forEach(revoke);
139
143
  this.#uploads.clear();
140
144
  };
141
145
  /**
@@ -168,8 +172,7 @@ export class FileUploads {
168
172
  const upload = this.#uploads.get(id);
169
173
  if (upload?.status !== 'uploading')
170
174
  return;
171
- if (upload.preview)
172
- URL.revokeObjectURL(upload.preview);
175
+ revoke(upload);
173
176
  this.#uploads.delete(id);
174
177
  };
175
178
  #send = async (id, kind, file) => {
@@ -185,7 +188,6 @@ export class FileUploads {
185
188
  return;
186
189
  const block = edytor.idToBlock.get(id);
187
190
  const upload = this.#uploads.get(id);
188
- const preview = upload?.status === 'uploading' ? upload.preview : null;
189
191
  // Given a source meanwhile (a peer, the link field): nothing to fill.
190
192
  if (block?.isInTree && kind.filled(block.data))
191
193
  this.#uploads.delete(id);
@@ -196,8 +198,7 @@ export class FileUploads {
196
198
  this.#uploads.set(id, { status: 'landed', data });
197
199
  else
198
200
  this.#write(id, data);
199
- if (preview)
200
- URL.revokeObjectURL(preview);
201
+ revoke(upload);
201
202
  };
202
203
  /** The data's write, outside the history; refused (readonly), it is held for the next show. */
203
204
  #write = (id, data) => {
@@ -229,5 +230,14 @@ export const uploadHooks = (edytor, upload) => {
229
230
  return {};
230
231
  const uploads = new FileUploads(edytor);
231
232
  claimUploads(edytor, uploads);
232
- return { onPaste: uploads.paste, onEdytorAttached: () => uploads.destroy };
233
+ return {
234
+ onPaste: uploads.paste,
235
+ onEdytorAttached: ({ node }) => {
236
+ const detach = uploads.drop.attach(node);
237
+ return () => {
238
+ detach();
239
+ uploads.destroy();
240
+ };
241
+ }
242
+ };
233
243
  };
package/dist/plugins.d.ts CHANGED
@@ -581,6 +581,15 @@ export type BlockDefinition = {
581
581
  html?: string | ((block: JSONBlock, content: string, children: string) => string);
582
582
  /** Clipboard plain-text form; default content then children, one per line. */
583
583
  plain?: (block: JSONBlock, content: string, children: string) => string;
584
+ /**
585
+ * Markdown form (`toMarkdown`): `content` is the block's text as inline
586
+ * markdown (marks by their own `markdown` forms), `children` its children's
587
+ * markdown, blocks apart by a blank line (indent them yourself:
588
+ * {@link MarkdownContext.indent}). Default: the bundled kind's of that
589
+ * name (`edytor/export` holds them; the bundled records carry none), else
590
+ * the `plain` form when the kind has one, else the content, then the children.
591
+ */
592
+ markdown?: (block: JSONBlock, content: string, children: string, context: MarkdownContext) => string;
584
593
  /**
585
594
  * HTML import: the block's data when `element` is this kind, else
586
595
  * `undefined`. Checked before the tag tables (a preset's export form or
@@ -652,6 +661,8 @@ export type InlineBlockDefinition = {
652
661
  * an empty `<span data-edytor-inline-block>`.
653
662
  */
654
663
  html?: (data: JSONInlineBlock['data']) => string;
664
+ /** Markdown form of the atom (`toMarkdown`, escaped by you); default its `plain` form. */
665
+ markdown?: (data: JSONInlineBlock['data']) => string;
655
666
  /**
656
667
  * HTML import: the atom's data when `element`, met in a line's inline
657
668
  * content, is this atom, else `undefined`. Its contents are then not read
@@ -678,6 +689,12 @@ export type MarkDefinition = {
678
689
  * matches its bare `tag` (value `true`).
679
690
  */
680
691
  parse?: (element: HTMLElement) => SerializableContent | undefined;
692
+ /**
693
+ * Markdown form (`toMarkdown`): the delimiters around the marked text
694
+ * (`['**', '**']`), or a function of the value giving them (a link's
695
+ * `['[', '](href)']`). Without one the text is exported unmarked.
696
+ */
697
+ markdown?: readonly [string, string] | ((value: any) => readonly [string, string] | undefined);
681
698
  void?: boolean;
682
699
  /** Whether typing at the mark's edges extends it (`marksForInsertion`); default `inclusive`. */
683
700
  edge?: MarkEdge;
@@ -755,3 +772,12 @@ export type EditorCommand = {
755
772
  searchOnly?: boolean;
756
773
  };
757
774
  export type InitializedPlugin = ReturnType<Plugin>;
775
+ /** What a kind's `markdown` form is given beside its block, content and children. */
776
+ export type MarkdownContext = {
777
+ /** The block's position in its run of same-kind siblings, from 1 (a numbered item's number). */
778
+ index: number;
779
+ /** Every line of `text` prefixed with `prefix` (`' '` under a list item, `'> '` in a quote). */
780
+ indent: (text: string, prefix: string) => string;
781
+ /** Rich text as inline markdown, by the marks' and atoms' forms (a table cell's). */
782
+ inline: (content: JSONBlock['content']) => string;
783
+ };
package/dist/plugins.js CHANGED
@@ -1,25 +1 @@
1
1
  export {};
2
- // Extension-surface note (replaces stale TODOs): the gaps this used to
3
- // list are all covered by the contract above —
4
- // * `onBeforeInput` is a PluginOperations hook (line ~111).
5
- // * Block focus/selection lifecycle is `onFocus`/`onBlur`/`onSelect`/
6
- // `onDeselect` on BlockDefinition.
7
- // * Arrow-key and select-all behavior are hotkeys, not dedicated
8
- // events: `mod+a` select-all lives in hotkeys.ts and arrow handling
9
- // ships in the arrowMove plugin — plugins can override either via
10
- // the `hotkeys` definition map (prevention is first-win).
11
- // A dedicated raw-key event hook does not exist; add one only if a real
12
- // consumer needs more than hotkey overrides.
13
- // ISLAND BLOCKS
14
- // Island blocks are blocks that are independent of the document.
15
- // They are editable
16
- // They can be nested inside other blocks
17
- // But their children can not be nested or merged with other blocks
18
- // Their structure will remain as if or will not.
19
- // In case of a merge operation, the island block will be merged with the parent block and all its children will ne unnest and set to the default block type without being added as children of the merge destination block. The same wil happen to the island's content.
20
- // VOID BLOCKS
21
- // Void blocks are blocks that are not editable
22
- // They can be nested inside other blocks
23
- // They can not accept children nor be merged with other blocks
24
- // If the content of the void block is rendered, it will still be editable. This to allow for rendering caption of the void block. I
25
- // In void blocks the content is treated as an island
@@ -340,7 +340,7 @@ export const deleteSelectedBlocks = (edytor, blocks = selectedMembers(edytor)) =
340
340
  ...beside(blocks[0], 'blockBefore', blocks),
341
341
  ...beside(blocks.at(-1), 'blockAfter', blocks)
342
342
  ];
343
- const deleted = edytor.dispatcher.caret(at?.text, at?.offset ?? 0, () => edytor.deleteBlocks(blocks.map((block) => block.id)));
343
+ const deleted = edytor.dispatcher.caretIn(at?.text, at?.offset ?? 0, () => edytor.deleteBlocks(blocks.map((block) => block.id)));
344
344
  if (deleted?.status !== 'applied')
345
345
  return null;
346
346
  edytor.announcer.deleted(what);
@@ -354,7 +354,7 @@ export const deleteSelectedBlocks = (edytor, blocks = selectedMembers(edytor)) =
354
354
  const text = virtual ? edytor.idToBlock.get(virtual)?.firstText : undefined;
355
355
  const survivor = besides.find((block) => block.isInTree);
356
356
  if (text)
357
- edytor.dispatcher.caret(text, 0);
357
+ edytor.dispatcher.caretIn(text, 0);
358
358
  else if (survivor)
359
359
  edytor.selection.select(blockSelection([survivor.id]));
360
360
  return text ?? null;
@@ -94,7 +94,7 @@ export declare class EdytorSelection {
94
94
  * the start binds right (boundary inserts stay outside), the end and a
95
95
  * caret bind left; `none` when the start text is not bound to the document.
96
96
  */
97
- textValue: (startText: Text, yStart: number, endText?: Text, yEnd?: number, isReversed?: boolean, startAffinity?: "left" | "right", mint?: (text: Text, offset: number, affinity?: "left" | "right") => TextAnchor | null) => SelectionValue;
97
+ textValue: (startText: Text, yStart: number, endText?: Text, yEnd?: number, isReversed?: boolean, startAffinity?: "left" | "right") => SelectionValue;
98
98
  /**
99
99
  * A pointer drag is in progress (a press in the host, until its release):
100
100
  * the projector does not display under it, and the overlay chrome
@@ -123,9 +123,9 @@ export declare class EdytorSelection {
123
123
  * facade maps to the owning backing text — content routed from other
124
124
  * blocks' backing texts (merges/splits) anchors correctly.
125
125
  *
126
- * `null` while the text isn't bound to a live block (mirrors the
127
- * baseline where a non-integrated Y.Text couldn't produce an absolute
128
- * position).
126
+ * Minted from the text's block record, live or not (the atoms of a text
127
+ * that died to a merge live on in the block that claimed them); `null`
128
+ * for the root's own text.
129
129
  */
130
130
  createTextAnchor: (text: Text, offset: number, affinity?: "left" | "right") => TextAnchor | null;
131
131
  /**
@@ -148,12 +148,6 @@ export declare class EdytorSelection {
148
148
  * never mounts.
149
149
  */
150
150
  displayable: (id: string) => boolean;
151
- /** The view's visibility rule (`selection/visibility.ts`), for extensions: hidden by view state, and the shown neighbour. */
152
- hidden: (block: Block, removed?: ReadonlySet<Block>) => boolean;
153
- shown: (block: Block, step: "blockBefore" | "blockAfter", { sealed, removed }?: {
154
- sealed?: boolean;
155
- removed?: ReadonlySet<Block>;
156
- }) => Block | null;
157
151
  /** The text's computed direction (a Surface fact): arrow and word keys are visual. */
158
152
  rtl: (text: Text) => boolean;
159
153
  }
@@ -389,7 +389,8 @@ export class EdytorSelection {
389
389
  * the start binds right (boundary inserts stay outside), the end and a
390
390
  * caret bind left; `none` when the start text is not bound to the document.
391
391
  */
392
- textValue = (startText, yStart, endText = startText, yEnd = yStart, isReversed = false, startAffinity, mint = this.createTextAnchor) => {
392
+ textValue = (startText, yStart, endText = startText, yEnd = yStart, isReversed = false, startAffinity) => {
393
+ const mint = this.createTextAnchor;
393
394
  const collapsed = startText === endText && yStart === yEnd;
394
395
  const start = mint(startText, yStart, startAffinity ?? (collapsed ? 'left' : 'right'));
395
396
  if (!start)
@@ -413,9 +414,7 @@ export class EdytorSelection {
413
414
  if (!startText || !endText)
414
415
  return;
415
416
  const at = (text, offset) => Math.min(Math.max(offset, 0), text.length);
416
- const value = this.textValue(startText, at(startText, yStart), endText, at(endText, yEnd), isReversed, undefined, (text, offset, side) => text.parent.isRoot
417
- ? null
418
- : this.edytor.facade.anchorAt(text.blockId, text.segStart + offset, side));
417
+ const value = this.textValue(startText, at(startText, yStart), endText, at(endText, yEnd), isReversed);
419
418
  const target = project(value, this.edytor.facade).start
420
419
  ? value
421
420
  : this.#seamValue(startText.parent?.id ?? null);
@@ -652,12 +651,12 @@ export class EdytorSelection {
652
651
  * facade maps to the owning backing text — content routed from other
653
652
  * blocks' backing texts (merges/splits) anchors correctly.
654
653
  *
655
- * `null` while the text isn't bound to a live block (mirrors the
656
- * baseline where a non-integrated Y.Text couldn't produce an absolute
657
- * position).
654
+ * Minted from the text's block record, live or not (the atoms of a text
655
+ * that died to a merge live on in the block that claimed them); `null`
656
+ * for the root's own text.
658
657
  */
659
658
  createTextAnchor = (text, offset, affinity = 'left') => {
660
- if (text.parent.isRoot || !text.isInDocument)
659
+ if (text.parent.isRoot)
661
660
  return null;
662
661
  return this.edytor.facade.anchorAt(text.blockId, text.segStart + offset, affinity);
663
662
  };
@@ -688,9 +687,6 @@ export class EdytorSelection {
688
687
  ?.content.find((part) => part instanceof Text && part.node != null)?.node;
689
688
  return !!node && !node.closest(visibility.HIDDEN);
690
689
  };
691
- /** The view's visibility rule (`selection/visibility.ts`), for extensions: hidden by view state, and the shown neighbour. */
692
- hidden = visibility.hidden;
693
- shown = visibility.shown;
694
690
  /** The text's computed direction (a Surface fact): arrow and word keys are visual. */
695
691
  rtl = (text) => !!text.node?.isConnected && getComputedStyle(text.node).direction === 'rtl';
696
692
  /** A caret at the seam `dead` vacated (`doc/anchors`); `null` when nothing displays. */
@@ -24,7 +24,7 @@ export const replaceSelectedAtom = (edytor, value = '') => {
24
24
  edytor.removeInline(parent.id, selected.id);
25
25
  if (value)
26
26
  edytor.insertText(parent.id, text.segStart + offset, value);
27
- edytor.dispatcher.caret(text, offset + value.length);
27
+ edytor.dispatcher.caretIn(text, offset + value.length);
28
28
  return true;
29
29
  };
30
30
  /** The selection is the editor's own: an inline atom or blocks (no DOM range shows it). */
@@ -128,18 +128,14 @@ export declare class Dispatcher {
128
128
  taken: boolean;
129
129
  };
130
130
  /**
131
- * A command's result caret: selected once and recorded on the result;
132
- * the projector displays it after the flush. With `ops`, the
133
- * caret is declared before the command's operations run — minted while its
134
- * text is live, so it survives them — and written only when they applied;
135
- * meanwhile the seam repair leaves this view's endpoints to it. Takes a
136
- * text segment and an offset in it, or a `{ block, offset }` caret in
137
- * block offsets (an inline atom counting 1).
131
+ * A command's result caret, `{ block, offset }` in block offsets (an
132
+ * inline atom counting 1): selected once and recorded on the result;
133
+ * the projector displays it after the flush. With `ops`, the caret is
134
+ * declared before the command's operations run — minted while its text
135
+ * is live, so it survives them — and written only when they applied;
136
+ * meanwhile the seam repair leaves this view's endpoints to it.
138
137
  */
139
- caret: {
140
- (text: Text | null | undefined, offset: number, ops?: () => OpResult): OpResult | undefined;
141
- (caret: Caret | DocPosition | null | undefined, ops?: () => OpResult): OpResult | undefined;
142
- };
138
+ caret: (caret: Caret | DocPosition | null | undefined, ops?: () => OpResult) => OpResult | undefined;
143
139
  /**
144
140
  * The cut an operation applies as it writes. Inside a user command none
145
141
  * (the command cut); outside one, its bare policy. A led operation is a
@@ -566,27 +566,20 @@ export class Dispatcher {
566
566
  return this.running ? this.kind : null;
567
567
  }
568
568
  /**
569
- * A command's result caret: selected once and recorded on the result;
570
- * the projector displays it after the flush. With `ops`, the
571
- * caret is declared before the command's operations run — minted while its
572
- * text is live, so it survives them — and written only when they applied;
573
- * meanwhile the seam repair leaves this view's endpoints to it. Takes a
574
- * text segment and an offset in it, or a `{ block, offset }` caret in
575
- * block offsets (an inline atom counting 1).
569
+ * A command's result caret, `{ block, offset }` in block offsets (an
570
+ * inline atom counting 1): selected once and recorded on the result;
571
+ * the projector displays it after the flush. With `ops`, the caret is
572
+ * declared before the command's operations run — minted while its text
573
+ * is live, so it survives them — and written only when they applied;
574
+ * meanwhile the seam repair leaves this view's endpoints to it.
576
575
  */
577
- caret = (target, ...rest) => {
578
- // The form is the arguments', not the target's: a nullish caret still runs its `ops`.
579
- if (typeof rest[0] === 'number') {
580
- return this.#caret(target, rest[0], rest[1]);
581
- }
582
- const ops = rest[0];
583
- const caret = target;
584
- // `{ block, offset }`: block offsets (a handle or an id), mapped to the text segment that shows the offset.
576
+ caret = (caret, ops) => {
585
577
  const block = typeof caret?.block === 'string' ? this.edytor.idToBlock.get(caret.block) : caret?.block;
586
578
  const at = block?.textAtOffset(Math.max(0, caret.offset));
587
- return this.#caret(at?.text, at?.offset ?? 0, ops);
579
+ return this.caretIn(at?.text, at?.offset ?? 0, ops);
588
580
  };
589
- #caret = (text, offset, ops) => {
581
+ /** @internal `caret` at an offset inside a text segment. */
582
+ caretIn = (text, offset, ops) => {
590
583
  const selection = this.edytor.selection;
591
584
  const at = text ? Math.max(0, Math.min(offset, text.length)) : 0;
592
585
  const value = text ? selection.textValue(text, at) : null;
@@ -642,7 +635,8 @@ export class Dispatcher {
642
635
  try {
643
636
  for (let i = 0; i < this.queue.length; i++) {
644
637
  if (i === 1000) {
645
- console.warn('[edytor] normalization is not converging; skipping further passes');
638
+ if (DEV)
639
+ console.warn('[edytor] normalization is not converging; skipping further passes');
646
640
  break;
647
641
  }
648
642
  const entry = this.queue[i];
@@ -654,7 +648,7 @@ export class Dispatcher {
654
648
  const count = (counts.get(id) ?? 0) + 1;
655
649
  counts.set(id, count);
656
650
  if (count > MAX_PASSES) {
657
- if (count === MAX_PASSES + 1)
651
+ if (DEV && count === MAX_PASSES + 1)
658
652
  console.warn(`edytor: ${normalize} on block "${id}" exceeded ${MAX_PASSES - 1} passes — a plugin normalizer is not converging; skipping further passes`);
659
653
  continue;
660
654
  }
@@ -47,7 +47,7 @@ export const inputRulesHook = (edytor, rules) => {
47
47
  /** The typed text lands as typed, the caret after it. */
48
48
  const typeAsIs = () => {
49
49
  edytor.insertText(...payload);
50
- dispatcher.caret(text, yStart + value.length);
50
+ dispatcher.caretIn(text, yStart + value.length);
51
51
  };
52
52
  /**
53
53
  * Run `rule` over `match`: whether it wrote, or `null` when it declined
@@ -303,7 +303,7 @@ export const toTrailingParagraph = (edytor) => {
303
303
  const text = edytor.idToBlock.get(ids[0] ?? '')?.firstText;
304
304
  if (!text)
305
305
  return false;
306
- edytor.dispatcher.caret(text, 0);
306
+ edytor.dispatcher.caretIn(text, 0);
307
307
  return true;
308
308
  };
309
309
  /**
@@ -1,14 +1,3 @@
1
- /**
2
- * Suggestions (site `editor/suggestions`): content one view proposes at a
3
- * position — AI output, a completion — shown only in that view until it is
4
- * accepted. Session state: nothing reaches the document, its history,
5
- * presence, `onChange` or another view before `accept()`, which is one
6
- * command (`acceptSuggestion`: admission, hooks, one transaction, one undo
7
- * step) placing the content as a paste places it (`flow.place`, `flow.slot`,
8
- * `flow.inline`). A suggestion is anchored by block ids (a text range by its
9
- * anchors): it survives selection changes and edits elsewhere, and is dropped
10
- * when its block dies. The layer owns the list; the surface renders it.
11
- */
12
1
  import type { BlockId, DocAnchor, OpResult } from '../crdt/index.js';
13
2
  import type { Edytor } from '../edytor.svelte.js';
14
3
  import { type JSONBlock } from '../utils/json.js';
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Suggestions (site `editor/suggestions`): content one view proposes at a
3
+ * position — AI output, a completion — shown only in that view until it is
4
+ * accepted. Session state: nothing reaches the document, its history,
5
+ * presence, `onChange` or another view before `accept()`, which is one
6
+ * command (`acceptSuggestion`: admission, hooks, one transaction, one undo
7
+ * step) placing the content as a paste places it (`flow.place`, `flow.slot`,
8
+ * `flow.inline`). A suggestion is anchored by block ids (a text range by its
9
+ * anchors): it survives selection changes and edits elsewhere, and is dropped
10
+ * when its block dies. The layer owns the list; the surface renders it.
11
+ */
12
+ import { DEV } from 'esm-env';
1
13
  import { REFUSED } from '../crdt/doc/plan.js';
2
14
  import { textToBlocks } from '../clipboard/textBlocks.js';
3
15
  import { selectedMembers, viewOf } from '../selection/visibility.js';
@@ -329,17 +341,17 @@ export class Suggestions {
329
341
  resolved = { replace: { anchor: value.anchor, focus: value.focus } };
330
342
  else {
331
343
  const block = value.kind === 'atom' ? value.blockId : projection.start?.block;
332
- resolved = block ? { after: block } : fail('no selection to replace');
344
+ resolved = block ? { after: block } : fail(DEV ? 'no selection to replace' : '');
333
345
  }
334
346
  }
335
347
  else
336
348
  resolved = at;
337
349
  if (!this.#alive(resolved))
338
- fail('the position names no shown block');
350
+ fail(DEV ? 'the position names no shown block' : '');
339
351
  if ('inside' in resolved) {
340
352
  const probe = edytor.facade.prepare.insertBlocks({ parent: resolved.inside, index: 0 }, []);
341
353
  if (!('writes' in probe))
342
- fail(`block "${resolved.inside}" holds no children`);
354
+ fail(DEV ? `block "${resolved.inside}" holds no children` : '');
343
355
  }
344
356
  return resolved;
345
357
  };
@@ -3,18 +3,18 @@
3
3
  * first media plugin given an `upload` claims (`uploadHooks`), and whether it takes a
4
4
  * file (a kind's `upload` accepts it). A file dragged over the view shows
5
5
  * where it lands only when its uploads take it (`drop`, the plugin layer's
6
- * `plugins/fileDrop.ts`); a view without uploads ignores dropped files.
6
+ * `plugins/fileDrop.ts`, Pragmatic's external adapter); a view without uploads ignores dropped files.
7
7
  */
8
8
  import type { Edytor } from '../edytor.svelte.js';
9
9
  import type { DropAt } from './attempt.js';
10
- /** Files dragged from outside over the view: where they land, shown. */
10
+ /** Files dragged from outside over the view: where they land. */
11
11
  export interface FileDropPort {
12
- /** A `dragover` of files: show where they land; `false` where none does (nothing shows). */
13
- over(event: DragEvent): boolean;
14
- /** The drop: the placement shown, re-resolved at the drop point, and nothing shows any more. */
15
- at(event: DragEvent): DropAt | null;
16
- /** The drag left the view, or ended: nothing shows. */
17
- end(): void;
12
+ /**
13
+ * The placement the drop of files being handled landed at, once (the
14
+ * plugin layer's drop target resolves it before the host's `drop`
15
+ * listener runs); `null` where none shows.
16
+ */
17
+ take(): DropAt | null;
18
18
  }
19
19
  /** What the view's uploads answer about a file. */
20
20
  export interface ViewUploads {
@@ -18,9 +18,7 @@ export const caretBox = (text, offset, after) => {
18
18
  const [endNode, end] = domPointOf(element, from + 1);
19
19
  range.setStart(startNode, start);
20
20
  range.setEnd(endNode, end);
21
- // No layout engine (a server, jsdom) lays nothing out: no box.
22
- const list = typeof range.getClientRects === 'function' ? range.getClientRects() : null;
23
- return [...(list ?? [])].find((r) => r.width > 0 || r.height > 0) ?? null;
21
+ return [...range.getClientRects()].find((r) => r.width > 0 || r.height > 0) ?? null;
24
22
  };
25
23
  const next = box(offset);
26
24
  const previous = box(offset - 1);
@@ -2,6 +2,18 @@ import type { Edytor } from '../edytor.svelte.js';
2
2
  import type { Text } from '../text/text.svelte.js';
3
3
  import { type Kind, type Owned } from './attributes.js';
4
4
  import { type Patched, type Replaced } from './cells.js';
5
+ /**
6
+ * What the browser shows for `text`: its element's text nodes in order (WebKit's
7
+ * converted-space wrappers read as plain spaces), the empty filler and the
8
+ * trailing-newline marker stripped.
9
+ */
10
+ export declare const readDomText: (text: Text) => string;
11
+ export declare const getCollapsedDomTextSelection: (edytor: Edytor) => {
12
+ text: Text;
13
+ offset: number;
14
+ } | null;
15
+ /** A native line break (a diff inserting only newlines at `at`) runs as the key's occurrence. */
16
+ export declare const lineBreak: (edytor: Edytor, text: Text, value: string, at: number) => Promise<void>;
5
17
  export declare class SurfaceObserver {
6
18
  #private;
7
19
  private edytor;
@@ -12,7 +24,11 @@ export declare class SurfaceObserver {
12
24
  constructor(edytor: Edytor);
13
25
  /** Observe `root` (the host): records name what to compare. */
14
26
  attach: (root: HTMLElement) => () => void;
15
- /** Register an element the core rendered (an attachment); returns its release. */
27
+ /**
28
+ * Register an element the core rendered (an attachment); returns its
29
+ * release. A text element takes the table's attributes now (its id, its
30
+ * editing host inside a void).
31
+ */
16
32
  register: (node: Element, kind: Kind, block: string, name?: string, owned?: () => Owned) => () => void;
17
33
  /** The render anchor (`Edytor.svelte`): a registered child of the strict root. */
18
34
  anchor: (node: HTMLElement) => {