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
@@ -72,6 +72,13 @@ export class SlashMenuController extends TextTriggerController {
72
72
  constructor(edytor) {
73
73
  super(edytor, '/', 'slash-menu');
74
74
  }
75
+ /** A `+`'s menu stands under the `+` (its block while the `+` is gone); a `/` menu at the caret. */
76
+ anchorRect() {
77
+ const { addition } = this;
78
+ const anchor = addition?.anchor?.isConnected ? addition.anchor : addition?.block.node;
79
+ const box = anchor?.getBoundingClientRect();
80
+ return box && (box.width || box.height) ? box : super.anchorRect();
81
+ }
75
82
  /** A `+`'s menu names its highlighted row from its own field, and tells the `+` it opened it. */
76
83
  popupOf(active) {
77
84
  const { addition } = this;
@@ -1,5 +1,3 @@
1
- import { caretRect } from '../triggers/place.js';
2
- import { placeNear } from '../../surface/overlay.js';
3
1
  import { SlashMenuController } from './SlashMenuController.svelte.js';
4
2
  import { handleMenuEvents, mountChrome } from '../chrome.js';
5
3
  import { withTriggerMenus } from '../triggers/triggers.js';
@@ -7,21 +5,14 @@ import { withTriggerMenus } from '../triggers/triggers.js';
7
5
  export const createSlashMenuPlugin = (options = {}) => (edytor) => {
8
6
  const controller = new SlashMenuController(edytor);
9
7
  /**
10
- * Beside the caret (below a `+` it was opened from), kept in the
11
- * viewport; measured in the overlay's frame, written after, as
12
- * is the close of a `+` menu whose block is gone.
8
+ * Measured in the overlay's frame, written after: the close of a `+`
9
+ * menu whose block is gone (a peer's or a command's delete), as the
10
+ * block menu does. The menu places itself by `controller.anchor`.
13
11
  */
14
- const positionMenu = (host) => {
15
- if (!edytor.node || !controller.isOpen)
16
- return;
12
+ const positionMenu = () => {
17
13
  const { addition } = controller;
18
- // Its block is gone (a peer's or a command's delete): the `+` menu closes, as the block menu does.
19
- if (addition && !addition.block.isInTree)
14
+ if (controller.isOpen && addition && !addition.block.isInTree)
20
15
  return () => controller.dismiss();
21
- const anchor = addition?.anchor?.isConnected ? addition.anchor : addition?.block.node;
22
- const box = anchor?.getBoundingClientRect();
23
- const rect = box && (box.width || box.height) ? box : caretRect(edytor);
24
- return rect && placeNear(host, rect, [310, 350]);
25
16
  };
26
17
  const plugin = {
27
18
  onEdytorAttached: ({ node }) => handleMenuEvents(edytor, node,
@@ -29,7 +29,6 @@
29
29
  const labels = $derived(labelsOf(chrome.edytor, 'table', tableEnglish));
30
30
  const layout = $derived(chrome.layout);
31
31
  const line = $derived(chrome.line);
32
- const menuPlace = $derived(chrome.menuPlace);
33
32
 
34
33
  // The view turning readonly mid-drag drops the preview at once.
35
34
  $effect(() => {
@@ -114,8 +113,8 @@
114
113
  {/if}
115
114
  {/each}
116
115
  {/if}
117
- {#if menu && chrome.menu && menuPlace}
118
- <div data-edytor-table-menu-host style:left={px(menuPlace.left)} style:top={px(menuPlace.top)}>
116
+ {#if menu && chrome.menu}
117
+ <div data-edytor-table-menu-host>
119
118
  {@render menu(chrome)}
120
119
  </div>
121
120
  {/if}
@@ -1,9 +1,10 @@
1
1
  <script module lang="ts">
2
+ import { tableForms } from '../../export/forms.js';
2
3
  import './table.css';
3
4
  import type { BlockSnippetPayload, BlockView, KindMenuAction, Plugin } from '../../plugins.js';
4
5
  import type { Block } from '../../block/block.svelte.js';
5
6
  import type { Edytor } from '../../edytor.svelte.js';
6
- import { tableKinds } from '../../crdt/semantics.js';
7
+
7
8
  import { listedColumns, tableBlock, unlistedOrder } from '../../crdt/tables.js';
8
9
  import { onPress } from '../../events/onFocus.js';
9
10
  import { all } from '../../utils/listen.js';
@@ -161,6 +162,8 @@
161
162
  ...switches
162
163
  ];
163
164
  };
165
+ /** The kinds' export and import forms. */
166
+ const forms = tableForms();
164
167
  /** Tab and Shift+Tab (`step`) walk the cells, unless blocks are selected. */
165
168
  const tab =
166
169
  (step: 1 | -1): HotKey =>
@@ -181,7 +184,7 @@
181
184
  },
182
185
  blocks: {
183
186
  table: {
184
- ...tableKinds.table,
187
+ ...forms.table,
185
188
  element: (data) => ({
186
189
  tag: 'div',
187
190
  attributes: {
@@ -208,26 +211,17 @@
208
211
  group: 'Advanced blocks'
209
212
  }
210
213
  ],
211
- menu: menuOf,
212
- html: (block, _, children) => tableHtml(block.data ?? {}, children),
213
- plain: (_, __, children) => children,
214
- parse: (element) => (element.localName === 'table' ? parseTable(element) : undefined)
214
+ menu: menuOf
215
215
  },
216
216
  tableRow: {
217
- ...tableKinds.tableRow,
217
+ ...forms.tableRow,
218
218
  element: { tag: 'div', attributes: { 'data-edytor-table-row': '', role: 'row' } },
219
- snippet: row,
220
- html: (_, __, children) => `<tr>${children}</tr>`,
221
- plain: (_, __, children) => children.split('\n').join('\t'),
222
- parse: (element) => (element.localName === 'tr' ? {} : undefined)
219
+ snippet: row
223
220
  },
224
221
  tableCell: {
225
- ...tableKinds.tableCell,
222
+ ...forms.tableCell,
226
223
  element: cellElement,
227
- snippet: cell,
228
- html: (_, content) => `<td>${content}</td>`,
229
- plain: (_, content) => content.replace(/\n/g, ' '),
230
- parse: (element) => (/^t[dh]$/.test(element.localName) ? parseCell(element) : undefined)
224
+ snippet: cell
231
225
  }
232
226
  },
233
227
  hotkeys: {
@@ -278,13 +272,21 @@
278
272
  return plugin;
279
273
  };
280
274
 
281
- /** `table`'s grid template: each column's width (the resize preview while a band drags it). */
275
+ /**
276
+ * `table`'s grid template: each column's width (the resize preview while a
277
+ * band drags it). In a suggestion's preview, the widths its data lists.
278
+ */
282
279
  const templateOf = (view: BlockView) => {
283
280
  const edytor = view.handle?.edytor;
284
281
  const chrome = edytor && chromes.get(edytor);
285
282
  const listed = view.data.columns;
286
283
  const fallback = chrome?.columnWidth ?? 120;
287
- const columns = edytor ? columnsOfTable(edytor, view.id) : [];
284
+ // A suggestion's preview has no handle (no document grid): its listed columns, in order.
285
+ const columns = edytor
286
+ ? columnsOfTable(edytor, view.id)
287
+ : Array.isArray(listed)
288
+ ? listed.map((c, i) => String((c as { id?: unknown } | null)?.id ?? i))
289
+ : [];
288
290
  if (Array.isArray(listed)) {
289
291
  const entry = (id: string) => listed.find((c) => (c as { id?: unknown } | null)?.id === id);
290
292
  return columns
@@ -322,42 +324,6 @@
322
324
  };
323
325
 
324
326
  /** The table's HTML (`<table>`): its header row's and header column's cells are `th`. */
325
- const tableHtml = (data: Record<string, unknown>, rows: string) => {
326
- let html = rows;
327
- if (data.headerColumn === true)
328
- html = html.replace(/<tr><td>([\s\S]*?)<\/td>/g, '<tr><th>$1</th>');
329
- if (data.headerRow === true)
330
- html = html.replace(
331
- /^<tr>([\s\S]*?)<\/tr>/,
332
- (_, row: string) => `<tr>${row.replace(/<td>/g, '<th>').replace(/<\/td>/g, '</th>')}</tr>`
333
- );
334
- return `<table><tbody>${html}</tbody></table>`;
335
- };
336
-
337
- /** The rows of an imported `<table>` (its own, not a nested table's). */
338
- const rowsOf = (table: HTMLElement): HTMLTableRowElement[] =>
339
- [...table.querySelectorAll('tr')].filter((tr) => tr.closest('table') === table);
340
-
341
- /** An imported `<table>`'s data: a column per cell of its widest row, its header flags. */
342
- const parseTable = (table: HTMLElement) => {
343
- const rows = rowsOf(table);
344
- const width = Math.max(1, ...rows.map((tr) => tr.cells.length));
345
- const head = (cell: Element | undefined) => cell?.localName === 'th';
346
- const headerRow = rows.length > 1 && [...(rows[0]?.cells ?? [])].every((c) => head(c));
347
- const headerColumn =
348
- rows.length > 0 && rows.slice(headerRow ? 1 : 0).every((tr) => head(tr.cells[0]));
349
- return {
350
- columns: Array.from({ length: width }, (_, i) => ({ id: `c${i + 1}` })),
351
- ...(headerRow && { headerRow: true }),
352
- ...(headerColumn && !headerRow && { headerColumn: true })
353
- };
354
- };
355
-
356
- /** An imported cell's column: its position in its row (`c1`, `c2`, … as its table's). */
357
- const parseCell = (cell: HTMLElement) => {
358
- const index = (cell as HTMLTableCellElement).cellIndex;
359
- return { column: `c${(Number.isInteger(index) && index >= 0 ? index : 0) + 1}` };
360
- };
361
327
  </script>
362
328
 
363
329
  <!--
@@ -95,8 +95,11 @@ export declare class TableChrome extends ChromeList {
95
95
  /** @internal */
96
96
  minColumnWidth: number);
97
97
  protected get count(): number;
98
- /** The view is readonly (reactive): the chrome shows nothing. */
99
- get readonly(): boolean;
98
+ /**
99
+ * Where the open menu stands (`ChromeAnchor`): its grip, which your
100
+ * `menu` places itself under.
101
+ */
102
+ readonly anchor: import("../chrome.js").ChromeAnchor;
100
103
  /**
101
104
  * Open the `kind` grip's menu on the row or column it stands for, its
102
105
  * first row that can run the keyboard's, or close it when it is open
@@ -1,14 +1,13 @@
1
1
  import { untrack } from 'svelte';
2
2
  import { autoScrollFor } from '../../dnd/autoScroll.js';
3
- import { draggable, dropTargetForElements, setCustomNativeDragPreview } from '../../dnd/pragmatic.js';
4
- import { EDYTOR_BLOCK_DRAG_MIME } from '../../events/onDrop.js';
3
+ import { dropTargetForElements, elementScrollers } from '../../dnd/pragmatic.js';
5
4
  import { takeKeys } from '../../events/onFocus.js';
6
5
  import { pin } from '../../surface/overlay.js';
7
- import { attribute } from '../chrome.js';
8
- import { ChromeList, holdKeys, menuKey, optionAttachment } from '../list.svelte.js';
6
+ import { attribute, virtualAnchor } from '../chrome.js';
7
+ import { ChromeList, holdKeys, keysOf, menuKey, optionAttachment } from '../list.svelte.js';
9
8
  import { BandDrag, bandKey } from '../bands.svelte.js';
10
9
  import { all, listen } from '../../utils/listen.js';
11
- import { dragPreview } from '../blockHandles/dragPreview.js';
10
+ import { dragSource, ghost } from '../blockHandles/dragPreview.js';
12
11
  import { caretCell, deleteColumn, deleteRows, gridOf, insertColumn, insertRow, moveColumn, moveRows, widthOf } from './table.js';
13
12
  /** The drag data key naming the chrome a row's or a column's drag is from. */
14
13
  const MOVE = Symbol('edytor table move');
@@ -107,10 +106,6 @@ export class TableChrome extends ChromeList {
107
106
  get count() {
108
107
  return this.#rows.length;
109
108
  }
110
- /** The view is readonly (reactive): the chrome shows nothing. */
111
- get readonly() {
112
- return this.edytor.readonly;
113
- }
114
109
  /**
115
110
  * The column resize in progress.
116
111
  * @internal
@@ -238,22 +233,16 @@ export class TableChrome extends ChromeList {
238
233
  ];
239
234
  }
240
235
  /**
241
- * Where the open menu stands: under its grip.
242
- * @internal
236
+ * Where the open menu stands (`ChromeAnchor`): its grip, which your
237
+ * `menu` places itself under.
243
238
  */
244
- get menuPlace() {
239
+ anchor = virtualAnchor(() => this.#gripAnchor()?.getBoundingClientRect(), () => this.#gripAnchor());
240
+ /** The grip elements in the page, by kind. */
241
+ #gripNodes = {};
242
+ #gripAnchor = () => {
245
243
  const kind = this.menu?.kind;
246
- const at = kind && this.#lines(kind);
247
- if (!at)
248
- return null;
249
- const { layout, index } = at;
250
- if (kind === 'row') {
251
- const row = layout.rows[index];
252
- return { left: this.span.left - 9, top: row.top + Math.min(row.height, 28) };
253
- }
254
- const column = layout.columns[index];
255
- return { left: column.left + column.width / 2 - 12, top: layout.top + 10 };
256
- }
244
+ return kind ? this.#gripNodes[kind] : undefined;
245
+ };
257
246
  /**
258
247
  * The drop line of a grip's drag, layer-relative: between two rows
259
248
  * (columns), where the drop would land.
@@ -550,43 +539,37 @@ export class TableChrome extends ChromeList {
550
539
  #grips = {};
551
540
  #grip = (node, kind) => {
552
541
  node.setAttribute('aria-haspopup', 'menu');
542
+ this.#gripNodes[kind] = node;
553
543
  $effect(() => {
554
544
  const open = this.menu?.kind === kind;
555
545
  attribute(node, 'aria-expanded', String(open));
556
546
  attribute(node, 'aria-controls', open ? this.listId : undefined);
557
547
  attribute(node, 'data-dragging', this.moving?.kind === kind ? 'true' : undefined);
558
548
  });
559
- const off = all(listen(node, 'click', () => this.toggle(kind)), listen(node, 'keydown', (event) => {
549
+ const off = all(listen(node, 'click', () => this.toggle(kind)), listen(node, 'keydown', keysOf({
560
550
  // Escape gives the keys back to the editor (closing the menu if open).
561
- if (event.key === 'Escape')
562
- this.close();
563
- else if (event.key === 'ArrowDown' && this.menu?.kind !== kind)
564
- this.toggle(kind);
565
- else
566
- return;
567
- event.preventDefault();
568
- event.stopPropagation();
569
- }), draggable({
570
- element: node,
551
+ Escape: this.close,
552
+ ArrowDown: () => this.menu?.kind !== kind && this.toggle(kind)
553
+ }, true)), dragSource(node, {
571
554
  canDrag: () => !this.edytor.readonly && this.edytor.dispatcher.permits() && this.#source(kind) !== null,
572
- getInitialData: () => ({ [MOVE]: this }),
573
- // The editor's own drag: no view takes it as a foreign drop.
574
- getInitialDataForExternal: () => {
555
+ data: () => ({ [MOVE]: this }),
556
+ id: () => {
575
557
  const source = this.#source(kind);
576
- return {
577
- [EDYTOR_BLOCK_DRAG_MIME]: (kind === 'row' ? source?.id : source?.table.id) ?? ''
578
- };
558
+ return (kind === 'row' ? source?.id : source?.table.id) ?? '';
579
559
  },
580
- onGenerateDragPreview: ({ nativeSetDragImage, location }) => this.#begin(kind, node, location.current.input, nativeSetDragImage),
581
- // The drag library tells the source before the drop target: the drop runs first.
582
- onDrop: () => queueMicrotask(() => {
560
+ begin: (pointer, setImage) => this.#begin(kind, node, pointer, setImage),
561
+ end: () => {
583
562
  for (const release of this.#releases.splice(0))
584
563
  release();
585
564
  this.moving = null;
586
565
  this.edytor.overlay.invalidate();
587
- })
566
+ }
588
567
  }));
589
- return off;
568
+ return () => {
569
+ off();
570
+ if (this.#gripNodes[kind] === node)
571
+ delete this.#gripNodes[kind];
572
+ };
590
573
  };
591
574
  /**
592
575
  * The `+` under the table (`row`: a row at its end, the caret in its
@@ -675,14 +658,7 @@ export class TableChrome extends ChromeList {
675
658
  this.moving = { kind, table: table.id, id, gap: null };
676
659
  const widths = this.#widthsOf(table);
677
660
  const cells = (kind === 'row' ? grid.rows[index].cells : grid.rows.map((r) => r.cells[index])).flatMap((cell) => (cell && this.edytor.idToBlock.get(cell)?.node) || []);
678
- if (nativeSetDragImage && cells.length) {
679
- const preview = dragPreview(root, cells, pointer, kind === 'row' ? widths : [widths[index]]);
680
- setCustomNativeDragPreview({
681
- nativeSetDragImage,
682
- getOffset: preview.offset,
683
- render: ({ container }) => container.append(preview.element)
684
- });
685
- }
661
+ ghost(nativeSetDragImage, root, cells, pointer, kind === 'row' ? widths : [widths[index]]);
686
662
  const ours = (data) => data[MOVE] === this;
687
663
  // The page is the target: the line follows the pointer from the grips, outside the grid.
688
664
  this.#releases.push(dropTargetForElements({
@@ -695,7 +671,7 @@ export class TableChrome extends ChromeList {
695
671
  }));
696
672
  const scroller = table.node?.querySelector('[data-edytor-table-scroll]');
697
673
  if (scroller)
698
- this.#releases.push(autoScrollFor(scroller, ours));
674
+ this.#releases.push(autoScrollFor(scroller, elementScrollers(ours)));
699
675
  this.edytor.overlay.invalidate();
700
676
  }
701
677
  /**
@@ -45,7 +45,7 @@ export const insertRow = (table, index, caret = false) => {
45
45
  const plan = run(table, 'insertTableRow', { index }, (p) => ops.insertTableRow(edytor.facade, table.id, p.index));
46
46
  const row = plan ? (edytor.idToBlock.get(plan.ids[0]) ?? null) : null;
47
47
  if (row && caret)
48
- edytor.dispatcher.caret(row.children[0]?.firstText, 0);
48
+ edytor.dispatcher.caretIn(row.children[0]?.firstText, 0);
49
49
  return row;
50
50
  };
51
51
  /** Delete `rows` with their cells (`table.delete-row`). */
@@ -61,7 +61,7 @@ export const insertColumn = (table, index, width, caret) => {
61
61
  const at = Math.max(0, Math.min(Math.trunc(index) || 0, grid.columns.length - 1));
62
62
  const text = cellAt(table, grid, caret, at)?.firstText;
63
63
  if (text)
64
- table.edytor.dispatcher.caret(text, 0);
64
+ table.edytor.dispatcher.caretIn(text, 0);
65
65
  }
66
66
  return applied;
67
67
  };
@@ -80,7 +80,7 @@ export const fillCell = (table, row, column) => {
80
80
  const plan = run(table, 'fillTableCell', { row, column }, (p) => ops.fillTableCell(edytor.facade, p.row, p.column));
81
81
  const cell = plan ? (edytor.idToBlock.get(plan.ids[0]) ?? null) : null;
82
82
  if (cell)
83
- edytor.dispatcher.caret(cell.firstText, 0);
83
+ edytor.dispatcher.caretIn(cell.firstText, 0);
84
84
  return cell;
85
85
  };
86
86
  /** Toggle `flag` (`headerRow` or `headerColumn`) of `table`'s data (`table.header`). */
@@ -1,4 +1,5 @@
1
1
  <script module lang="ts">
2
+ import { tocForms } from '../../export/forms.js';
2
3
  import type { Plugin, BlockSnippetPayload } from '../../plugins.js';
3
4
  import { tocKinds } from '../../crdt/semantics.js';
4
5
  import { tocEnglish, keywordsOf, labelsOf } from '../../labels.js';
@@ -53,11 +54,8 @@
53
54
  group: 'Advanced blocks'
54
55
  }
55
56
  ],
56
- html: () => '<nav data-edytor-toc></nav>',
57
- plain: () => '',
58
57
  // Its own export: the list is drawn from the document, never carried.
59
- parse: (el) =>
60
- el.localName === 'nav' && el.hasAttribute('data-edytor-toc') ? {} : undefined
58
+ ...tocForms().toc
61
59
  }
62
60
  }
63
61
  };
@@ -76,6 +76,13 @@ export declare class ToolbarController {
76
76
  get card(): boolean;
77
77
  /** The hovered link's URL, as stored (the card shows it). */
78
78
  get hoveredHref(): string;
79
+ /**
80
+ * Where the bar stands (`ChromeAnchor`): the selection's box, which your
81
+ * `toolbar` places itself above (below when there is no room).
82
+ */
83
+ readonly anchor: import("../chrome.js").ChromeAnchor;
84
+ /** Where the link card stands (`ChromeAnchor`): the hovered link, which your `card` places itself under. */
85
+ readonly cardAnchor: import("../chrome.js").ChromeAnchor;
79
86
  /** Show the card for `anchor`, or hide it (`null`). */
80
87
  hover(anchor: HTMLAnchorElement | null): void;
81
88
  /** The card's Open: the hovered link in a new tab. */
@@ -4,9 +4,10 @@ import { selectedTextSpans } from '../../selection/visibility.js';
4
4
  import { getYIndex, SUGGESTION, SYNTHETIC_TEXT_OVERLAY_SELECTOR } from '../../selection/selection.utils.js';
5
5
  import { convertBlocks, convertibleKinds, rowOf } from '../../kinds.js';
6
6
  import { getSelectionBlocks } from '../../selection/replaceSelection.js';
7
- import { identify } from '../chrome.js';
7
+ import { identify, virtualAnchor } from '../chrome.js';
8
8
  import { all, listen } from '../../utils/listen.js';
9
9
  import { takeKeys } from '../../events/onFocus.js';
10
+ import { keysOf } from '../list.svelte.js';
10
11
  /** The bar's controls the arrows walk (a disabled one is skipped). */
11
12
  const STOPS = 'button:not([disabled]), input:not([disabled]), select:not([disabled])';
12
13
  const getLinkHref = (value) => isRecord(value) && typeof value.href === 'string' ? value.href : null;
@@ -113,20 +114,13 @@ export class ToolbarController {
113
114
  node.id = this.linkFieldId;
114
115
  if (this.takeFieldFocus())
115
116
  node.focus({ preventScroll: true });
116
- const keydown = (event) => {
117
- if (event.defaultPrevented || event.isComposing)
118
- return;
119
- if (event.key === 'Enter') {
120
- event.preventDefault();
117
+ return listen(node, 'keydown', keysOf({
118
+ Enter: () => {
121
119
  this.applyLink();
122
120
  this.closePanel(true);
123
- }
124
- else if (event.key === 'Escape') {
125
- event.preventDefault();
126
- this.closePanel(true);
127
- }
128
- };
129
- return listen(node, 'keydown', keydown);
121
+ },
122
+ Escape: () => this.closePanel(true)
123
+ }));
130
124
  };
131
125
  /** Give the keyboard back to the editor (Escape in the bar), over the held selection. */
132
126
  release() {
@@ -232,6 +226,19 @@ export class ToolbarController {
232
226
  get hoveredHref() {
233
227
  return this.hovered?.getAttribute('href') ?? '';
234
228
  }
229
+ /**
230
+ * Where the bar stands (`ChromeAnchor`): the selection's box, which your
231
+ * `toolbar` places itself above (below when there is no room).
232
+ */
233
+ anchor = virtualAnchor(() => {
234
+ const editor = this.edytor.node;
235
+ const selection = editor?.ownerDocument.getSelection();
236
+ if (!selection?.rangeCount || !editor?.contains(selection.anchorNode))
237
+ return;
238
+ return selection.getRangeAt(0).getBoundingClientRect();
239
+ }, () => this.edytor.node);
240
+ /** Where the link card stands (`ChromeAnchor`): the hovered link, which your `card` places itself under. */
241
+ cardAnchor = virtualAnchor(() => this.hovered?.getBoundingClientRect(), () => this.hovered);
235
242
  /** Show the card for `anchor`, or hide it (`null`). */
236
243
  hover(anchor) {
237
244
  if (this.hovered === anchor)
@@ -1,42 +1,14 @@
1
1
  import { ToolbarController } from './ToolbarController.svelte.js';
2
- import { mountChrome } from '../chrome.js';
3
- import { placeNear } from '../../surface/overlay.js';
2
+ import { keepFocus, mountChrome } from '../chrome.js';
4
3
  import { all, listen } from '../../utils/listen.js';
5
- /** A press in a field of the chrome (an input, a select): it takes focus. */
6
- const isNativeFieldEvent = (event) => event
7
- .composedPath()
8
- .some((target) => target instanceof Element && target.matches('input, textarea, select'));
9
4
  /** The selection toolbar, drawn by your `toolbar` snippet, and the link card by your `card`. */
10
5
  export const createToolbarPlugin = (options = {}) => (edytor) => {
11
6
  const controller = new ToolbarController(edytor);
12
- /** Above the selection, kept in the viewport; measured in the overlay's frame, written after. */
13
- const positionToolbar = (host) => {
14
- const editor = edytor.node;
15
- if (!editor || !controller.isVisible)
16
- return;
17
- const selection = editor.ownerDocument.getSelection();
18
- if (!selection?.rangeCount || !editor.contains(selection.anchorNode))
19
- return;
20
- const range = selection.getRangeAt(0);
21
- if (typeof range.getBoundingClientRect !== 'function')
22
- return;
23
- return placeNear(host, range.getBoundingClientRect(), [460, 40], {
24
- above: true,
25
- center: true,
26
- // The bar, not its open panel: panels drop over the text below it.
27
- box: host.querySelector('[data-edytor-toolbar-bar]') ?? host.firstElementChild
28
- });
29
- };
30
- /** Just below the hovered link, flush with it (the host's top padding bridges the two). */
31
- const positionCard = (host) => {
7
+ /** A hovered link that left the page hides its card (measured in the overlay's frame). */
8
+ const positionCard = () => {
32
9
  const anchor = controller.hovered;
33
- if (!anchor || !controller.card)
34
- return;
35
- if (!anchor.isConnected)
10
+ if (anchor && controller.card && !anchor.isConnected)
36
11
  return () => controller.hover(null);
37
- const rect = anchor.getBoundingClientRect();
38
- const [left, top] = [`${Math.max(8, rect.left)}px`, `${rect.bottom}px`];
39
- return () => Object.assign(host.style, { left, top, paddingTop: '4px' });
40
12
  };
41
13
  /**
42
14
  * The link card follows the pointer: over a link in the host it shows,
@@ -97,32 +69,26 @@ export const createToolbarPlugin = (options = {}) => (edytor) => {
97
69
  edytor.overlay.invalidate();
98
70
  },
99
71
  onEdytorAttached: () => {
100
- const offs = [];
101
72
  // No press on the chrome takes focus, its background and a custom
102
73
  // snippet's markup included (not only its buttons): the editor keeps
103
74
  // its focus and its selection. A field (the link panel's input) takes
104
75
  // its own. Every press has a `mousedown`, WebKit's lone one too
105
76
  // (`onFocus.ts`), so no `pointerdown` is cancelled.
106
- const keep = (event) => {
107
- if (!isNativeFieldEvent(event))
108
- event.preventDefault();
109
- };
110
- /** Mount `snippet` in the overlay while `shown`, its host keeping the focus on a press. */
111
- const mount = (snippet, shown, name, zIndex, measure) => {
112
- offs.push(mountChrome(edytor, snippet, controller, shown, { name, zIndex, measure }));
113
- const host = snippet && edytor.overlay.layer?.querySelector(`[data-${name}]`);
114
- offs.push(listen(host, 'mousedown', keep));
115
- return host;
116
- };
117
- mount(options.toolbar, () => controller.isVisible, 'edytor-toolbar-host', 60, positionToolbar);
118
- const card = mount(options.card, () => controller.card, 'edytor-link-card-host', 61, positionCard);
119
- if (edytor.node && card)
120
- offs.push(trackLinks(edytor.node, card));
121
- offs.push(() => controller.hover(null));
122
- return () => {
123
- for (const off of offs.reverse())
124
- off();
125
- };
77
+ const host = (name) => edytor.overlay.layer?.querySelector(`[data-${name}]`);
78
+ const offs = [
79
+ mountChrome(edytor, options.toolbar, controller, () => controller.isVisible, {
80
+ name: 'edytor-toolbar-host',
81
+ zIndex: 60
82
+ }),
83
+ mountChrome(edytor, options.card, controller, () => controller.card, {
84
+ name: 'edytor-link-card-host',
85
+ zIndex: 61,
86
+ measure: positionCard
87
+ })
88
+ ];
89
+ const bar = options.toolbar && host('edytor-toolbar-host');
90
+ const card = options.card && host('edytor-link-card-host');
91
+ return all(() => controller.hover(null), edytor.node && card ? trackLinks(edytor.node, card) : () => { }, listen(card, 'mousedown', keepFocus), listen(bar, 'mousedown', keepFocus), ...offs.reverse());
126
92
  }
127
93
  };
128
94
  };
@@ -34,8 +34,13 @@ export declare abstract class TextTriggerController extends ChromeList {
34
34
  char: string,
35
35
  /** The popup's owner name (`edytor.popups`), unique per view. */
36
36
  owner: string);
37
- /** The editor is readonly: the menu closes. */
38
- get readonly(): boolean;
37
+ /**
38
+ * Where the menu stands (`ChromeAnchor`): the caret, which your `menu`
39
+ * places itself under (above it when there is no room).
40
+ */
41
+ readonly anchor: import("../chrome.js").ChromeAnchor;
42
+ /** The anchor's box, read now: the caret's. */
43
+ protected anchorRect(): DOMRect | undefined;
39
44
  /** The record `popup` publishes, `active` the highlighted row's id when it is in the page. */
40
45
  protected popupOf(active: string | undefined): Popup;
41
46
  /** Whether a trigger typed in `block` opens the menu. */
@@ -1,5 +1,7 @@
1
1
  import { inCodeLines } from '../../session/inputRules.js';
2
2
  import { ChromeList, inPage } from '../list.svelte.js';
3
+ import { virtualAnchor } from '../chrome.js';
4
+ import { caretRect } from './place.js';
3
5
  /** A trigger opens at a text's start or after whitespace, never inside a word (`1/2`, `a@b`). */
4
6
  const startsTrigger = (text, offset) => offset === 0 || /\s/.test(text.stringContent[offset - 1] ?? '');
5
7
  /** What `items` and `onPick` read: the trigger's block, its range in block offsets, the query. */
@@ -30,9 +32,14 @@ export class TextTriggerController extends ChromeList {
30
32
  super(edytor, owner, 'listbox', (node) => this.isOpen && this.popupOf(inPage(node, this.rowId(this.index))));
31
33
  this.char = char;
32
34
  }
33
- /** The editor is readonly: the menu closes. */
34
- get readonly() {
35
- return this.edytor.readonly;
35
+ /**
36
+ * Where the menu stands (`ChromeAnchor`): the caret, which your `menu`
37
+ * places itself under (above it when there is no room).
38
+ */
39
+ anchor = virtualAnchor(() => this.anchorRect(), () => this.edytor.node);
40
+ /** The anchor's box, read now: the caret's. */
41
+ anchorRect() {
42
+ return caretRect(this.edytor);
36
43
  }
37
44
  /** The record `popup` publishes, `active` the highlighted row's id when it is in the page. */
38
45
  popupOf(active) {
@@ -139,7 +146,7 @@ export class TextTriggerController extends ChromeList {
139
146
  const kept = run.out === false || (run.taken && dispatcher.last?.status === 'refused');
140
147
  const out = await run.out;
141
148
  if (owns(text, held))
142
- dispatcher.caret(text, kept ? end : triggerStart);
149
+ dispatcher.caretIn(text, kept ? end : triggerStart);
143
150
  return out;
144
151
  }
145
152
  finally {
@@ -11,8 +11,7 @@ export const caretRect = (edytor) => {
11
11
  if (selection?.rangeCount && editor.contains(selection.anchorNode)) {
12
12
  const range = selection.getRangeAt(0).cloneRange();
13
13
  range.collapse(false);
14
- if (typeof range.getBoundingClientRect === 'function')
15
- rect = range.getBoundingClientRect();
14
+ rect = range.getBoundingClientRect();
16
15
  }
17
16
  if (!rect || (!rect.width && !rect.height)) {
18
17
  rect = edytor.selection.textRange().startText?.node?.getBoundingClientRect();