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
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Files dragged from outside over a view (Notion): the drop indicator a
3
+ * block drag shows, where the files land, and that placement for the drop.
4
+ * The drag is Pragmatic's external adapter's (`dnd/external.ts`), the same
5
+ * pipeline as the block and table drags: the host is its drop target, which
6
+ * takes a drag of files in an editable view. The placement is the block
7
+ * drag's own resolution (`DropResolver`) for a drag that moves no block,
8
+ * whose places are the ones the document takes new blocks of the files'
9
+ * kinds at (`canPlaceNew`). With or without the block handles.
10
+ *
11
+ * While they are dragged the browser shows each file's MIME type, not the
12
+ * file (the adapter's items carry it, a vendored change, `UPSTREAM.md`): the
13
+ * kinds are the ones the uploads would place those files as, so the line
14
+ * shows only where they land; none (a PDF with only an image uploader): no
15
+ * line, and the drop inserts nothing. Where the engine hides the types (no
16
+ * item, or an empty type), every kind the uploads take. The drop resolves
17
+ * again with the files themselves.
18
+ * Its listeners are the window's, capturing, so the drop target's `onDrop`
19
+ * runs before the host's own `drop` listener (`events/onDrop.ts`), which
20
+ * takes the placement (`ViewUploads.drop.take`) and runs the drop's
21
+ * occurrence (the uploads' paste hook places one block per file there, one
22
+ * command). Plugin-owned: the view's `FileUploads` (the first media plugin
23
+ * listed with an `upload`) installs it, so a view without one ships none of it.
24
+ */
25
+ import type { Edytor } from '../edytor.svelte.js';
26
+ import type { DropAt } from '../session/attempt.js';
27
+ /** What the drag asks of the view's uploads. */
28
+ export type DropUploads = {
29
+ /** The kind that takes `file`, if any. */
30
+ kindOf(file: File): string | undefined;
31
+ /** Every kind that takes files. */
32
+ readonly types: readonly string[];
33
+ };
34
+ export declare class FileDrag {
35
+ #private;
36
+ private readonly edytor;
37
+ private readonly uploads;
38
+ constructor(edytor: Edytor, uploads: DropUploads);
39
+ /** The host takes drags of files from outside while it is editable; the answer releases it. */
40
+ attach(host: HTMLElement): () => void;
41
+ /** The placement the last drop of files landed at, once (the host's `drop` takes it). */
42
+ take(): DropAt | null;
43
+ }
@@ -0,0 +1,122 @@
1
+ import { canPlaceNew } from '../session/moves.js';
2
+ import { autoScrollFor } from '../dnd/autoScroll.js';
3
+ import { DropIndicator } from '../dnd/dropIndicator.js';
4
+ import { containsFiles, dropTargetForExternal, externalScrollers, getFiles } from '../dnd/external.js';
5
+ import { DropResolver, isDropTarget } from '../dnd/placement.js';
6
+ import { isKindControl } from '../events/nativeInteractiveControl.js';
7
+ export class FileDrag {
8
+ edytor;
9
+ uploads;
10
+ #indicator;
11
+ #resolver;
12
+ #scrolling = null;
13
+ #dropped = null;
14
+ #begun = false;
15
+ #kinds = [];
16
+ constructor(edytor, uploads) {
17
+ this.edytor = edytor;
18
+ this.uploads = uploads;
19
+ this.#indicator = new DropIndicator(edytor, {
20
+ group: () => [],
21
+ besideOf: (block) => this.#resolver.besideOf(block),
22
+ layoutOf: (outer) => this.#resolver.layoutOf(outer)
23
+ });
24
+ this.#resolver = new DropResolver(edytor, {
25
+ targetOf: (node) => {
26
+ const block = edytor.idToBlock.get(node.dataset.edytorId ?? '');
27
+ return block?.node === node && isDropTarget(edytor, block) ? block : undefined;
28
+ },
29
+ handle: () => null,
30
+ indicator: this.#indicator
31
+ });
32
+ }
33
+ /** The host takes drags of files from outside while it is editable; the answer releases it. */
34
+ attach(host) {
35
+ const release = dropTargetForExternal({
36
+ element: host,
37
+ canDrop: ({ source }) => !this.edytor.readonly && containsFiles({ source }) && this.uploads.types.length > 0,
38
+ onDragEnter: ({ source, location }) => {
39
+ this.#kinds = this.#draggedKinds(source.items);
40
+ this.#dropped = null;
41
+ this.#scrolling ??= autoScrollFor(host, externalScrollers(() => !this.edytor.readonly));
42
+ this.#over(this.#kinds, location.current.input);
43
+ },
44
+ onDrag: ({ location }) => this.#over(this.#kinds, location.current.input),
45
+ onDragLeave: () => this.#end(),
46
+ onDrop: ({ source, location }) => {
47
+ const kinds = new Set(getFiles({ source }).flatMap((file) => this.uploads.kindOf(file) ?? []));
48
+ const placement = kinds.size ? this.#over([...kinds], location.current.input) : null;
49
+ this.#end();
50
+ this.#dropped = placement && { block: placement.target.id, position: placement.position };
51
+ }
52
+ });
53
+ return () => {
54
+ release();
55
+ this.#end();
56
+ };
57
+ }
58
+ /** The placement the last drop of files landed at, once (the host's `drop` takes it). */
59
+ take() {
60
+ const dropped = this.#dropped;
61
+ this.#dropped = null;
62
+ return dropped;
63
+ }
64
+ /** The kinds the dragged files would be, by their types; hidden types: every kind that takes files. */
65
+ #draggedKinds(items) {
66
+ const files = items.filter((item) => item.kind === 'file');
67
+ if (!files.length || files.some((item) => !item.type))
68
+ return this.uploads.types;
69
+ const kinds = new Set();
70
+ for (const { type } of files) {
71
+ const kind = this.uploads.kindOf(new File([], 'file', { type }));
72
+ if (kind)
73
+ kinds.add(kind);
74
+ }
75
+ return [...kinds];
76
+ }
77
+ /** The drag of files of `kinds`: no block moves; a place takes them all. */
78
+ #drag(kinds) {
79
+ return {
80
+ group: [],
81
+ fits: ({ target, position }) => canPlaceNew(this.edytor, kinds, target, position)
82
+ };
83
+ }
84
+ /**
85
+ * The placement at the pointer, shown (`null`: none, and nothing shows):
86
+ * over a drop target, its row's; elsewhere in the host, its margins'; on
87
+ * a kind's own control, none.
88
+ */
89
+ #over(kinds, input) {
90
+ const host = this.edytor.node;
91
+ const document = host?.ownerDocument;
92
+ if (!document)
93
+ return null;
94
+ if (!this.#begun) {
95
+ this.#begun = true;
96
+ this.#indicator.begin(document);
97
+ }
98
+ const at = document.elementsFromPoint?.(input.clientX, input.clientY)[0] ?? null;
99
+ const control = at !== null && isKindControl(host, at);
100
+ const drag = this.#drag(kinds);
101
+ const node = control ? null : this.#resolver.hitAt(input);
102
+ const placement = control
103
+ ? null
104
+ : node
105
+ ? this.#resolver.over(drag, node, input)
106
+ : this.#resolver.margin(drag, input);
107
+ if (!placement) {
108
+ this.#indicator.clear();
109
+ return null;
110
+ }
111
+ // A margin's placement shows as the block drag's page target does (its reorder holds).
112
+ this.#indicator.show(node ?? document.body, placement);
113
+ return placement;
114
+ }
115
+ /** The drag left the view, or dropped: nothing shows, nothing scrolls. */
116
+ #end() {
117
+ this.#scrolling?.();
118
+ this.#scrolling = null;
119
+ this.#begun = false;
120
+ this.#indicator.end();
121
+ }
122
+ }
@@ -14,6 +14,11 @@ export type ReplaceMatches = Commands['replaceMatches'];
14
14
  export declare class FindController {
15
15
  #private;
16
16
  private edytor;
17
+ /**
18
+ * Where the bar stands (`ChromeAnchor`): the editor, whose top right
19
+ * your `bar` places itself in.
20
+ */
21
+ readonly anchor: import("../chrome.js").ChromeAnchor;
17
22
  isOpen: boolean;
18
23
  /** The replacement text. */
19
24
  replacement: string;
@@ -4,6 +4,8 @@ import { showText } from '../planned.js';
4
4
  import { findMatches } from './search.js';
5
5
  import { listen } from '../../utils/listen.js';
6
6
  import { takeKeys } from '../../events/onFocus.js';
7
+ import { keysOf } from '../list.svelte.js';
8
+ import { virtualAnchor } from '../chrome.js';
7
9
  /**
8
10
  * The find bar's state and actions (`plugins/find`): the query, the matches
9
11
  * in reading order, the current one and the replacements. It reads the
@@ -12,6 +14,11 @@ import { takeKeys } from '../../events/onFocus.js';
12
14
  */
13
15
  export class FindController {
14
16
  edytor;
17
+ /**
18
+ * Where the bar stands (`ChromeAnchor`): the editor, whose top right
19
+ * your `bar` places itself in.
20
+ */
21
+ anchor = virtualAnchor(() => this.edytor.node?.getBoundingClientRect(), () => this.edytor.node);
15
22
  isOpen = $state(false);
16
23
  /** The replacement text. */
17
24
  replacement = $state('');
@@ -151,20 +158,13 @@ export class FindController {
151
158
  * closes, Mod+F selects the query.
152
159
  */
153
160
  #keys = (node, query) => {
154
- const keydown = (event) => {
155
- if (event.isComposing)
156
- return;
157
- const mod = this.edytor.keymap.isMac ? event.metaKey : event.ctrlKey;
158
- if (event.key === 'Enter')
159
- (query ? (event.shiftKey ? this.previous : this.next) : this.replace)();
160
- else if (event.key === 'Escape')
161
- this.close();
162
- else if (query && mod && event.key.toLowerCase() === 'f')
163
- node.select();
164
- else
165
- return;
166
- event.preventDefault();
167
- };
161
+ const select = (event) => query && (this.edytor.keymap.isMac ? event.metaKey : event.ctrlKey) && node.select();
162
+ const keydown = keysOf({
163
+ Enter: (event) => (query ? (event.shiftKey ? this.previous : this.next) : this.replace)(),
164
+ Escape: () => this.close(),
165
+ f: select,
166
+ F: select
167
+ });
168
168
  return listen(node, 'keydown', keydown);
169
169
  };
170
170
  /** Stop following the document (the editor is going away). */
@@ -8,19 +8,6 @@ const controllers = new WeakMap();
8
8
  const MAX_HIGHLIGHTS = 1000;
9
9
  /** The find plugin's controller of `edytor`; `undefined` when the plugin is not listed. */
10
10
  export const findController = (edytor) => controllers.get(edytor);
11
- /** At the editor's top right, inside the viewport; measured in the overlay's frame. */
12
- const place = (edytor, find) => (host) => {
13
- const editor = edytor.node;
14
- const view = editor?.ownerDocument.defaultView;
15
- if (!editor || !view || !find.isOpen)
16
- return;
17
- const rect = editor.getBoundingClientRect();
18
- const width = host.firstElementChild?.getBoundingClientRect().width || 420;
19
- const right = Math.min(rect.right, view.innerWidth - 8);
20
- const left = `${Math.max(8, right - width)}px`;
21
- const top = `${Math.max(8, rect.top + 8)}px`;
22
- return () => Object.assign(host.style, { left, top });
23
- };
24
11
  /**
25
12
  * Find and replace (Notion's find in page), opt-in: Mod+F opens a bar over
26
13
  * the editor's top right. It searches the document's text in reading order,
@@ -63,8 +50,7 @@ export const createFindPlugin = (options = {}) => (edytor) => {
63
50
  });
64
51
  const unmountBar = mountChrome(edytor, options.bar, find, () => find.isOpen, {
65
52
  name: 'edytor-find-host',
66
- zIndex: 55,
67
- measure: place(edytor, find)
53
+ zIndex: 55
68
54
  });
69
55
  return () => {
70
56
  find.destroy();
@@ -3,8 +3,9 @@
3
3
 
4
4
  /**
5
5
  * The image chrome (`ImageControls`), in the overlay: a resize handle on
6
- * each side of the hovered image (Notion's pill, at mid-height). The
7
- * toolbar is the image toolbar plugin's.
6
+ * each side of the hovered image (Notion's pill, at mid-height), and the
7
+ * image toolbar plugin's toolbar (the app's markup, which places itself
8
+ * by the toolbar's `anchor`), while no handle drags.
8
9
  */
9
10
  let { controls }: { controls: ImageControls } = $props();
10
11
 
@@ -38,6 +39,11 @@
38
39
  {@attach controls.band.press((event) => controls.start(event, side))}
39
40
  ></div>
40
41
  {/each}
42
+ {#if controls.toolbar && !controls.drag}
43
+ <div data-edytor-image-toolbar-slot data-selecting={controls.selecting ? 'true' : undefined}>
44
+ {@render controls.toolbar.snippet(controls.toolbar)}
45
+ </div>
46
+ {/if}
41
47
  {/if}
42
48
  </div>
43
49
 
@@ -1,16 +1,15 @@
1
1
  <script module lang="ts">
2
- import { escapeHtml } from '../../clipboard/serializeClipboardFragment.js';
2
+ import { imageForms } from '../../export/forms.js';
3
+ import './image.css';
4
+
3
5
  import type { Plugin, BlockSnippetPayload } from '../../plugins.js';
4
- import { branded } from '../brand.js';
5
- import type { Edytor } from '../../edytor.svelte.js';
6
6
  import EmptySlot from '../media/EmptySlot.svelte';
7
7
  import ImageChrome from './ImageChrome.svelte';
8
- import { imageKinds } from '../../crdt/semantics.js';
8
+
9
9
  import {
10
10
  imageAlignOf,
11
11
  imageAltOf,
12
12
  imageWidthOf,
13
- imgData,
14
13
  isImageFile,
15
14
  MAX_INLINE_IMAGE_BYTES,
16
15
  MIN_IMAGE_WIDTH,
@@ -20,9 +19,12 @@
20
19
  type ImageAlign
21
20
  } from './image.js';
22
21
  import { ImageControls, imageControls } from './controls.svelte.js';
23
- import { MEDIA_ICONS, type EmptyView } from '../media/empty.svelte.js';
22
+ import { MEDIA_ICONS } from '../media/empty.svelte.js';
24
23
  import { imageEnglish, keywordsOf, labelsOf } from '../../labels.js';
25
24
  import { listen } from '../../utils/listen.js';
25
+ import { fileUploads, uploadHooks, type Uploader } from '../uploads.svelte.js';
26
+ import { emptyHosts } from '../media/EmptySlot.svelte';
27
+ import UploadPreview from '../media/UploadPreview.svelte';
26
28
 
27
29
  export {
28
30
  MAX_INLINE_IMAGE_BYTES,
@@ -38,10 +40,16 @@
38
40
 
39
41
  const px = (width: number | undefined) => (width === undefined ? undefined : `${width}px`);
40
42
 
41
- /** One view's image state: its source rule, the chrome. */
42
- type View = EmptyView & { controls: ImageControls };
43
- /** By view; the first image plugin of a view owns it, as its kind's definition (first wins). */
44
- const views = new WeakMap<Edytor, View>();
43
+ export type ImagePluginOptions = {
44
+ /**
45
+ * Upload an image file and answer its URL: an empty image's Upload,
46
+ * and the pasted and dropped image files, each placed as an image at
47
+ * once (one undo step), showing the file while it uploads, then filled
48
+ * with the URL this answers. Without it, images are only linked. The
49
+ * first image plugin listed in a view gives it.
50
+ */
51
+ upload?: Uploader;
52
+ };
45
53
 
46
54
  /**
47
55
  * Notion's image block: an "Add an image" panel until it has a source (a
@@ -49,31 +57,23 @@
49
57
  * an editable caption, its alt text, width and alignment set from the
50
58
  * chrome the plugin shows over it. Void: its only text is the caption.
51
59
  */
52
- export const createImagePlugin = (): Plugin => {
60
+ export const createImagePlugin = ({ upload }: ImagePluginOptions = {}): Plugin => {
53
61
  const plugin: Plugin = (edytor) => {
54
62
  const labels = labelsOf(edytor, 'image', imageEnglish);
55
- const own: View = {
56
- controls: new ImageControls(edytor),
57
- // An accepted source; an inline image over the cap is never stored.
58
- store: (block, value) => {
59
- const src = safeImageSrc(value);
60
- if (!src) return 'invalid';
61
- if (oversizedInlineImage(src)) return 'inline';
62
- block.edytor.patchData(block.id, [{ path: ['src'], value: src }]);
63
- return null;
64
- }
65
- };
66
- // The records read with no view (`plugin(undefined)`) keep no state.
67
- if (edytor && !views.has(edytor)) {
68
- views.set(edytor, own);
69
- imageControls.set(edytor, own.controls);
70
- }
71
- const owns = () => views.get(edytor) === own;
63
+ const uploads = uploadHooks(edytor, upload);
64
+ // The view's image state (its source rule, the chrome): the first image
65
+ // plugin of a view owns it, as its kind's definition (first wins). The
66
+ // records read with no view (`plugin(undefined)`) keep no state.
67
+ const controls = new ImageControls(edytor);
68
+ if (edytor && !imageControls.has(edytor)) imageControls.set(edytor, controls);
69
+ const owns = () => imageControls.get(edytor) === controls;
72
70
  return {
71
+ onPaste: uploads.onPaste,
73
72
  // The chrome: in the overlay, for the image under the pointer.
74
- onEdytorAttached: ({ node }) => {
75
- if (!owns()) return;
76
- const { controls } = own;
73
+ onEdytorAttached: (view) => {
74
+ const release = uploads.onEdytorAttached?.(view);
75
+ if (!owns()) return release;
76
+ const { node } = view;
77
77
  const over = (event: PointerEvent) => controls.hover(event.target);
78
78
  const leave = (event: PointerEvent) =>
79
79
  !controls.drag && controls.leave(event.relatedTarget);
@@ -94,23 +94,26 @@
94
94
  off2();
95
95
  off3();
96
96
  unmount();
97
+ if (typeof release === 'function') release();
97
98
  };
98
99
  },
99
100
  blocks: {
100
101
  image: {
101
- ...imageKinds.image,
102
+ ...imageForms().image,
102
103
  snippet: image,
103
104
  element: 'figure',
104
- // Image files: with the uploads plugin, an Upload and pasted or dropped files.
105
- upload: {
106
- accepts: isImageFile,
107
- data: (_file, answer) => {
108
- const src = storableImageSrc(answer);
109
- return src ? { src } : null;
110
- },
111
- filled: (data) => safeImageSrc(data.src) !== null,
112
- preview: true
113
- },
105
+ // Image files, with an `upload`: an Upload and pasted or dropped files.
106
+ ...(upload && {
107
+ upload: {
108
+ send: upload,
109
+ accepts: isImageFile,
110
+ data: (_file, answer) => {
111
+ const src = storableImageSrc(answer);
112
+ return src ? { src } : null;
113
+ },
114
+ filled: (data) => safeImageSrc(data.src) !== null
115
+ }
116
+ }),
114
117
  presets: [
115
118
  {
116
119
  label: labels.image,
@@ -118,32 +121,13 @@
118
121
  keywords: keywordsOf(edytor, 'block.image', ['picture', 'photo', 'img']),
119
122
  group: 'Media'
120
123
  }
121
- ],
122
- html: (block, caption) => {
123
- const src = safeImageSrc(block.data?.src);
124
- const width = imageWidthOf(block.data);
125
- const align = imageAlignOf(block.data);
126
- const img = src
127
- ? `<img src="${escapeHtml(src)}" alt="${escapeHtml(imageAltOf(block.data))}"${width ? ` width="${Math.round(width)}"` : ''}>`
128
- : '';
129
- const at = align === 'center' ? '' : ` data-align="${align}"`;
130
- return `<figure${at}>${img}<figcaption>${caption}</figcaption></figure>`;
131
- },
132
- // A `figure` holding an `img`, or a bare `img`, with an accepted
133
- // source; a pasted inline image over the cap is not imported.
134
- parse: (el) => {
135
- if (el.localName === 'img') return imgData(el) ?? undefined;
136
- const data = el.localName === 'figure' && imgData(el.querySelector('img'));
137
- if (!data) return undefined;
138
- const align = el.getAttribute('data-align');
139
- return align === 'left' || align === 'right' ? { ...data, align } : data;
140
- }
124
+ ]
141
125
  }
142
126
  }
143
127
  };
144
128
  };
145
129
 
146
- return branded('image', plugin);
130
+ return plugin;
147
131
  };
148
132
  </script>
149
133
 
@@ -151,8 +135,9 @@
151
135
  <!-- prettier-ignore -->
152
136
  {#snippet image({ block, content }: BlockSnippetPayload<ImageData>)}
153
137
  <!-- A suggestion's preview (no handle) reads only its data. -->
154
- {@const view = block.handle ? views.get(block.handle.edytor) : undefined}
138
+ {@const controls = block.handle ? imageControls.get(block.handle.edytor) : undefined}
155
139
  {@const src = safeImageSrc(block.data.src)}
140
+ {@const pending = block.handle && !src ? fileUploads(block.handle.edytor)?.of(block.id) : undefined}
156
141
  {#if src}
157
142
  <div use:block.void data-edytor-image data-align={imageAlignOf(block.data)}>
158
143
  <img
@@ -160,37 +145,24 @@
160
145
  alt={imageAltOf(block.data)}
161
146
  draggable="false"
162
147
  style:width={px(
163
- view ? view.controls.width(block.id, block.data) : imageWidthOf(block.data)
148
+ controls ? controls.width(block.id, block.data) : imageWidthOf(block.data)
164
149
  )}
165
150
  />
166
151
  </div>
152
+ {:else if pending?.status === 'uploading' && !emptyHosts.has(block.handle!.edytor)}
153
+ <!-- An upload with no empty markup to draw it: the file at once, its progress over it. -->
154
+ <div use:block.void data-edytor-image-empty>
155
+ <UploadPreview upload={pending} image />
156
+ </div>
167
157
  {:else}
168
158
  <div use:block.void data-edytor-image-empty>
169
159
  <EmptySlot
170
160
  block={block.handle}
171
161
  kind="image"
172
- viewOf={(edytor) => (edytor ? views.get(edytor) : undefined)}
162
+ viewOf={(edytor) => (edytor ? imageControls.get(edytor) : undefined)}
173
163
  />
174
164
  </div>
175
165
  {/if}<!--
176
166
  The core renders the kind's <figure> around this markup.
177
167
  --><!-- svelte-ignore a11y_figcaption_parent --><figcaption>{@render content()}</figcaption>
178
168
  {/snippet}
179
-
180
- <style>
181
- /* Where an image narrower than its block sits (Notion's alignment). */
182
- [data-edytor-image] {
183
- display: flex;
184
- position: relative;
185
- justify-content: center;
186
- }
187
- [data-edytor-image][data-align='left'] {
188
- justify-content: flex-start;
189
- }
190
- [data-edytor-image][data-align='right'] {
191
- justify-content: flex-end;
192
- }
193
- [data-edytor-image] img {
194
- max-width: 100%;
195
- }
196
- </style>
@@ -1,5 +1,7 @@
1
+ import './image.css';
1
2
  import type { Plugin } from '../../plugins.js';
2
3
  import { MAX_INLINE_IMAGE_BYTES, MIN_IMAGE_WIDTH, oversizedInlineImage, safeImageSrc, storableImageSrc, type ImageAlign } from './image.js';
4
+ import { type Uploader } from '../uploads.svelte.js';
3
5
  export { MAX_INLINE_IMAGE_BYTES, MIN_IMAGE_WIDTH, oversizedInlineImage, safeImageSrc, storableImageSrc, type ImageAlign };
4
6
  /** The data an image block reads (all optional). */
5
7
  export type ImageData = {
@@ -8,13 +10,23 @@ export type ImageData = {
8
10
  width?: number;
9
11
  align?: ImageAlign;
10
12
  };
13
+ export type ImagePluginOptions = {
14
+ /**
15
+ * Upload an image file and answer its URL: an empty image's Upload,
16
+ * and the pasted and dropped image files, each placed as an image at
17
+ * once (one undo step), showing the file while it uploads, then filled
18
+ * with the URL this answers. Without it, images are only linked. The
19
+ * first image plugin listed in a view gives it.
20
+ */
21
+ upload?: Uploader;
22
+ };
11
23
  /**
12
24
  * Notion's image block: an "Add an image" panel until it has a source (a
13
25
  * pasted link, or an upload when `upload` is given), then the image with
14
26
  * an editable caption, its alt text, width and alignment set from the
15
27
  * chrome the plugin shows over it. Void: its only text is the caption.
16
28
  */
17
- export declare const createImagePlugin: () => Plugin;
29
+ export declare const createImagePlugin: ({ upload }?: ImagePluginOptions) => Plugin;
18
30
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
19
31
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
20
32
  $$bindings?: Bindings;
@@ -1,6 +1,6 @@
1
1
  import { BandDrag } from '../bands.svelte.js';
2
2
  import { layerBox, pin, sameValues } from '../../surface/overlay.js';
3
- import { imageAlignOf, imageWidthOf, MIN_IMAGE_WIDTH } from './image.js';
3
+ import { imageAlignOf, imageWidthOf, MIN_IMAGE_WIDTH, oversizedInlineImage, safeImageSrc } from './image.js';
4
4
  /** The image element of a shown image block (none while it uploads); an image holds no block. */
5
5
  const imageOf = (node) => node?.querySelector('[data-edytor-image] > img') ?? null;
6
6
  /**
@@ -29,13 +29,27 @@ export class ImageControls {
29
29
  /** The handles' gesture (a resize band's: press, view-only preview, one write). @internal */
30
30
  band;
31
31
  /** The view's image toolbar, when its plugin is listed. @internal */
32
- toolbar;
32
+ toolbar = $state();
33
33
  constructor(
34
34
  /** @internal */
35
35
  edytor) {
36
36
  this.edytor = edytor;
37
37
  this.band = new BandDrag(edytor);
38
38
  }
39
+ /**
40
+ * Store a link or an upload's answer on `block` (an empty image's): an
41
+ * accepted source; an inline image over the cap is never stored.
42
+ * @internal
43
+ */
44
+ store = (block, value) => {
45
+ const src = safeImageSrc(value);
46
+ if (!src)
47
+ return 'invalid';
48
+ if (oversizedInlineImage(src))
49
+ return 'inline';
50
+ block.edytor.patchData(block.id, [{ path: ['src'], value: src }]);
51
+ return null;
52
+ };
39
53
  /** The handle being dragged. */
40
54
  get drag() {
41
55
  return this.band.drag;
@@ -0,0 +1,17 @@
1
+ /* Loaded by the plugin's module (a component's own <style> is dropped when only
2
+ its module is used: the plugin factory, never the component). */
3
+ /* Where an image narrower than its block sits (Notion's alignment). */
4
+ [data-edytor-image] {
5
+ display: flex;
6
+ position: relative;
7
+ justify-content: center;
8
+ }
9
+ [data-edytor-image][data-align='left'] {
10
+ justify-content: flex-start;
11
+ }
12
+ [data-edytor-image][data-align='right'] {
13
+ justify-content: flex-end;
14
+ }
15
+ [data-edytor-image] img {
16
+ max-width: 100%;
17
+ }
@@ -11,7 +11,6 @@ import type { Attachment } from 'svelte/attachments';
11
11
  import type { Snippet } from 'svelte';
12
12
  import type { Plugin } from '../../plugins.js';
13
13
  import { type ImageAlign } from './image.js';
14
- import { type ImageControls } from './controls.svelte.js';
15
14
  /**
16
15
  * What a `toolbar` snippet receives: the image the chrome is on (`box`),
17
16
  * its alignment (`align`) and alt text (`alt`, `setAlt`), the alt field's
@@ -20,9 +19,11 @@ import { type ImageControls } from './controls.svelte.js';
20
19
  */
21
20
  export declare class ImageToolbar {
22
21
  #private;
23
- constructor(
24
- /** @internal */
25
- controls: ImageControls);
22
+ /**
23
+ * Where the toolbar stands (`ChromeAnchor`): the image the chrome is
24
+ * on, whose top right your `toolbar` places itself in.
25
+ */
26
+ readonly anchor: import("../chrome.js").ChromeAnchor;
26
27
  /** The measured image the toolbar is on (`null`: none). */
27
28
  get box(): import("./controls.svelte.js").ImageBox | null;
28
29
  /** The view is readonly (reactive). */