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
@@ -1,6 +1,7 @@
1
1
  import { type Snippet } from 'svelte';
2
2
  import type { Attachment } from 'svelte/attachments';
3
3
  import type { Edytor } from '../../edytor.svelte.js';
4
+ export { TEX, expressionOf, parseEquation, sourceMathml, type EquationData } from './tex.js';
4
5
  /** The part of KaTeX the plugin uses: `katex.renderToString`. */
5
6
  export type KatexLike = {
6
7
  renderToString: (tex: string, options?: Record<string, unknown>) => string;
@@ -66,20 +67,12 @@ export type EquationErrorPayload = {
66
67
  /** The view's word for it (`equation.invalid`). */
67
68
  label: string;
68
69
  };
69
- /** The data of a block equation and of an inline one: its TeX source. */
70
- export type EquationData = {
71
- expression?: string;
72
- };
73
70
  /** What drawing an equation gives: KaTeX's markup, its error, or nothing yet (the source shows). */
74
71
  export type Drawn = {
75
72
  html: string;
76
73
  } | {
77
74
  error: string;
78
75
  } | null;
79
- /** The MathML annotation that carries an equation's TeX source. */
80
- export declare const TEX = "application/x-tex";
81
- /** An equation's TeX source: its `expression`, a string. */
82
- export declare const expressionOf: (data: unknown) => string;
83
76
  /**
84
77
  * KaTeX for one plugin: loaded once, the first time an equation is drawn in
85
78
  * a browser, and reactive (an equation drawn before it loads shows its
@@ -104,13 +97,6 @@ export declare class EquationRenderer {
104
97
  mathml: (tex: string, display: boolean) => string;
105
98
  private options;
106
99
  }
107
- /**
108
- * HTML import: the TeX source an equation's element carries — a `<math>`'s
109
- * `application/x-tex` annotation (or its `alttext`), alone or in KaTeX's
110
- * markup (`.katex`, `.katex-display`) — when the element is an equation of
111
- * the display asked (`display="block"`: a block), else `undefined`.
112
- */
113
- export declare const parseEquation: (element: HTMLElement, display: boolean) => EquationData | undefined;
114
100
  /** The equation being edited: a block equation, or an inline one (`atom`) in block `block`. */
115
101
  export type EquationTarget = {
116
102
  block: string;
@@ -141,6 +127,12 @@ export declare class EquationEditor {
141
127
  target: EquationTarget | null;
142
128
  /** The view is readonly (reactive): the editor closes. */
143
129
  get readonly(): boolean;
130
+ /**
131
+ * Where the panel stands: the edited equation (`ChromeAnchor`; Notion
132
+ * centers it under a block equation, `display`, and starts it under an
133
+ * inline one).
134
+ */
135
+ get anchor(): import("../chrome.js").ChromeAnchor;
144
136
  /** A block equation is edited (a field of several lines). */
145
137
  get display(): boolean;
146
138
  /** The edited equation's TeX source (reactive through its block's cell). */
@@ -3,15 +3,10 @@ import { blockSelection } from '../../session/selection.js';
3
3
  import { InlineBlock } from '../../block/inlineBlock.svelte.js';
4
4
  import { takeKeys } from '../../events/onFocus.js';
5
5
  import { Anchored } from '../anchored.svelte.js';
6
- import { escapeHtml } from '../../clipboard/serializeClipboardFragment.js';
6
+ import { expressionOf, sourceMathml } from './tex.js';
7
+ export { TEX, expressionOf, parseEquation, sourceMathml } from './tex.js';
7
8
  import { all, listen } from '../../utils/listen.js';
8
- /** The MathML annotation that carries an equation's TeX source. */
9
- export const TEX = 'application/x-tex';
10
- /** An equation's TeX source: its `expression`, a string. */
11
- export const expressionOf = (data) => {
12
- const value = data?.expression;
13
- return typeof value === 'string' ? value : '';
14
- };
9
+ import { keysOf } from '../list.svelte.js';
15
10
  /** The cap of the drawings each renderer keeps. */
16
11
  const CACHE = 256;
17
12
  /**
@@ -73,9 +68,7 @@ export class EquationRenderer {
73
68
  // TeX KaTeX cannot read: the source as text, below.
74
69
  }
75
70
  }
76
- const source = escapeHtml(tex);
77
- return (`<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"${display ? ' display="block"' : ''}>` +
78
- `<semantics><mrow><mtext>${source}</mtext></mrow><annotation encoding="${TEX}">${source}</annotation></semantics></math></span>`);
71
+ return sourceMathml(tex, display);
79
72
  };
80
73
  options = (display, output) => ({
81
74
  displayMode: display,
@@ -97,29 +90,6 @@ export class EquationRenderer {
97
90
  }, (error) => console.error('[edytor] KaTeX failed to load', error));
98
91
  };
99
92
  }
100
- /**
101
- * HTML import: the TeX source an equation's element carries — a `<math>`'s
102
- * `application/x-tex` annotation (or its `alttext`), alone or in KaTeX's
103
- * markup (`.katex`, `.katex-display`) — when the element is an equation of
104
- * the display asked (`display="block"`: a block), else `undefined`.
105
- */
106
- export const parseEquation = (element, display) => {
107
- const own = element.localName === 'math';
108
- const wrapper = element.classList.contains('katex') ||
109
- element.classList.contains('katex-display') ||
110
- element.hasAttribute('data-edytor-equation');
111
- if (!own && !wrapper)
112
- return undefined;
113
- const math = own ? element : element.querySelector('math');
114
- if (!math)
115
- return undefined;
116
- const block = math.getAttribute('display') === 'block' || element.classList.contains('katex-display');
117
- if (block !== display)
118
- return undefined;
119
- const tex = math.querySelector(`annotation[encoding="${TEX}"]`)?.textContent ??
120
- math.getAttribute('alttext');
121
- return tex?.trim() ? { expression: tex.trim() } : undefined;
122
- };
123
93
  /**
124
94
  * One view's equation editor (Notion's): a click on an equation of an
125
95
  * editable view (Enter on a selected one, or a new empty one) opens its
@@ -161,12 +131,12 @@ export class EquationEditor {
161
131
  return this.edytor.readonly;
162
132
  }
163
133
  /**
164
- * Where the panel stands: under the edited equation (a block one's
165
- * middle, an inline one's start), layer-relative.
166
- * @internal
134
+ * Where the panel stands: the edited equation (`ChromeAnchor`; Notion
135
+ * centers it under a block equation, `display`, and starts it under an
136
+ * inline one).
167
137
  */
168
- get place() {
169
- return this.target && this.panel.place(6, this.display);
138
+ get anchor() {
139
+ return this.panel.anchor;
170
140
  }
171
141
  /** A block equation is edited (a field of several lines). */
172
142
  get display() {
@@ -216,7 +186,6 @@ export class EquationEditor {
216
186
  if (!target)
217
187
  return;
218
188
  this.target = null;
219
- this.panel.box = null;
220
189
  this.edytor.overlay.invalidate();
221
190
  const block = this.#block(target);
222
191
  if (!block)
@@ -248,16 +217,10 @@ export class EquationEditor {
248
217
  * them. Focus leaving the panel for somewhere else closes it.
249
218
  */
250
219
  field = (node) => untrack(() => {
251
- const keydown = (event) => {
252
- if (event.isComposing)
253
- return;
254
- const enter = event.key === 'Enter' && !(event.shiftKey && this.display);
255
- if (!enter && event.key !== 'Escape')
256
- return;
257
- event.preventDefault();
258
- event.stopPropagation();
259
- this.close();
260
- };
220
+ const keydown = keysOf({
221
+ Enter: (event) => !(event.shiftKey && this.display) && this.close(),
222
+ Escape: () => this.close()
223
+ }, true);
261
224
  const element = node;
262
225
  const off = listen(element, 'keydown', keydown);
263
226
  const off2 = listen(element, 'focusout', this.panel.blurred);
@@ -0,0 +1,17 @@
1
+ /** The data of a block equation and of an inline one: its TeX source. */
2
+ export type EquationData = {
3
+ expression?: string;
4
+ };
5
+ /** The MathML annotation that carries an equation's TeX source. */
6
+ export declare const TEX = "application/x-tex";
7
+ /** An equation's TeX source: its `expression`, a string. */
8
+ export declare const expressionOf: (data: unknown) => string;
9
+ /** MathML whose `application/x-tex` annotation is `tex`, the source as its text, in KaTeX's `katex` class. */
10
+ export declare const sourceMathml: (tex: string, display: boolean) => string;
11
+ /**
12
+ * HTML import: the TeX source an equation's element carries — a `<math>`'s
13
+ * `application/x-tex` annotation (or its `alttext`), alone or in KaTeX's
14
+ * markup (`.katex`, `.katex-display`) — when the element is an equation of
15
+ * the display asked (`display="block"`: a block), else `undefined`.
16
+ */
17
+ export declare const parseEquation: (element: HTMLElement, display: boolean) => EquationData | undefined;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * An equation's pure forms: its data, its TeX source, the MathML that
3
+ * carries the source (`sourceMathml`, the clipboard's form before KaTeX
4
+ * loads and the export's on a server) and its HTML import. No Svelte.
5
+ */
6
+ import { escapeHtml } from '../../clipboard/serializeClipboardFragment.js';
7
+ /** The MathML annotation that carries an equation's TeX source. */
8
+ export const TEX = 'application/x-tex';
9
+ /** An equation's TeX source: its `expression`, a string. */
10
+ export const expressionOf = (data) => {
11
+ const value = data?.expression;
12
+ return typeof value === 'string' ? value : '';
13
+ };
14
+ /** MathML whose `application/x-tex` annotation is `tex`, the source as its text, in KaTeX's `katex` class. */
15
+ export const sourceMathml = (tex, display) => {
16
+ const source = escapeHtml(tex);
17
+ return (`<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"${display ? ' display="block"' : ''}>` +
18
+ `<semantics><mrow><mtext>${source}</mtext></mrow><annotation encoding="${TEX}">${source}</annotation></semantics></math></span>`);
19
+ };
20
+ /**
21
+ * HTML import: the TeX source an equation's element carries — a `<math>`'s
22
+ * `application/x-tex` annotation (or its `alttext`), alone or in KaTeX's
23
+ * markup (`.katex`, `.katex-display`) — when the element is an equation of
24
+ * the display asked (`display="block"`: a block), else `undefined`.
25
+ */
26
+ export const parseEquation = (element, display) => {
27
+ const own = element.localName === 'math';
28
+ const wrapper = element.classList.contains('katex') ||
29
+ element.classList.contains('katex-display') ||
30
+ element.hasAttribute('data-edytor-equation');
31
+ if (!own && !wrapper)
32
+ return undefined;
33
+ const math = own ? element : element.querySelector('math');
34
+ if (!math)
35
+ return undefined;
36
+ const block = math.getAttribute('display') === 'block' || element.classList.contains('katex-display');
37
+ if (block !== display)
38
+ return undefined;
39
+ const tex = math.querySelector(`annotation[encoding="${TEX}"]`)?.textContent ??
40
+ math.getAttribute('alttext');
41
+ return tex?.trim() ? { expression: tex.trim() } : undefined;
42
+ };
@@ -1,28 +1,43 @@
1
1
  /**
2
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 core's `onDrop.ts` asks the view's uploads, `ViewUploads.drop`, and passes it to the occurrence; the view's uploads place one
5
- * block per file there, one command). The placement is the block drag's
6
- * own resolution (`DropResolver`) for a drag that moves no block, whose
7
- * places are the ones the document takes new blocks of the files' kinds
8
- * at (`canPlaceNew`). Shown only in an editable view whose uploads take
9
- * one of the files (`viewUploads`); with or without the block handles.
10
- * Plugin-owned: the view's `FileUploads` (the first media plugin listed with
11
- * an `upload`) installs it, so a view without one ships none of it.
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.
12
24
  */
13
25
  import type { Edytor } from '../edytor.svelte.js';
14
26
  import type { DropAt } from '../session/attempt.js';
15
- import type { DropPlacement } from '../dnd/geometry.js';
16
- /**
17
- * A `dragover` of files from outside: where they land, shown (Notion's
18
- * indicator), `null` where none (a read-only view, no uploads taking them,
19
- * no place the document takes them at).
20
- */
21
- export declare const fileDragOver: (edytor: Edytor, event: DragEvent) => DropPlacement | null;
22
- /**
23
- * A drop of files from outside: the placement shown (re-resolved at the
24
- * drop point), in block terms, and nothing shows any more.
25
- */
26
- export declare const fileDropAt: (edytor: Edytor, event: DragEvent) => DropAt | null;
27
- /** The files' drag left the view (or ended): the indicator goes. */
28
- export declare const fileDragEnd: (edytor: Edytor) => void | undefined;
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
+ }
@@ -1,49 +1,78 @@
1
1
  import { canPlaceNew } from '../session/moves.js';
2
- import { viewUploads } from '../session/uploads.js';
2
+ import { autoScrollFor } from '../dnd/autoScroll.js';
3
3
  import { DropIndicator } from '../dnd/dropIndicator.js';
4
+ import { containsFiles, dropTargetForExternal, externalScrollers, getFiles } from '../dnd/external.js';
4
5
  import { DropResolver, isDropTarget } from '../dnd/placement.js';
5
- /** The kinds the files of a drag would be, by their types (a `dragover` sees no file, only its type). */
6
- const kindsOf = (edytor, dataTransfer) => {
7
- const uploads = viewUploads(edytor);
8
- if (!uploads || !dataTransfer)
9
- return [];
10
- const kinds = new Set();
11
- for (const item of Array.from(dataTransfer.items ?? []))
12
- if (item.kind === 'file') {
13
- const file = item.getAsFile() ?? new File([], 'file', { type: item.type });
14
- const kind = uploads.kindOf(file);
15
- if (kind)
16
- kinds.add(kind);
17
- }
18
- // An engine that lists no item for a file (WebKit before the drop): its files themselves.
19
- if (!kinds.size)
20
- for (const file of Array.from(dataTransfer.files ?? [])) {
21
- const kind = uploads.kindOf(file);
22
- if (kind)
23
- kinds.add(kind);
24
- }
25
- return [...kinds];
26
- };
27
- class FileDrag {
6
+ import { isKindControl } from '../events/nativeInteractiveControl.js';
7
+ export class FileDrag {
28
8
  edytor;
29
- indicator;
30
- resolver;
9
+ uploads;
10
+ #indicator;
11
+ #resolver;
12
+ #scrolling = null;
13
+ #dropped = null;
31
14
  #begun = false;
32
- constructor(edytor) {
15
+ #kinds = [];
16
+ constructor(edytor, uploads) {
33
17
  this.edytor = edytor;
34
- this.indicator = new DropIndicator(edytor, {
18
+ this.uploads = uploads;
19
+ this.#indicator = new DropIndicator(edytor, {
35
20
  group: () => [],
36
- besideOf: (block) => this.resolver.besideOf(block),
37
- layoutOf: (outer) => this.resolver.layoutOf(outer)
21
+ besideOf: (block) => this.#resolver.besideOf(block),
22
+ layoutOf: (outer) => this.#resolver.layoutOf(outer)
38
23
  });
39
- this.resolver = new DropResolver(edytor, {
24
+ this.#resolver = new DropResolver(edytor, {
40
25
  targetOf: (node) => {
41
26
  const block = edytor.idToBlock.get(node.dataset.edytorId ?? '');
42
27
  return block?.node === node && isDropTarget(edytor, block) ? block : undefined;
43
28
  },
44
29
  handle: () => null,
45
- indicator: this.indicator
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
+ }
46
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];
47
76
  }
48
77
  /** The drag of files of `kinds`: no block moves; a place takes them all. */
49
78
  #drag(kinds) {
@@ -54,63 +83,40 @@ class FileDrag {
54
83
  }
55
84
  /**
56
85
  * The placement at the pointer, shown (`null`: none, and nothing shows):
57
- * over a drop target, its row's; elsewhere in the host, its margins'.
86
+ * over a drop target, its row's; elsewhere in the host, its margins'; on
87
+ * a kind's own control, none.
58
88
  */
59
- over(kinds, input) {
60
- const document = this.edytor.node?.ownerDocument;
89
+ #over(kinds, input) {
90
+ const host = this.edytor.node;
91
+ const document = host?.ownerDocument;
61
92
  if (!document)
62
93
  return null;
63
94
  if (!this.#begun) {
64
95
  this.#begun = true;
65
- this.indicator.begin(document);
96
+ this.#indicator.begin(document);
66
97
  }
98
+ const at = document.elementsFromPoint?.(input.clientX, input.clientY)[0] ?? null;
99
+ const control = at !== null && isKindControl(host, at);
67
100
  const drag = this.#drag(kinds);
68
- const node = this.resolver.hitAt(input);
69
- const placement = node
70
- ? this.resolver.over(drag, node, input)
71
- : this.resolver.margin(drag, input);
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);
72
107
  if (!placement) {
73
- this.indicator.clear();
108
+ this.#indicator.clear();
74
109
  return null;
75
110
  }
76
111
  // A margin's placement shows as the block drag's page target does (its reorder holds).
77
- this.indicator.show(node ?? document.body, placement);
112
+ this.#indicator.show(node ?? document.body, placement);
78
113
  return placement;
79
114
  }
80
- /** The drag left the view, or dropped: nothing shows. */
81
- end() {
115
+ /** The drag left the view, or dropped: nothing shows, nothing scrolls. */
116
+ #end() {
117
+ this.#scrolling?.();
118
+ this.#scrolling = null;
82
119
  this.#begun = false;
83
- this.indicator.end();
120
+ this.#indicator.end();
84
121
  }
85
122
  }
86
- const drags = new WeakMap();
87
- const dragOf = (edytor) => {
88
- let drag = drags.get(edytor);
89
- if (!drag)
90
- drags.set(edytor, (drag = new FileDrag(edytor)));
91
- return drag;
92
- };
93
- /**
94
- * A `dragover` of files from outside: where they land, shown (Notion's
95
- * indicator), `null` where none (a read-only view, no uploads taking them,
96
- * no place the document takes them at).
97
- */
98
- export const fileDragOver = (edytor, event) => {
99
- const kinds = edytor.readonly ? [] : kindsOf(edytor, event.dataTransfer);
100
- if (!kinds.length) {
101
- drags.get(edytor)?.end();
102
- return null;
103
- }
104
- return dragOf(edytor).over(kinds, event);
105
- };
106
- /**
107
- * A drop of files from outside: the placement shown (re-resolved at the
108
- * drop point), in block terms, and nothing shows any more.
109
- */
110
- export const fileDropAt = (edytor, event) => {
111
- const placement = fileDragOver(edytor, event);
112
- fileDragEnd(edytor);
113
- return placement && { block: placement.target.id, position: placement.position };
114
- };
115
- /** The files' drag left the view (or ended): the indicator goes. */
116
- export const fileDragEnd = (edytor) => drags.get(edytor)?.end();
@@ -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