@thomasfosterau/effect-richtext 0.0.0 → 0.1.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 (176) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +117 -1
  3. package/dist/_virtual/_rolldown/runtime.js +12 -0
  4. package/dist/commands/commands.d.ts +323 -0
  5. package/dist/commands/commands.js +722 -0
  6. package/dist/commands/internal.js +43 -0
  7. package/dist/commands/keymaps.d.ts +46 -0
  8. package/dist/commands/keymaps.js +64 -0
  9. package/dist/commands.d.ts +3 -0
  10. package/dist/commands.js +3 -0
  11. package/dist/compat/commands.d.ts +250 -0
  12. package/dist/compat/commands.js +275 -0
  13. package/dist/compat/internal/core.d.ts +20 -0
  14. package/dist/compat/internal/core.js +148 -0
  15. package/dist/compat/internal/props.js +82 -0
  16. package/dist/compat/keymap.d.ts +21 -0
  17. package/dist/compat/keymap.js +38 -0
  18. package/dist/compat/model.d.ts +893 -0
  19. package/dist/compat/model.js +1212 -0
  20. package/dist/compat/orderedmap.d.ts +74 -0
  21. package/dist/compat/orderedmap.js +124 -0
  22. package/dist/compat/schema-basic.d.ts +38 -0
  23. package/dist/compat/schema-basic.js +238 -0
  24. package/dist/compat/schema-list.d.ts +81 -0
  25. package/dist/compat/schema-list.js +156 -0
  26. package/dist/compat/state.d.ts +415 -0
  27. package/dist/compat/state.js +689 -0
  28. package/dist/compat/test-builder.d.ts +98 -0
  29. package/dist/compat/test-builder.js +197 -0
  30. package/dist/compat/transform.d.ts +458 -0
  31. package/dist/compat/transform.js +803 -0
  32. package/dist/compat/view.d.ts +411 -0
  33. package/dist/compat/view.js +409 -0
  34. package/dist/dropcursor.d.ts +46 -0
  35. package/dist/dropcursor.js +176 -0
  36. package/dist/extensions/defaults.d.ts +716 -0
  37. package/dist/extensions/defaults.js +209 -0
  38. package/dist/extensions/editor.d.ts +246 -0
  39. package/dist/extensions/editor.js +271 -0
  40. package/dist/extensions/extension.d.ts +375 -0
  41. package/dist/extensions/extension.js +233 -0
  42. package/dist/extensions.d.ts +4 -0
  43. package/dist/extensions.js +4 -0
  44. package/dist/gapcursor.d.ts +66 -0
  45. package/dist/gapcursor.js +273 -0
  46. package/dist/history.d.ts +178 -0
  47. package/dist/history.js +412 -0
  48. package/dist/html.d.ts +158 -0
  49. package/dist/html.js +164 -0
  50. package/dist/inputrules.d.ts +271 -0
  51. package/dist/inputrules.js +321 -0
  52. package/dist/internal/attempt.js +15 -0
  53. package/dist/internal/json.js +29 -0
  54. package/dist/keymap/keymap.d.ts +130 -0
  55. package/dist/keymap/keymap.js +159 -0
  56. package/dist/keymap.d.ts +2 -0
  57. package/dist/keymap.js +1 -0
  58. package/dist/markdown/errors.d.ts +23 -0
  59. package/dist/markdown/errors.js +26 -0
  60. package/dist/markdown/from_markdown.d.ts +185 -0
  61. package/dist/markdown/from_markdown.js +266 -0
  62. package/dist/markdown/schema.d.ts +522 -0
  63. package/dist/markdown/schema.js +292 -0
  64. package/dist/markdown/to_markdown.d.ts +251 -0
  65. package/dist/markdown/to_markdown.js +413 -0
  66. package/dist/markdown.d.ts +5 -0
  67. package/dist/markdown.js +5 -0
  68. package/dist/model/attrsCodec.js +48 -0
  69. package/dist/model/comparedeep.js +17 -0
  70. package/dist/model/content.d.ts +232 -0
  71. package/dist/model/content.js +630 -0
  72. package/dist/model/diff.js +66 -0
  73. package/dist/model/dom.d.ts +3 -0
  74. package/dist/model/errors.d.ts +90 -0
  75. package/dist/model/errors.js +76 -0
  76. package/dist/model/fragment.d.ts +131 -0
  77. package/dist/model/fragment.js +262 -0
  78. package/dist/model/from_dom.d.ts +299 -0
  79. package/dist/model/from_dom.js +618 -0
  80. package/dist/model/json.d.ts +37 -0
  81. package/dist/model/json.js +126 -0
  82. package/dist/model/mark.d.ts +73 -0
  83. package/dist/model/mark.js +121 -0
  84. package/dist/model/node.d.ts +267 -0
  85. package/dist/model/node.js +434 -0
  86. package/dist/model/replace.d.ts +78 -0
  87. package/dist/model/replace.js +207 -0
  88. package/dist/model/resolvedpos.d.ts +196 -0
  89. package/dist/model/resolvedpos.js +305 -0
  90. package/dist/model/schema.d.ts +740 -0
  91. package/dist/model/schema.js +451 -0
  92. package/dist/model/to_dom.d.ts +169 -0
  93. package/dist/model/to_dom.js +238 -0
  94. package/dist/model.d.ts +12 -0
  95. package/dist/model.js +12 -0
  96. package/dist/placeholder.d.ts +53 -0
  97. package/dist/placeholder.js +79 -0
  98. package/dist/reactive/observer.d.ts +54 -0
  99. package/dist/reactive/observer.js +61 -0
  100. package/dist/reactive/selectors.d.ts +22 -0
  101. package/dist/reactive/selectors.js +61 -0
  102. package/dist/reactive/stateref.d.ts +72 -0
  103. package/dist/reactive/stateref.js +71 -0
  104. package/dist/reactive/streams.d.ts +83 -0
  105. package/dist/reactive/streams.js +85 -0
  106. package/dist/reactive.d.ts +16 -0
  107. package/dist/reactive.js +26 -0
  108. package/dist/schema-basic.d.ts +448 -0
  109. package/dist/schema-basic.js +269 -0
  110. package/dist/schema-list/commands.d.ts +66 -0
  111. package/dist/schema-list/commands.js +203 -0
  112. package/dist/schema-list.d.ts +92 -0
  113. package/dist/schema-list.js +100 -0
  114. package/dist/state/errors.d.ts +39 -0
  115. package/dist/state/errors.js +32 -0
  116. package/dist/state/plugin.d.ts +232 -0
  117. package/dist/state/plugin.js +82 -0
  118. package/dist/state/selection.d.ts +297 -0
  119. package/dist/state/selection.js +486 -0
  120. package/dist/state/state.d.ts +212 -0
  121. package/dist/state/state.js +489 -0
  122. package/dist/state/transaction.d.ts +145 -0
  123. package/dist/state/transaction.js +195 -0
  124. package/dist/state.d.ts +6 -0
  125. package/dist/state.js +6 -0
  126. package/dist/test-builder.d.ts +3582 -0
  127. package/dist/test-builder.js +239 -0
  128. package/dist/transform/attr_step.d.ts +60 -0
  129. package/dist/transform/attr_step.js +105 -0
  130. package/dist/transform/errors.d.ts +50 -0
  131. package/dist/transform/errors.js +35 -0
  132. package/dist/transform/map.d.ts +182 -0
  133. package/dist/transform/map.js +280 -0
  134. package/dist/transform/mark.js +98 -0
  135. package/dist/transform/mark_set_step.js +69 -0
  136. package/dist/transform/mark_step.d.ts +113 -0
  137. package/dist/transform/mark_step.js +225 -0
  138. package/dist/transform/replace.d.ts +18 -0
  139. package/dist/transform/replace.js +381 -0
  140. package/dist/transform/replace_step.d.ts +109 -0
  141. package/dist/transform/replace_step.js +175 -0
  142. package/dist/transform/step.d.ts +157 -0
  143. package/dist/transform/step.js +168 -0
  144. package/dist/transform/structure.d.ts +89 -0
  145. package/dist/transform/structure.js +350 -0
  146. package/dist/transform/transform.d.ts +248 -0
  147. package/dist/transform/transform.js +373 -0
  148. package/dist/transform.d.ts +10 -0
  149. package/dist/transform.js +10 -0
  150. package/dist/view/browser.js +39 -0
  151. package/dist/view/capturekeys.js +282 -0
  152. package/dist/view/clipboard.d.ts +8 -0
  153. package/dist/view/clipboard.js +245 -0
  154. package/dist/view/decoration.d.ts +339 -0
  155. package/dist/view/decoration.js +566 -0
  156. package/dist/view/dom.d.ts +18 -0
  157. package/dist/view/dom.js +140 -0
  158. package/dist/view/domchange.js +295 -0
  159. package/dist/view/domcoords.d.ts +22 -0
  160. package/dist/view/domcoords.js +448 -0
  161. package/dist/view/domobserver.d.ts +47 -0
  162. package/dist/view/domobserver.js +328 -0
  163. package/dist/view/errors.d.ts +18 -0
  164. package/dist/view/errors.js +21 -0
  165. package/dist/view/input.d.ts +58 -0
  166. package/dist/view/input.js +704 -0
  167. package/dist/view/selection.js +198 -0
  168. package/dist/view/view.d.ts +699 -0
  169. package/dist/view/view.js +623 -0
  170. package/dist/view/viewdesc.d.ts +236 -0
  171. package/dist/view/viewdesc.js +1205 -0
  172. package/dist/view-css.d.ts +43 -0
  173. package/dist/view-css.js +124 -0
  174. package/dist/view.d.ts +6 -0
  175. package/dist/view.js +4 -0
  176. package/package.json +200 -4
@@ -0,0 +1,623 @@
1
+ import { NodeSelection } from "../state/selection.js";
2
+ import { chrome, ie, safari } from "./browser.js";
3
+ import { parseFromClipboard, serializeForClipboard } from "./clipboard.js";
4
+ import { Decoration, viewDecorations } from "./decoration.js";
5
+ import { clearReusedRange, deepActiveElement } from "./dom.js";
6
+ import { readDOMChange } from "./domchange.js";
7
+ import { coordsAtPos, endOfTextblock, focusPreventScroll, posAtCoords, resetScrollPos, scrollRectIntoView, storeScrollPos } from "./domcoords.js";
8
+ import { DOMObserver, safariShadowSelectionRange } from "./domobserver.js";
9
+ import { ViewError } from "./errors.js";
10
+ import { Dragging, InputState, clearComposition, destroyInput, dispatchEvent, doPaste, endComposition, ensureListeners, findCompositionNode, initInput } from "./input.js";
11
+ import { anchorInRightPlace, selectionToDOM, syncNodeSelection } from "./selection.js";
12
+ import { docViewDesc } from "./viewdesc.js";
13
+ import * as Effect from "effect/Effect";
14
+ import * as Option from "effect/Option";
15
+ import * as Cause from "effect/Cause";
16
+ import * as Exit from "effect/Exit";
17
+ import * as Context from "effect/Context";
18
+ import { PreventSchedulerYield } from "effect/Scheduler";
19
+ // src/view/view.ts
20
+ /**
21
+ * The editor view.
22
+ *
23
+ * @since 0.1.0
24
+ */
25
+ /** @internal */
26
+ const __parseFromClipboard = parseFromClipboard;
27
+ /** @internal */
28
+ const __endComposition = endComposition;
29
+ /**
30
+ * @since 0.1.0
31
+ * @category symbols
32
+ */
33
+ const EditorViewTypeId = Symbol.for("effect-richtext/view/EditorView");
34
+ /**
35
+ * An editor view manages the DOM structure that represents an editable
36
+ * document. Its state and behavior are determined by its props.
37
+ *
38
+ * `R` and `E` are the requirements and errors of the effects the view
39
+ * runs: those of its state's plugins and of its effectful props. Create
40
+ * a view with `EditorView.make`, inside an Effect that provides `R` and a
41
+ * `Scope`: the view captures the current context to run handler effects
42
+ * later, and is destroyed when the scope closes.
43
+ *
44
+ * @since 0.1.0
45
+ * @category models
46
+ */
47
+ var EditorView = class EditorView {
48
+ /** @internal */
49
+ constructor(place, props, context) {
50
+ this._root = null;
51
+ this.focused = false;
52
+ this.trackWrites = null;
53
+ this.mounted = false;
54
+ this.markCursor = null;
55
+ this.cursorWrapper = null;
56
+ this.lastSelectedViewDesc = void 0;
57
+ this.input = new InputState();
58
+ this.prevDirectPlugins = [];
59
+ this.pluginViews = [];
60
+ this.listeners = [];
61
+ this.dispatching = null;
62
+ this.requiresGeckoHackNode = false;
63
+ this.dragging = null;
64
+ this.dispatch = (tr) => Effect.catchCause(this.tryDispatch(tr), (cause) => Effect.sync(() => this.reportError(cause)));
65
+ this.dispatchUnsafe = (tr) => {
66
+ const fiber = Effect.runForkWith(this.runContext)(this.tryDispatch(tr));
67
+ const exit = fiber.pollUnsafe();
68
+ if (exit) {
69
+ if (Exit.isFailure(exit)) throw Cause.squash(exit.cause);
70
+ return;
71
+ }
72
+ fiber.addObserver((exit) => {
73
+ if (Exit.isFailure(exit)) this.reportError(exit.cause);
74
+ });
75
+ };
76
+ this.context = context;
77
+ this.runContext = Context.add(context, PreventSchedulerYield, true);
78
+ this._props = props;
79
+ this.state = props.state;
80
+ this.directPlugins = props.plugins || [];
81
+ this.directPlugins.forEach(checkStateComponent);
82
+ this.dom = place && place.mount || document.createElement("div");
83
+ if (place) {
84
+ if (place.appendChild) place.appendChild(this.dom);
85
+ else if (typeof place == "function") place(this.dom);
86
+ else if (place.mount) this.mounted = true;
87
+ }
88
+ this.editable = getEditable(this);
89
+ updateCursorWrapper(this);
90
+ this.nodeViews = buildNodeViews(this);
91
+ this.docView = docViewDesc(this.state.doc, computeDocDeco(this), viewDecorations(this), this.dom, this);
92
+ this.domObserver = new DOMObserver(this, (from, to, typeOver, added) => readDOMChange(this, from, to, typeOver, added));
93
+ this.domObserver.start();
94
+ initInput(this);
95
+ this.updatePluginViews();
96
+ }
97
+ /**
98
+ * Create a view inside a scope. `place` may be a DOM node that the
99
+ * editor should be appended to, a function that will place it into the
100
+ * document, or an object whose `mount` property holds the node to use
101
+ * as the document container. If it is `null`, the editor will not be
102
+ * added to the document.
103
+ *
104
+ * The view captures the current context (which must provide `R`) to
105
+ * run the effects of its plugins and props, and is destroyed when the
106
+ * scope closes.
107
+ *
108
+ * When props' handlers require services that the state's plugins
109
+ * don't, pass the type arguments explicitly
110
+ * (`EditorView.make<typeof schema, ServiceA | ServiceB>(...)`).
111
+ */
112
+ static make(place, props) {
113
+ return Effect.acquireRelease(Effect.flatMap(Effect.context(), (context) => Effect.sync(() => new EditorView(place, props, context))), (view) => Effect.sync(() => view.destroy()));
114
+ }
115
+ static makeUnsafe(place, props, context) {
116
+ return new EditorView(place, props, context ?? Context.empty());
117
+ }
118
+ /** Holds `true` when a composition is active. */
119
+ get composing() {
120
+ return this.input.composing;
121
+ }
122
+ /** The view's current props. */
123
+ get props() {
124
+ if (this._props.state != this.state) {
125
+ const prev = this._props;
126
+ this._props = {};
127
+ for (const name in prev) this._props[name] = prev[name];
128
+ this._props.state = this.state;
129
+ }
130
+ return this._props;
131
+ }
132
+ /** Update the view's props. Will immediately cause an update to the DOM. */
133
+ update(props) {
134
+ if (props.handleDOMEvents != this._props.handleDOMEvents) ensureListeners(this);
135
+ const prevProps = this._props;
136
+ this._props = props;
137
+ if (props.plugins) {
138
+ props.plugins.forEach(checkStateComponent);
139
+ this.directPlugins = props.plugins;
140
+ }
141
+ this.updateStateInner(props.state, prevProps);
142
+ }
143
+ /**
144
+ * Update the view by updating existing props object with the object
145
+ * given as argument. Equivalent to
146
+ * `view.update(Object.assign({}, view.props, props))`.
147
+ */
148
+ setProps(props) {
149
+ const updated = {};
150
+ for (const name in this._props) updated[name] = this._props[name];
151
+ updated.state = this.state;
152
+ for (const name in props) updated[name] = props[name];
153
+ this.update(updated);
154
+ }
155
+ /** Update the editor's `state` prop, without touching any of the other props. */
156
+ updateState(state) {
157
+ this.updateStateInner(state, this._props);
158
+ }
159
+ updateStateInner(state, prevProps) {
160
+ const prev = this.state;
161
+ const transactions = this.dispatching ?? [];
162
+ this.dispatching = null;
163
+ let redraw = false, updateSel = false;
164
+ if (state.storedMarksOrNull && this.composing) {
165
+ clearComposition(this);
166
+ updateSel = true;
167
+ }
168
+ this.state = state;
169
+ const pluginsChanged = prev.plugins != state.plugins || this._props.plugins != prevProps.plugins;
170
+ if (pluginsChanged || this._props.plugins != prevProps.plugins || this._props.nodeViews != prevProps.nodeViews) {
171
+ const nodeViews = buildNodeViews(this);
172
+ if (changedNodeViews(nodeViews, this.nodeViews)) {
173
+ this.nodeViews = nodeViews;
174
+ redraw = true;
175
+ }
176
+ }
177
+ if (pluginsChanged || prevProps.handleDOMEvents != this._props.handleDOMEvents) ensureListeners(this);
178
+ this.editable = getEditable(this);
179
+ updateCursorWrapper(this);
180
+ const innerDeco = viewDecorations(this), outerDeco = computeDocDeco(this);
181
+ const scroll = prev.plugins != state.plugins && !prev.doc.eq(state.doc) ? "reset" : state.scrollToSelection > prev.scrollToSelection ? "to selection" : "preserve";
182
+ const updateDoc = redraw || !this.docView.matchesNode(state.doc, outerDeco, innerDeco);
183
+ if (updateDoc || !state.selection.eq(prev.selection)) updateSel = true;
184
+ const oldScrollPos = scroll == "preserve" && updateSel && this.dom.style.overflowAnchor == null && storeScrollPos(this);
185
+ if (updateSel) {
186
+ this.domObserver.stop();
187
+ let forceSelUpdate = updateDoc && (ie || chrome) && !this.composing && !prev.selection.empty && !state.selection.empty && selectionContextChanged(prev.selection, state.selection);
188
+ if (updateDoc) {
189
+ const chromeKludge = chrome ? this.trackWrites = this.domSelectionRange().focusNode : null;
190
+ if (this.composing) this.input.compositionNode = findCompositionNode(this);
191
+ if (redraw || !this.docView.update(state.doc, outerDeco, innerDeco, this)) {
192
+ this.docView.updateOuterDeco(outerDeco);
193
+ this.docView.destroy();
194
+ this.docView = docViewDesc(state.doc, outerDeco, innerDeco, this.dom, this);
195
+ }
196
+ if (chromeKludge && (!this.trackWrites || !this.dom.contains(this.trackWrites))) forceSelUpdate = true;
197
+ }
198
+ if (forceSelUpdate || !(this.input.mouseDown && this.domObserver.currentSelection.eq(this.domSelectionRange()) && anchorInRightPlace(this) && this.input.mouseDown.delaySelUpdate())) selectionToDOM(this, forceSelUpdate);
199
+ else {
200
+ syncNodeSelection(this, state.selection);
201
+ this.domObserver.setCurSelection();
202
+ }
203
+ this.domObserver.start();
204
+ }
205
+ this.updatePluginViews(prev);
206
+ if (this.dragging?.node && !prev.doc.eq(state.doc)) this.updateDraggedNode(this.dragging, prev);
207
+ if (scroll == "reset") this.dom.scrollTop = 0;
208
+ else if (scroll == "to selection") this.scrollToSelection();
209
+ else if (oldScrollPos) resetScrollPos(oldScrollPos);
210
+ if (this.listeners.length) this.notifyListeners({
211
+ state,
212
+ prevState: prev,
213
+ transactions
214
+ });
215
+ }
216
+ notifyListeners(update) {
217
+ const listeners = this.listeners;
218
+ for (let i = 0; i < listeners.length; i++) try {
219
+ listeners[i](update);
220
+ } catch (e) {
221
+ this.reportError(Cause.die(e));
222
+ }
223
+ }
224
+ /**
225
+ * Register a listener called after every state update of the view
226
+ * (once the DOM and plugin views have been updated). Returns a
227
+ * function that removes the listener. Exceptions thrown by listeners
228
+ * are reported to `onError`.
229
+ */
230
+ subscribe(listener) {
231
+ this.listeners = [...this.listeners, listener];
232
+ return () => {
233
+ this.listeners = this.listeners.filter((l) => l !== listener);
234
+ };
235
+ }
236
+ withDispatching(transactions, f) {
237
+ const prev = this.dispatching;
238
+ this.dispatching = transactions;
239
+ try {
240
+ return f();
241
+ } finally {
242
+ this.dispatching = prev;
243
+ }
244
+ }
245
+ /** @internal */
246
+ scrollToSelection() {
247
+ const startDOM = this.domSelectionRange().focusNode;
248
+ if (!startDOM || !this.dom.contains(startDOM.nodeType == 1 ? startDOM : startDOM.parentNode)) {} else if (this.someHandler("handleScrollToSelection", (f) => f(this))) {} else if (this.state.selection instanceof NodeSelection) {
249
+ const target = this.docView.domAfterPos(this.state.selection.from);
250
+ if (target.nodeType == 1) scrollRectIntoView(this, target.getBoundingClientRect(), startDOM);
251
+ } else scrollRectIntoView(this, this.coordsAtPos(this.state.selection.head, 1), startDOM);
252
+ }
253
+ destroyPluginViews() {
254
+ let view;
255
+ while (view = this.pluginViews.pop()) if (view.destroy) view.destroy();
256
+ }
257
+ updatePluginViews(prevState) {
258
+ if (!prevState || prevState.plugins != this.state.plugins || this.directPlugins != this.prevDirectPlugins) {
259
+ this.prevDirectPlugins = this.directPlugins;
260
+ this.destroyPluginViews();
261
+ for (let i = 0; i < this.directPlugins.length; i++) {
262
+ const plugin = this.directPlugins[i];
263
+ if (plugin.spec.view) this.pluginViews.push(plugin.spec.view(this));
264
+ }
265
+ for (let i = 0; i < this.state.plugins.length; i++) {
266
+ const plugin = this.state.plugins[i];
267
+ if (plugin.spec.view) this.pluginViews.push(plugin.spec.view(this));
268
+ }
269
+ } else for (let i = 0; i < this.pluginViews.length; i++) {
270
+ const pluginView = this.pluginViews[i];
271
+ if (pluginView.update) pluginView.update(this, prevState);
272
+ }
273
+ }
274
+ updateDraggedNode(dragging, prev) {
275
+ const sel = dragging.node;
276
+ let found = -1;
277
+ if (sel.from < this.state.doc.content.size && this.state.doc.nodeAtOrNull(sel.from) == sel.node) found = sel.from;
278
+ else {
279
+ const movedPos = sel.from + (this.state.doc.content.size - prev.doc.content.size);
280
+ if ((movedPos > 0 && movedPos < this.state.doc.content.size && this.state.doc.nodeAtOrNull(movedPos)) == sel.node) found = movedPos;
281
+ }
282
+ this.dragging = new Dragging(dragging.slice, dragging.move, found < 0 ? void 0 : NodeSelection.createUnsafe(this.state.doc, found));
283
+ }
284
+ someProp(propName, f) {
285
+ const prop = this._props && this._props[propName];
286
+ let value;
287
+ if (prop != null && (value = f ? f(prop) : prop)) return value;
288
+ for (let i = 0; i < this.directPlugins.length; i++) {
289
+ const prop = this.directPlugins[i].props[propName];
290
+ if (prop != null && (value = f ? f(prop) : prop)) return value;
291
+ }
292
+ const plugins = this.state.plugins;
293
+ if (plugins) for (let i = 0; i < plugins.length; i++) {
294
+ const prop = plugins[i].props[propName];
295
+ if (prop != null && (value = f ? f(prop) : prop)) return value;
296
+ }
297
+ }
298
+ /**
299
+ * Like `someProp`, but for event handler props, whose results may be
300
+ * Effects: those are run (see `HandlerResult`) and their results used.
301
+ *
302
+ * @internal
303
+ */
304
+ someHandler(propName, f) {
305
+ return !!this.someProp(propName, (h) => this.runHandler(f(h)));
306
+ }
307
+ /**
308
+ * Interpret the result of an event handler prop, running it with the
309
+ * view's context when it is an Effect.
310
+ *
311
+ * @internal
312
+ */
313
+ runHandler(result) {
314
+ if (!Effect.isEffect(result)) return !!result;
315
+ if (Exit.isExit(result)) {
316
+ if (Exit.isSuccess(result)) return !!result.value;
317
+ this.reportError(result.cause);
318
+ return false;
319
+ }
320
+ const exit = this.runEffect(result);
321
+ if (exit === void 0) return true;
322
+ return Exit.isSuccess(exit) ? !!exit.value : false;
323
+ }
324
+ /**
325
+ * Run an effect with the view's captured context. Returns its exit
326
+ * when it completed synchronously (reporting failures to `onError`),
327
+ * or `undefined` when it is still running in the background (in which
328
+ * case failures are reported when it completes).
329
+ *
330
+ * @internal
331
+ */
332
+ runEffect(effect) {
333
+ const fiber = Effect.runForkWith(this.runContext)(effect);
334
+ const exit = fiber.pollUnsafe();
335
+ if (exit) {
336
+ if (Exit.isFailure(exit)) this.reportError(exit.cause);
337
+ return exit;
338
+ }
339
+ fiber.addObserver((exit) => {
340
+ if (Exit.isFailure(exit)) this.reportError(exit.cause);
341
+ });
342
+ }
343
+ /** @internal */
344
+ reportError(cause) {
345
+ const onError = this._props.onError;
346
+ if (onError) onError(cause);
347
+ else Effect.runForkWith(this.context)(Effect.logError(cause));
348
+ }
349
+ /** Query whether the view has focus. */
350
+ hasFocus() {
351
+ if (ie) {
352
+ let node = this.root.activeElement;
353
+ if (node == this.dom) return true;
354
+ if (!node || !this.dom.contains(node)) return false;
355
+ while (node && this.dom != node && this.dom.contains(node)) {
356
+ if (node.contentEditable == "false") return false;
357
+ node = node.parentElement;
358
+ }
359
+ return true;
360
+ }
361
+ return this.root.activeElement == this.dom;
362
+ }
363
+ /** Focus the editor. */
364
+ focus() {
365
+ this.domObserver.stop();
366
+ if (this.editable) focusPreventScroll(this.dom);
367
+ selectionToDOM(this);
368
+ this.domObserver.start();
369
+ }
370
+ /**
371
+ * Get the document root in which the editor exists. This will usually
372
+ * be the top-level `document`, but might be a shadow DOM root if the
373
+ * editor is inside one.
374
+ */
375
+ get root() {
376
+ const cached = this._root;
377
+ if (cached == null) {
378
+ for (let search = this.dom.parentNode; search; search = search.parentNode) if (search.nodeType == 9 || search.nodeType == 11 && search.host) {
379
+ if (!search.getSelection) Object.getPrototypeOf(search).getSelection = () => search.ownerDocument.getSelection();
380
+ return this._root = search;
381
+ }
382
+ }
383
+ return cached || document;
384
+ }
385
+ /**
386
+ * When an existing editor view is moved to a new document or shadow
387
+ * tree, call this to make it recompute its root.
388
+ */
389
+ updateRoot() {
390
+ this._root = null;
391
+ }
392
+ /**
393
+ * Given a pair of viewport coordinates, return the document position
394
+ * that corresponds to them. Returns `None` if the given coordinates
395
+ * aren't inside of the editor.
396
+ */
397
+ posAtCoords(coords) {
398
+ return Option.fromNullishOr(posAtCoords(this, coords));
399
+ }
400
+ /**
401
+ * Returns the viewport rectangle at a given document position. `left`
402
+ * and `right` will be the same number, as this returns a flat
403
+ * cursor-ish rectangle. If the position is between two things that
404
+ * aren't directly adjacent, `side` determines which element is used.
405
+ * When < 0, the element before the position is used, otherwise the
406
+ * element after.
407
+ */
408
+ coordsAtPos(pos, side = 1) {
409
+ return coordsAtPos(this, pos, side);
410
+ }
411
+ /**
412
+ * Find the DOM position that corresponds to the given document
413
+ * position. When `side` is negative, find the position as close as
414
+ * possible to the content before the position. When positive, prefer
415
+ * positions close to the content after the position. When zero,
416
+ * prefer as shallow a position as possible.
417
+ *
418
+ * Note that you should **not** mutate the editor's internal DOM, only
419
+ * inspect it (and even that is usually not necessary).
420
+ */
421
+ domAtPos(pos, side = 0) {
422
+ return this.docView.domFromPos(pos, side);
423
+ }
424
+ /**
425
+ * Find the DOM node that represents the document node after the given
426
+ * position. Returns `None` when the position doesn't point in front of
427
+ * a node or if the node is inside an opaque node view.
428
+ *
429
+ * This is intended to be able to call things like
430
+ * `getBoundingClientRect` on that DOM node. Do **not** mutate the
431
+ * editor DOM directly, or add styling this way, since that will be
432
+ * immediately overriden by the editor as it redraws the node.
433
+ */
434
+ nodeDOM(pos) {
435
+ const desc = this.docView.descAt(pos);
436
+ return Option.fromNullishOr(desc ? desc.nodeDOM : null);
437
+ }
438
+ /**
439
+ * Find the document position that corresponds to a given DOM position.
440
+ * (Whenever possible, it is preferable to inspect the document
441
+ * structure directly, rather than poking around in the DOM, but
442
+ * sometimes—for example when interpreting an event target—you don't
443
+ * have a choice.)
444
+ *
445
+ * The `bias` parameter can be used to influence which side of a DOM
446
+ * node to use when the position is inside a leaf node. Fails with a
447
+ * `ViewError` when the DOM position is not inside the editor.
448
+ */
449
+ posAtDOM(node, offset, bias = -1) {
450
+ return Effect.suspend(() => {
451
+ const pos = this.docView.posFromDOM(node, offset, bias);
452
+ return pos == null || pos < 0 ? Effect.fail(new ViewError({ message: "DOM position not inside the editor" })) : Effect.succeed(pos);
453
+ });
454
+ }
455
+ /** Like `posAtDOM`, but throws a `ViewError` when the position is not inside the editor. */
456
+ posAtDOMUnsafe(node, offset, bias = -1) {
457
+ const pos = this.docView.posFromDOM(node, offset, bias);
458
+ if (pos == null || pos < 0) throw new ViewError({ message: "DOM position not inside the editor" });
459
+ return pos;
460
+ }
461
+ /**
462
+ * Find out whether the selection is at the end of a textblock when
463
+ * moving in a given direction. When, for example, given `"left"`, it
464
+ * will return true if moving left from the current cursor position
465
+ * would leave that position's parent textblock. Will apply to the
466
+ * view's current state by default, but it is possible to pass a
467
+ * different state.
468
+ */
469
+ endOfTextblock(dir, state) {
470
+ return endOfTextblock(this, state || this.state, dir);
471
+ }
472
+ /**
473
+ * Run the editor's paste logic with the given HTML string. The
474
+ * `event`, if given, will be passed to the `handlePaste` hook.
475
+ */
476
+ pasteHTML(html, event) {
477
+ return doPaste(this, "", html, false, event || new ClipboardEvent("paste"));
478
+ }
479
+ /** Run the editor's paste logic with the given plain-text input. */
480
+ pasteText(text, event) {
481
+ return doPaste(this, text, null, true, event || new ClipboardEvent("paste"));
482
+ }
483
+ /**
484
+ * Serialize the given slice as it would be if it was copied from this
485
+ * editor. Returns a DOM element that contains a representation of the
486
+ * slice as its children, a textual representation, and the
487
+ * transformed slice (which can be different from the given input due
488
+ * to hooks like `transformCopied`).
489
+ */
490
+ serializeForClipboard(slice) {
491
+ return serializeForClipboard(this, slice);
492
+ }
493
+ /** Removes the editor from the DOM and destroys all node views. */
494
+ destroy() {
495
+ if (!this.docView) return;
496
+ destroyInput(this);
497
+ this.destroyPluginViews();
498
+ if (this.mounted) {
499
+ this.docView.update(this.state.doc, [], viewDecorations(this), this);
500
+ this.dom.textContent = "";
501
+ } else if (this.dom.parentNode) this.dom.parentNode.removeChild(this.dom);
502
+ this.docView.destroy();
503
+ this.docView = null;
504
+ clearReusedRange();
505
+ }
506
+ /**
507
+ * This is true when the view has been destroyed (and thus should not
508
+ * be used anymore).
509
+ */
510
+ get isDestroyed() {
511
+ return this.docView == null;
512
+ }
513
+ /** Used for testing. */
514
+ dispatchEvent(event) {
515
+ return dispatchEvent(this, event);
516
+ }
517
+ /**
518
+ * Like `dispatch`, but fails with the errors of the plugin hooks and
519
+ * of `dispatchTransaction` instead of reporting them to `onError`.
520
+ * Runs with the view's captured context.
521
+ */
522
+ tryDispatch(tr) {
523
+ return Effect.provideContext(Effect.suspend(() => {
524
+ const dispatchTransaction = this._props.dispatchTransaction;
525
+ if (dispatchTransaction) {
526
+ const result = this.withDispatching([tr], () => dispatchTransaction.call(this, tr));
527
+ if (!Effect.isEffect(result)) return Effect.void;
528
+ return Effect.suspend(() => {
529
+ const transactions = [tr];
530
+ this.dispatching = transactions;
531
+ return Effect.ensuring(result, Effect.sync(() => {
532
+ if (this.dispatching === transactions) this.dispatching = null;
533
+ }));
534
+ });
535
+ }
536
+ return Effect.map(this.state.applyTransaction(tr), (result) => this.withDispatching(result.transactions, () => this.updateState(result.state)));
537
+ }), this.context);
538
+ }
539
+ /**
540
+ * Dispatch a transaction on behalf of a DOM event: like
541
+ * `dispatchUnsafe`, but failures are reported to `onError` instead of
542
+ * thrown.
543
+ *
544
+ * @internal
545
+ */
546
+ dispatchInternal(tr) {
547
+ this.runEffect(this.tryDispatch(tr));
548
+ }
549
+ /** @internal */
550
+ domSelectionRange() {
551
+ const sel = this.dom.isConnected && this.domSelection();
552
+ if (!sel) return {
553
+ focusNode: null,
554
+ focusOffset: 0,
555
+ anchorNode: null,
556
+ anchorOffset: 0
557
+ };
558
+ return safari && this.root.nodeType === 11 && deepActiveElement(this.dom.ownerDocument) == this.dom && safariShadowSelectionRange(this, sel) || sel;
559
+ }
560
+ /** @internal */
561
+ domSelection() {
562
+ return this.root.getSelection();
563
+ }
564
+ };
565
+ function computeDocDeco(view) {
566
+ const attrs = Object.create(null);
567
+ attrs.class = "ProseMirror";
568
+ attrs.contenteditable = String(view.editable);
569
+ view.someProp("attributes", (prop) => {
570
+ const value = typeof prop == "function" ? prop(view.state) : prop;
571
+ if (value) {
572
+ for (const attr in value) if (attr == "class") attrs.class += " " + value[attr];
573
+ else if (attr == "style") attrs.style = (attrs.style ? attrs.style + ";" : "") + value[attr];
574
+ else if (!attrs[attr] && attr != "contenteditable" && attr != "nodeName") attrs[attr] = String(value[attr]);
575
+ }
576
+ });
577
+ if (!attrs.translate) attrs.translate = "no";
578
+ return [Decoration.node(0, view.state.doc.content.size, attrs)];
579
+ }
580
+ function updateCursorWrapper(view) {
581
+ if (view.markCursor) {
582
+ const dom = document.createElement("img");
583
+ dom.className = "ProseMirror-separator";
584
+ dom.setAttribute("mark-placeholder", "true");
585
+ dom.setAttribute("alt", "");
586
+ view.cursorWrapper = {
587
+ dom,
588
+ deco: Decoration.widget(view.state.selection.from, dom, {
589
+ raw: true,
590
+ marks: view.markCursor
591
+ })
592
+ };
593
+ } else view.cursorWrapper = null;
594
+ }
595
+ function getEditable(view) {
596
+ return !view.someProp("editable", (value) => value(view.state) === false);
597
+ }
598
+ function selectionContextChanged(sel1, sel2) {
599
+ const depth = Math.min(sel1.$anchor.sharedDepth(sel1.head), sel2.$anchor.sharedDepth(sel2.head));
600
+ return sel1.$anchor.start(depth) != sel2.$anchor.start(depth);
601
+ }
602
+ function buildNodeViews(view) {
603
+ const result = Object.create(null);
604
+ function add(obj) {
605
+ for (const prop in obj) if (!Object.prototype.hasOwnProperty.call(result, prop)) result[prop] = obj[prop];
606
+ }
607
+ view.someProp("nodeViews", add);
608
+ view.someProp("markViews", add);
609
+ return result;
610
+ }
611
+ function changedNodeViews(a, b) {
612
+ let nA = 0, nB = 0;
613
+ for (const prop in a) {
614
+ if (a[prop] != b[prop]) return true;
615
+ nA++;
616
+ }
617
+ for (const _ in b) nB++;
618
+ return nA != nB;
619
+ }
620
+ function checkStateComponent(plugin) {
621
+ if (plugin.spec.state || plugin.spec.filterTransaction || plugin.spec.appendTransaction) throw new ViewError({ message: "Plugins passed directly to the view must not have a state component" });
622
+ }
623
+ export { EditorView, EditorViewTypeId, __endComposition, __parseFromClipboard };