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
@@ -1,8 +1,9 @@
1
1
  <script module lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { Plugin } from '../../plugins.js';
4
- import { escapeHtml, fileNameOf, safeMediaSrc, type FileRowPayload } from './media.js';
4
+ import { type FileRowPayload } from './media.js';
5
5
  import { sourcePlugin } from './SourceBlock.svelte';
6
+ import type { Uploader } from '../uploads.svelte.js';
6
7
 
7
8
  export type { FileRowPayload };
8
9
 
@@ -13,6 +14,14 @@
13
14
  * downloads the file. The first file plugin listed in a view gives its row.
14
15
  */
15
16
  row?: Snippet<[FileRowPayload]>;
17
+ /**
18
+ * Upload a file and answer its URL: an empty file block's Upload, and
19
+ * the pasted and dropped files no other kind takes, each placed as a
20
+ * file block at once (one undo step), its row showing the file's name,
21
+ * size and progress, then filled with the URL this answers. Without
22
+ * it, files are only linked.
23
+ */
24
+ upload?: Uploader;
16
25
  };
17
26
 
18
27
  /**
@@ -24,24 +33,7 @@
24
33
  sourcePlugin(
25
34
  'file',
26
35
  ['attachment', 'upload', 'pdf', 'document'],
27
- {
28
- html: (block, caption) => {
29
- const src = safeMediaSrc(block.data?.src);
30
- const name = String(block.data?.name || (src && fileNameOf(src)) || '');
31
- const link = src ? `<a href="${escapeHtml(src)}">${escapeHtml(name)}</a>` : '';
32
- return `<figure data-edytor-file>${link}<figcaption>${caption}</figcaption></figure>`;
33
- },
34
- plain: (block, caption) =>
35
- [safeMediaSrc(block.data?.src), caption].filter(Boolean).join('\n'),
36
- // Its own export: a `figure[data-edytor-file]` holding the link.
37
- parse: (el) => {
38
- if (el.localName !== 'figure' || !el.hasAttribute('data-edytor-file')) return;
39
- const anchor = el.querySelector('a[href]');
40
- const src = safeMediaSrc(anchor?.getAttribute('href'));
41
- const name = anchor?.textContent?.trim().slice(0, 300);
42
- return src ? { src, name: name || fileNameOf(src) } : undefined;
43
- }
44
- },
45
- { row: options.row }
36
+ { row: options.row },
37
+ options.upload
46
38
  );
47
39
  </script>
@@ -1,6 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { Plugin } from '../../plugins.js';
3
3
  import { type FileRowPayload } from './media.js';
4
+ import type { Uploader } from '../uploads.svelte.js';
4
5
  export type { FileRowPayload };
5
6
  export type FilePluginOptions = {
6
7
  /**
@@ -9,6 +10,14 @@ export type FilePluginOptions = {
9
10
  * downloads the file. The first file plugin listed in a view gives its row.
10
11
  */
11
12
  row?: Snippet<[FileRowPayload]>;
13
+ /**
14
+ * Upload a file and answer its URL: an empty file block's Upload, and
15
+ * the pasted and dropped files no other kind takes, each placed as a
16
+ * file block at once (one undo step), its row showing the file's name,
17
+ * size and progress, then filled with the URL this answers. Without
18
+ * it, files are only linked.
19
+ */
20
+ upload?: Uploader;
12
21
  };
13
22
  /**
14
23
  * Notion's file block: an attachment, uploaded through `upload` (or
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" module>
2
2
  import type { Snippet } from 'svelte';
3
+ import type { MediaEmptyController } from './empty.svelte.js';
3
4
  import type { Plugin } from '../../plugins.js';
4
5
  import { emptyHosts } from './EmptySlot.svelte';
5
- import Self from './MediaEmpty.svelte';
6
6
 
7
7
  export type MediaEmptyOptions = {
8
8
  /**
@@ -23,60 +23,7 @@
23
23
  export const createMediaEmptyPlugin =
24
24
  ({ empty }: MediaEmptyOptions): Plugin =>
25
25
  (edytor) => {
26
- if (edytor && !emptyHosts.has(edytor)) emptyHosts.set(edytor, { Host: Self, empty });
26
+ if (edytor && !emptyHosts.has(edytor)) emptyHosts.set(edytor, empty);
27
27
  return {};
28
28
  };
29
29
  </script>
30
-
31
- <script lang="ts">
32
- import { untrack } from 'svelte';
33
- import type { Block } from '../../block/block.svelte.js';
34
- import type { Edytor } from '../../edytor.svelte.js';
35
- import type { MediaKind } from './media.js';
36
- import { MediaEmptyController, type EmptyView } from './empty.svelte.js';
37
- import { fileUploads } from '../uploads.svelte.js';
38
-
39
- /**
40
- * An image's or a media block's empty state: a `MediaEmptyController`,
41
- * which the view's `empty` snippet draws. The kind in the
42
- * block's view (`viewOf`) stores the link, or an uploaded file's URL
43
- * (only on a block still empty when it lands). A readonly view, or a
44
- * suggestion's preview (no `block`), may not write: no control may run
45
- * `upload`. A pasted or dropped file shows its upload (`pending`); a
46
- * failed one opens the panel with its error; data answered while the
47
- * block was not shown fills it as soon as it shows.
48
- */
49
- let {
50
- block,
51
- kind,
52
- viewOf,
53
- empty
54
- }: {
55
- block: Block | undefined;
56
- kind: MediaKind | 'image';
57
- /** The kind in a view: how it stores a source. */
58
- viewOf: (edytor: Edytor | undefined) => EmptyView | undefined;
59
- /** The view's `empty` snippet. */
60
- empty: Snippet<[MediaEmptyController]>;
61
- } = $props();
62
- const panel = untrack(() => new MediaEmptyController(block, kind, viewOf(block?.edytor)));
63
- /** The upload of a pasted or dropped file into this block, if any. */
64
- const pending = $derived(panel.pending);
65
- // After the flush that shows the block: never inside the transaction that showed it.
66
- $effect(() => {
67
- if (pending?.status === 'landed' && block && !block.edytor.readonly) {
68
- const id = block.id;
69
- untrack(() => fileUploads(block.edytor)?.fill(id));
70
- }
71
- });
72
- // The panel opens on a failed upload, with its error; the user closes it.
73
- $effect(() => {
74
- if (pending?.status === 'failed')
75
- untrack(() => {
76
- panel.open = true;
77
- panel.failed = 'upload';
78
- });
79
- });
80
- </script>
81
-
82
- {@render empty(panel)}
@@ -1,4 +1,5 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import type { MediaEmptyController } from './empty.svelte.js';
2
3
  import type { Plugin } from '../../plugins.js';
3
4
  export type MediaEmptyOptions = {
4
5
  /**
@@ -16,18 +17,21 @@ export type MediaEmptyOptions = {
16
17
  * listed claims the view.
17
18
  */
18
19
  export declare const createMediaEmptyPlugin: ({ empty }: MediaEmptyOptions) => Plugin;
19
- import type { Block } from '../../block/block.svelte.js';
20
- import type { Edytor } from '../../edytor.svelte.js';
21
- import type { MediaKind } from './media.js';
22
- import { MediaEmptyController, type EmptyView } from './empty.svelte.js';
23
- type $$ComponentProps = {
24
- block: Block | undefined;
25
- kind: MediaKind | 'image';
26
- /** The kind in a view: how it stores a source. */
27
- viewOf: (edytor: Edytor | undefined) => EmptyView | undefined;
28
- /** The view's `empty` snippet. */
29
- empty: Snippet<[MediaEmptyController]>;
30
- };
31
- declare const MediaEmpty: import("svelte").Component<$$ComponentProps, {}, "">;
32
- type MediaEmpty = ReturnType<typeof MediaEmpty>;
20
+ 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> {
21
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
22
+ $$bindings?: Bindings;
23
+ } & Exports;
24
+ (internal: unknown, props: {
25
+ $$events?: Events;
26
+ $$slots?: Slots;
27
+ }): Exports & {
28
+ $set?: any;
29
+ $on?: any;
30
+ };
31
+ z_$$bindings?: Bindings;
32
+ }
33
+ declare const MediaEmpty: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
34
+ [evt: string]: CustomEvent<any>;
35
+ }, {}, {}, string>;
36
+ type MediaEmpty = InstanceType<typeof MediaEmpty>;
33
37
  export default MediaEmpty;
@@ -1,16 +1,26 @@
1
1
  <script module lang="ts">
2
- import type { BlockDefinition, BlockSnippetPayload, Plugin } from '../../plugins.js';
2
+ import { capped, mediaForms } from '../../export/forms.js';
3
+ import type { Block } from '../../block/block.svelte.js';
4
+ import type { BlockSnippetPayload, Plugin } from '../../plugins.js';
3
5
  import EmptySlot from './EmptySlot.svelte';
4
6
  import Control from '../Control.svelte';
5
7
  import { labelsOf, mediaEnglish } from '../../labels.js';
8
+ import { emptyHosts } from './EmptySlot.svelte';
9
+ import UploadPreview from './UploadPreview.svelte';
10
+ import { fileUploads, uploadHooks, type Uploader } from '../uploads.svelte.js';
6
11
  import {
7
- claimed,
8
- escapeHtml,
12
+ EMBED_ALLOW,
13
+ EMBED_SANDBOX,
14
+ embedSourceOf,
15
+ fileNameFor,
9
16
  fileNameOf,
17
+ safeWebUrl,
18
+ type BookmarkCardPayload,
19
+ type EmbedFallbackPayload,
20
+ type MediaKind,
10
21
  mediaControl,
11
22
  mediaLink,
12
23
  mediaPlugin,
13
- mediaSourceOf,
14
24
  mediaView,
15
25
  safeMediaSrc,
16
26
  LINK_REL,
@@ -20,8 +30,47 @@
20
30
 
21
31
  type SourceKind = 'video' | 'audio' | 'file';
22
32
 
23
- /** The view's file row markup, if the app gave one. */
33
+ /** The view's markup for a file's row, a bookmark's card, an unplayable embed's link, if the app gave one. */
24
34
  const rowOf = mediaControl('file', 'row');
35
+ const cardOf = mediaControl('bookmark', 'card');
36
+ const fallbackOf = mediaControl('embed', 'fallback');
37
+
38
+ /**
39
+ * The view's embed allowlist: the first embed plugin listed in it (first
40
+ * wins, as for the kind). Never a broader list: no entry plays nothing.
41
+ */
42
+ const providers = (block: Block | undefined) =>
43
+ mediaView(block?.edytor, 'embed')?.providers ?? [];
44
+
45
+ /** The fields of a bookmark's `preview` it stores and shows: text capped, images `safeWebUrl` only. */
46
+ export const previewData = (preview: Record<string, unknown> | null | undefined) => {
47
+ const fields = {
48
+ title: capped(preview?.title, 300),
49
+ description: capped(preview?.description, 1000),
50
+ image: safeWebUrl(preview?.image) ?? undefined,
51
+ icon: safeWebUrl(preview?.icon) ?? undefined
52
+ };
53
+ return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
54
+ };
55
+
56
+ /** A file's row: its source, name and size, and whether it is uploading. */
57
+ const fileRow = (
58
+ block: Block | undefined,
59
+ src: string,
60
+ name: string,
61
+ size: number | undefined,
62
+ progress?: number | null
63
+ ): FileRowPayload => ({
64
+ block,
65
+ src,
66
+ name,
67
+ size,
68
+ sizeLabel:
69
+ size === undefined ? '' : labelsOf(block?.edytor, 'media', mediaEnglish).fileSize(size),
70
+ uploading: progress !== undefined,
71
+ progress: progress ?? null,
72
+ link: mediaLink({ draggable: 'false', download: name })
73
+ });
25
74
 
26
75
  /** The files each kind takes: a video, an audio track, any other file. */
27
76
  const ACCEPTS: Record<SourceKind, (file: File) => boolean> = {
@@ -48,32 +97,22 @@
48
97
  * attachment; Notion's): a media kind (`mediaPlugin`) drawn by this
49
98
  * snippet, a link or an uploaded file stored as one data write (a file's
50
99
  * whole data, its source, name and size; a video's or audio track's
51
- * `src`), and with an `upload`, the pasted and dropped files it takes
52
- * (`media.files`, its first listing in a view). A video or an audio track
100
+ * `src`), and with an `upload` (its uploader), the pasted, dropped and
101
+ * picked files it takes (`media.files`). A video or an audio track
53
102
  * exports as its tag in a `figure` and imports from one (bare, or in a
54
- * `figure`) with a safe `src` or `source`; the file kind brings its own
55
- * (`record`), and its `row` markup (`own`).
103
+ * `figure`) with a safe `src` or `source`, a file as its link; the file
104
+ * kind brings its `row` markup (`own`).
56
105
  */
57
106
  export const sourcePlugin = (
58
107
  kind: SourceKind,
59
108
  keywords: string[],
60
- record: Partial<BlockDefinition> = {
61
- html: (block, caption) => {
62
- const src = safeMediaSrc(block.data?.src);
63
- // The tag, then its attributes: `<video src="…" controls></video>`.
64
- const media = src ? `<${kind}` + ` src="${escapeHtml(src)}" controls></${kind}>` : '';
65
- return `<figure>${media}<figcaption>${caption}</figcaption></figure>`;
66
- },
67
- parse: (el) => {
68
- const src = mediaSourceOf(claimed(el, kind));
69
- return src ? { src } : undefined;
70
- }
71
- },
72
- own: Pick<MediaView, 'row'> = {}
109
+ own: Pick<MediaView, 'row'> = {},
110
+ upload?: Uploader
73
111
  ): Plugin =>
74
- mediaPlugin(kind, keywords, () => {
112
+ mediaPlugin(kind, keywords, (edytor) => {
75
113
  return {
76
114
  ...own,
115
+ hooks: uploadHooks(edytor, upload),
77
116
  store: (block, value, file) => {
78
117
  const data = sourceData(kind, value, file);
79
118
  if (!data) return 'invalid';
@@ -83,47 +122,111 @@
83
122
  },
84
123
  record: {
85
124
  snippet: source,
86
- // Its files: with the uploads plugin, an Upload and pasted or dropped files.
87
- upload: {
88
- accepts: ACCEPTS[kind],
89
- fallback: kind === 'file',
90
- data: (file, answer) => sourceData(kind, answer, file),
91
- filled: (data) => safeMediaSrc(data.src) !== null
92
- },
93
- ...record
125
+ // Its files, with an `upload`: an Upload and pasted or dropped files.
126
+ ...(upload && {
127
+ upload: {
128
+ send: upload,
129
+ accepts: ACCEPTS[kind],
130
+ fallback: kind === 'file',
131
+ data: (file: File, answer: string) => sourceData(kind, answer, file),
132
+ filled: (data: Record<string, unknown>) => safeMediaSrc(data.src) !== null
133
+ }
134
+ }),
135
+ ...mediaForms()[kind]
94
136
  }
95
137
  };
96
138
  });
139
+
140
+ export { source };
97
141
  </script>
98
142
 
99
143
  <!--
100
- A file (its name and size, linking to it), a video or an audio track in
101
- the browser's player, or the empty block until it has a source; its
102
- caption below.
144
+ A media block: a file (its name and size, linking to it), a video or an
145
+ audio track in the browser's player, an embed's player (or its link), a
146
+ bookmark's card, or the empty block until it has a source; its caption below.
103
147
  -->
104
148
  {#snippet source({ block, content }: BlockSnippetPayload<Record<string, unknown>>)}
105
- {@const src = safeMediaSrc(block.data.src)}
106
- {#if src && block.type === 'file'}
107
- {@const name =
108
- typeof block.data.name === 'string' && block.data.name ? block.data.name : fileNameOf(src)}
109
- {@const size = typeof block.data.size === 'number' ? block.data.size : undefined}
149
+ {@const url = safeWebUrl(block.data.url)}
150
+ {@const src =
151
+ block.type === 'embed' || block.type === 'bookmark' ? url : safeMediaSrc(block.data.src)}
152
+ {@const player =
153
+ block.type === 'embed' && block.handle ? embedSourceOf(url, providers(block.handle)) : null}
154
+ {@const pending =
155
+ block.handle && !src ? fileUploads(block.handle.edytor)?.of(block.id) : undefined}
156
+ {#if player}
157
+ {@const { height, aspectRatio = '16 / 9' } = player.provider}
158
+ <div
159
+ use:block.void
160
+ data-edytor-embed
161
+ style:aspect-ratio={height ? undefined : aspectRatio}
162
+ style:height={height ? `${height}px` : undefined}
163
+ >
164
+ <iframe
165
+ src={player.src}
166
+ title={player.provider.name}
167
+ sandbox={EMBED_SANDBOX}
168
+ allow={EMBED_ALLOW}
169
+ referrerpolicy="strict-origin-when-cross-origin"
170
+ loading="lazy"
171
+ allowfullscreen
172
+ style="display: block; width: 100%; height: 100%; border: 0"
173
+ ></iframe>
174
+ </div>
175
+ {:else if url && block.type === 'embed'}
176
+ <!-- A link no provider plays (a peer may store any), or a suggestion's preview (no handle, so no
177
+ view's allowlist): a plain link, never a frame. -->
178
+ <div use:block.void data-edytor-embed-unsupported>
179
+ <Control
180
+ own={fallbackOf}
181
+ initial={link}
182
+ payload={{ block: block.handle, url, link: mediaLink() }}
183
+ />
184
+ </div>
185
+ {:else if url && block.type === 'bookmark'}
186
+ <div use:block.void data-edytor-bookmark>
187
+ <Control
188
+ own={cardOf}
189
+ initial={card}
190
+ payload={{
191
+ block: block.handle,
192
+ url,
193
+ ...previewData(block.data),
194
+ link: mediaLink({ draggable: 'false' })
195
+ } as BookmarkCardPayload}
196
+ />
197
+ </div>
198
+ {:else if src && block.type === 'file'}
110
199
  <div use:block.void data-edytor-file>
111
200
  <Control
112
201
  own={rowOf}
113
202
  {initial}
114
- payload={{
115
- block: block.handle,
203
+ payload={fileRow(
204
+ block.handle,
116
205
  src,
117
- name,
118
- size,
119
- sizeLabel:
120
- size === undefined
121
- ? ''
122
- : labelsOf(block.handle?.edytor, 'media', mediaEnglish).fileSize(size),
123
- link: mediaLink({ draggable: 'false', download: name })
124
- }}
206
+ fileNameFor(block.data, src),
207
+ typeof block.data.size === 'number' ? block.data.size : undefined
208
+ )}
125
209
  />
126
210
  </div>
211
+ {:else if !src && pending?.status === 'uploading' && !emptyHosts.has(block.handle!.edytor)}
212
+ <!-- An upload with no empty markup to draw it: the file's name and size at once, its progress. -->
213
+ <div use:block.void data-edytor-media-empty>
214
+ {#if block.type === 'file' && pending.preview}
215
+ <Control
216
+ own={rowOf}
217
+ {initial}
218
+ payload={fileRow(
219
+ block.handle,
220
+ pending.preview,
221
+ pending.name,
222
+ pending.size,
223
+ pending.progress
224
+ )}
225
+ />
226
+ {:else}
227
+ <UploadPreview upload={pending} />
228
+ {/if}
229
+ </div>
127
230
  {:else if src && block.type === 'video'}
128
231
  <div use:block.void data-edytor-video>
129
232
  <!-- svelte-ignore a11y_media_has_caption -->
@@ -144,8 +247,8 @@
144
247
  <div use:block.void data-edytor-media-empty>
145
248
  <EmptySlot
146
249
  block={block.handle}
147
- kind={block.type as SourceKind}
148
- viewOf={(edytor) => mediaView(edytor, block.type as SourceKind)}
250
+ kind={block.type as MediaKind}
251
+ viewOf={(edytor) => mediaView(edytor, block.type as MediaKind)}
149
252
  />
150
253
  </div>
151
254
  {/if}
@@ -154,11 +257,34 @@
154
257
  <figcaption>{@render content()}</figcaption>
155
258
  {/snippet}
156
259
 
260
+ <!-- An embed no provider plays: the link itself. -->
261
+ {#snippet link({ url, link }: EmbedFallbackPayload)}
262
+ <a href={url} target="_blank" rel={LINK_REL} {@attach link}>{url}</a>
263
+ {/snippet}
264
+
265
+ <!-- Notion's card: the page's title, description and URL (with its icon) beside its cover. -->
266
+ {#snippet card({ url, title, description, image, icon, link }: BookmarkCardPayload)}
267
+ <a href={url} target="_blank" rel={LINK_REL} draggable="false" {@attach link}>
268
+ <span data-edytor-bookmark-text>
269
+ <span data-edytor-bookmark-title>{title ?? url}</span>
270
+ {#if description}<span data-edytor-bookmark-description>{description}</span>{/if}
271
+ <span data-edytor-bookmark-url>
272
+ {#if icon}<img src={icon} alt="" draggable="false" />{/if}<span>{url}</span>
273
+ </span>
274
+ </span>
275
+ {#if image}
276
+ <span data-edytor-bookmark-cover><img src={image} alt="" draggable="false" /></span>
277
+ {/if}
278
+ </a>
279
+ {/snippet}
280
+
157
281
  <!-- A file: its name and size, a link downloading it. -->
158
- {#snippet initial({ src, name, sizeLabel, link }: FileRowPayload)}
282
+ {#snippet initial({ src, name, sizeLabel, link, uploading, progress }: FileRowPayload)}
159
283
  <a href={src} target="_blank" rel={LINK_REL} draggable="false" download={name} {@attach link}>
160
284
  <span aria-hidden="true">📎</span>
161
285
  <span data-edytor-file-name>{name}</span>
162
286
  {#if sizeLabel}<span data-edytor-file-size>{sizeLabel}</span>{/if}
163
287
  </a>
288
+ {#if uploading}<progress data-edytor-file-progress value={progress ?? undefined} max="1"
289
+ ></progress>{/if}
164
290
  {/snippet}
@@ -1,18 +1,25 @@
1
- import type { BlockDefinition, Plugin } from '../../plugins.js';
1
+ import type { BlockSnippetPayload, Plugin } from '../../plugins.js';
2
+ import { type Uploader } from '../uploads.svelte.js';
2
3
  import { type MediaView } from './media.js';
4
+ declare const source: ({ block, content }: BlockSnippetPayload<Record<string, unknown>>) => ReturnType<import("svelte").Snippet>;
3
5
  type SourceKind = 'video' | 'audio' | 'file';
6
+ /** The fields of a bookmark's `preview` it stores and shows: text capped, images `safeWebUrl` only. */
7
+ export declare const previewData: (preview: Record<string, unknown> | null | undefined) => {
8
+ [k: string]: string | undefined;
9
+ };
4
10
  /**
5
11
  * The plugin of a kind holding one source (a video, an audio track, an
6
12
  * attachment; Notion's): a media kind (`mediaPlugin`) drawn by this
7
13
  * snippet, a link or an uploaded file stored as one data write (a file's
8
14
  * whole data, its source, name and size; a video's or audio track's
9
- * `src`), and with an `upload`, the pasted and dropped files it takes
10
- * (`media.files`, its first listing in a view). A video or an audio track
15
+ * `src`), and with an `upload` (its uploader), the pasted, dropped and
16
+ * picked files it takes (`media.files`). A video or an audio track
11
17
  * exports as its tag in a `figure` and imports from one (bare, or in a
12
- * `figure`) with a safe `src` or `source`; the file kind brings its own
13
- * (`record`), and its `row` markup (`own`).
18
+ * `figure`) with a safe `src` or `source`, a file as its link; the file
19
+ * kind brings its `row` markup (`own`).
14
20
  */
15
- export declare const sourcePlugin: (kind: SourceKind, keywords: string[], record?: Partial<BlockDefinition>, own?: Pick<MediaView, "row">) => Plugin;
21
+ export declare const sourcePlugin: (kind: SourceKind, keywords: string[], own?: Pick<MediaView, "row">, upload?: Uploader) => Plugin;
22
+ export { source };
16
23
  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> {
17
24
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
18
25
  $$bindings?: Bindings;
@@ -0,0 +1,32 @@
1
+ <script module lang="ts">
2
+ import './upload.css';
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import { getContext } from 'svelte';
7
+ import type { Edytor } from '../../edytor.svelte.js';
8
+ import type { FileUpload } from '../uploads.svelte.js';
9
+ import { labelsOf, mediaEnglish } from '../../labels.js';
10
+
11
+ /**
12
+ * A file uploading, the plugin's own markup when the view has no empty
13
+ * markup to draw it: an image at once from its object URL (this view's
14
+ * only, never stored) with the progress over it; another file's name and
15
+ * size, then the progress (indeterminate until `upload` reports one).
16
+ */
17
+ let {
18
+ upload,
19
+ image = false
20
+ }: { upload: Extract<FileUpload, { status: 'uploading' }>; image?: boolean } = $props();
21
+ const labels = labelsOf(getContext<Edytor>('edytor'), 'media', mediaEnglish);
22
+ </script>
23
+
24
+ <div data-edytor-upload data-edytor-upload-image={image ? '' : undefined} role="status">
25
+ {#if image && upload.preview}
26
+ <img src={upload.preview} alt={upload.name} draggable="false" />
27
+ {:else}
28
+ <span data-edytor-file-name>{upload.name}</span>
29
+ <span data-edytor-file-size>{labels.fileSize(upload.size)}</span>
30
+ {/if}
31
+ <progress value={upload.progress ?? undefined} max="1" aria-label={labels.uploading}></progress>
32
+ </div>
@@ -0,0 +1,11 @@
1
+ import './upload.css';
2
+ import type { FileUpload } from '../uploads.svelte.js';
3
+ type $$ComponentProps = {
4
+ upload: Extract<FileUpload, {
5
+ status: 'uploading';
6
+ }>;
7
+ image?: boolean;
8
+ };
9
+ declare const UploadPreview: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type UploadPreview = ReturnType<typeof UploadPreview>;
11
+ export default UploadPreview;
@@ -1,6 +1,17 @@
1
1
  <script module lang="ts">
2
2
  import type { Plugin } from '../../plugins.js';
3
3
  import { sourcePlugin } from './SourceBlock.svelte';
4
+ import type { Uploader } from '../uploads.svelte.js';
5
+
6
+ export type VideoPluginOptions = {
7
+ /**
8
+ * Upload a video file and answer its URL: an empty block's Upload, and
9
+ * the pasted and dropped video files, each placed as a block at once
10
+ * (one undo step), showing its name and progress, then filled with the
11
+ * URL this answers. Without it, videos are only linked.
12
+ */
13
+ upload?: Uploader;
14
+ };
4
15
 
5
16
  /**
6
17
  * Notion's video block: a video file (an `http(s)` link, or an upload
@@ -8,5 +19,6 @@
8
19
  * A YouTube or Vimeo link is an embed (`createEmbedPlugin()`). Void: its only
9
20
  * text is the caption.
10
21
  */
11
- export const createVideoPlugin = (): Plugin => sourcePlugin('video', ['movie', 'mp4', 'clip']);
22
+ export const createVideoPlugin = ({ upload }: VideoPluginOptions = {}): Plugin =>
23
+ sourcePlugin('video', ['movie', 'mp4', 'clip'], {}, upload);
12
24
  </script>
@@ -1,11 +1,21 @@
1
1
  import type { Plugin } from '../../plugins.js';
2
+ import type { Uploader } from '../uploads.svelte.js';
3
+ export type VideoPluginOptions = {
4
+ /**
5
+ * Upload a video file and answer its URL: an empty block's Upload, and
6
+ * the pasted and dropped video files, each placed as a block at once
7
+ * (one undo step), showing its name and progress, then filled with the
8
+ * URL this answers. Without it, videos are only linked.
9
+ */
10
+ upload?: Uploader;
11
+ };
2
12
  /**
3
13
  * Notion's video block: a video file (an `http(s)` link, or an upload
4
14
  * through `upload`) in the browser's player, with an editable caption.
5
15
  * A YouTube or Vimeo link is an embed (`createEmbedPlugin()`). Void: its only
6
16
  * text is the caption.
7
17
  */
8
- export declare const createVideoPlugin: () => Plugin;
18
+ export declare const createVideoPlugin: ({ upload }?: VideoPluginOptions) => Plugin;
9
19
  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> {
10
20
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
11
21
  $$bindings?: Bindings;
@@ -52,16 +52,25 @@ export declare class MediaEmptyController {
52
52
  * block). Show a passive placeholder then.
53
53
  */
54
54
  get readonly(): boolean;
55
- /** The view lists the uploads plugin and the kind takes files: offer Upload. */
55
+ /** The kind's plugin was given an `upload`: offer Upload. */
56
56
  get canUpload(): boolean;
57
57
  /** The upload of a pasted or dropped file into this block, if any (its `status`, `progress`, `preview`). */
58
58
  get pending(): FileUpload | undefined;
59
+ /**
60
+ * The file uploading into this block, shown at once: its object URL
61
+ * (this view's only, never stored; revoked once the upload ends), else `null`.
62
+ */
63
+ get preview(): string | null;
64
+ /** The share of the upload sent, `0` to `1`; `null` before a report, or with no upload. */
65
+ get progress(): number | null;
59
66
  /** Embed `value` (default the `draft`): the block takes the link, or `failed` says why not. */
60
67
  embed: (value?: string) => void;
61
68
  /**
62
- * Upload `file` with the view's uploader (`createUploadsPlugin`) and store the URL it answers,
63
- * only on the block as it was picked from: still in the tree, of its kind
64
- * and empty (a peer may have set a source, or deleted it, meanwhile).
69
+ * Upload `file` with its kind's uploader (its plugin's `upload`), the
70
+ * block showing it meanwhile (`pending`, `preview`, `progress`), and
71
+ * store the URL it answers, only on the block as it was picked from:
72
+ * still in the tree, of its kind and empty (a peer may have set a
73
+ * source, or deleted it, meanwhile).
65
74
  */
66
75
  upload: (picked: File) => Promise<void>;
67
76
  /** The link field (`{@attach empty.field}`): Enter embeds its `draft`. */