@handlewithcare/react-prosemirror 3.1.0-tiptap.50 → 3.1.0-tiptap.51

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.
@@ -404,14 +404,14 @@ const ChildNodeViews = /*#__PURE__*/ (0, _react.memo)(function ChildNodeViews(pa
404
404
  component: _SeparatorHackView.SeparatorHackView,
405
405
  marks: [],
406
406
  offset: lastChild?.offset ?? 0,
407
- index: (lastChild?.index ?? 0) + 2,
407
+ index: (lastChild?.index ?? 0) + 1,
408
408
  key: "trailing-hack-img"
409
409
  }, {
410
410
  type: "hack",
411
411
  component: _TrailingHackView.TrailingHackView,
412
412
  marks: [],
413
413
  offset: lastChild?.offset ?? 0,
414
- index: (lastChild?.index ?? 0) + 1,
414
+ index: (lastChild?.index ?? 0) + 2,
415
415
  key: "trailing-hack-br"
416
416
  });
417
417
  }
@@ -70,9 +70,7 @@ const CursorWrapper = /*#__PURE__*/ (0, _react.forwardRef)(function CursorWrappe
70
70
  domSel.collapse(node.parentNode, (0, _dom.domIndex)(node) + 1);
71
71
  // @ts-expect-error Internal property - domObserver
72
72
  view.domObserver.connectSelection();
73
- setTimeout(()=>{
74
- setShouldRender(false);
75
- });
73
+ setShouldRender(false);
76
74
  }, []);
77
75
  return shouldRender ? /*#__PURE__*/ _react.default.createElement("img", {
78
76
  ref: innerRef,
@@ -55,8 +55,7 @@ let TextNodeView = class TextNodeView extends _react.Component {
55
55
  }
56
56
  const pos = getPos();
57
57
  const { from, to } = view.state.selection;
58
- if (!(view.state.selection instanceof _prosemirrorstate.TextSelection) || from < pos || // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
59
- to > pos + node.nodeSize) {
58
+ if (!(view.state.selection instanceof _prosemirrorstate.TextSelection) || from < pos || to > pos + node.nodeSize) {
60
59
  return false;
61
60
  }
62
61
  return this.containsCompositionNodeText;
@@ -123,22 +122,20 @@ let TextNodeView = class TextNodeView extends _react.Component {
123
122
  this.destroy();
124
123
  this.viewDescRef = this.create();
125
124
  }
126
- setTimeout(()=>{
127
- const { view, node } = this.props;
128
- if (!(view instanceof _ReactEditorView.ReactEditorView)) {
129
- this.containsCompositionNodeText = true;
130
- return;
131
- }
132
- const textNode = view.input.compositionNode;
133
- if (!textNode) {
134
- this.containsCompositionNodeText = true;
135
- return;
136
- }
137
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138
- const text = textNode.nodeValue;
139
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
140
- this.containsCompositionNodeText = node.text === text;
141
- });
125
+ const { view, node } = this.props;
126
+ if (!(view instanceof _ReactEditorView.ReactEditorView)) {
127
+ this.containsCompositionNodeText = true;
128
+ return;
129
+ }
130
+ const textNode = view.input.compositionNode;
131
+ if (!textNode) {
132
+ this.containsCompositionNodeText = true;
133
+ return;
134
+ }
135
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
136
+ const text = textNode.nodeValue;
137
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138
+ this.containsCompositionNodeText = node.text === text;
142
139
  }
143
140
  shouldComponentUpdate(nextProps) {
144
141
  // When leaving the protected state, force a re-render so React's
@@ -11,6 +11,8 @@ Object.defineProperty(exports, "TrailingHackView", {
11
11
  const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
12
12
  const _ChildDescriptionsContext = require("../contexts/ChildDescriptionsContext.js");
13
13
  const _useClientLayoutEffect = require("../hooks/useClientLayoutEffect.js");
14
+ const _useEditorEffect = require("../hooks/useEditorEffect.js");
15
+ const _useEditorEventListener = require("../hooks/useEditorEventListener.js");
14
16
  const _viewdesc = require("../viewdesc.js");
15
17
  function _getRequireWildcardCache(nodeInterop) {
16
18
  if (typeof WeakMap !== "function") return null;
@@ -55,6 +57,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
55
57
  }
56
58
  function TrailingHackView(param) {
57
59
  let { getPos } = param;
60
+ const [shouldRender, setShouldRender] = (0, _react.useState)(true);
58
61
  const { siblingsRef, parentRef } = (0, _react.useContext)(_ChildDescriptionsContext.ChildDescriptionsContext);
59
62
  const viewDescRef = (0, _react.useRef)(null);
60
63
  const ref = (0, _react.useRef)(null);
@@ -83,6 +86,32 @@ function TrailingHackView(param) {
83
86
  }
84
87
  siblingsRef.current.sort(_viewdesc.sortViewDescs);
85
88
  });
89
+ // At the start of a composition, the browser will automatically delete
90
+ // the trailing hack br element. We need to unmount ourselves _before_
91
+ // that happens, so that React doesn't try to remove the already-removed
92
+ // br node when this component gets unmounted
93
+ (0, _useEditorEventListener.useEditorEventListener)("compositionstart", (view)=>{
94
+ const { from } = view.state.selection;
95
+ if (from === getPos()) {
96
+ setShouldRender(false);
97
+ }
98
+ });
99
+ // We need to run the same composition check when we first get mounted,
100
+ // in case we got mounted in the same render batch as the beginning of
101
+ // a composition
102
+ (0, _useEditorEffect.useEditorEffect)((view)=>{
103
+ if (!view.composing) return;
104
+ const { from } = view.state.selection;
105
+ if (from === getPos()) {
106
+ setShouldRender(false);
107
+ }
108
+ }, [
109
+ getPos
110
+ ]);
111
+ (0, _useEditorEventListener.useEditorEventListener)("compositionend", ()=>{
112
+ setShouldRender(true);
113
+ });
114
+ if (!shouldRender) return null;
86
115
  return /*#__PURE__*/ _react.default.createElement("br", {
87
116
  ref: ref,
88
117
  className: "ProseMirror-trailingBreak"
@@ -102,20 +102,9 @@ function useEditor(mount, options) {
102
102
  // running effects. Running effects will reattach selection
103
103
  // change listeners if the EditorView has been destroyed.
104
104
  if (view instanceof _ReactEditorView.ReactEditorView && !view.isDestroyed) {
105
- if (view.deferPendingEffects) {
106
- setTimeout(()=>{
107
- // Plugins might dispatch transactions from their
108
- // view update lifecycle hooks
109
- flushSyncRef.current = false;
110
- view.commitPendingEffects();
111
- flushSyncRef.current = true;
112
- });
113
- } else {
114
- flushSyncRef.current = false;
115
- view.commitPendingEffects();
116
- flushSyncRef.current = true;
117
- }
105
+ flushSyncRef.current = false;
118
106
  view.commitPendingEffects();
107
+ flushSyncRef.current = true;
119
108
  }
120
109
  });
121
110
  view.update(directEditorProps);
@@ -144,45 +144,43 @@ function useNodeViewDescription(getDOM, getContentDOM, constructor, props) {
144
144
  for (const child of children){
145
145
  child.parent = viewDesc;
146
146
  }
147
- setTimeout(()=>{
148
- // Because TextNodeViews can't locate the DOM nodes
149
- // for compositions, we need to override them here
150
- if (!viewDescRef.current?.contentDOM) return;
151
- const children = viewDescRef.current?.children;
152
- const compositionChildIndex = children.findIndex((child)=>child instanceof _viewdesc.CompositionViewDesc);
153
- if (compositionChildIndex === -1) return;
154
- const compositionViewDesc = children[compositionChildIndex];
155
- if (!(compositionViewDesc instanceof _viewdesc.CompositionViewDesc)) return;
156
- let compositionTopDOM = null;
157
- let search = children[compositionChildIndex - 1];
158
- while(search instanceof _viewdesc.MarkViewDesc){
159
- search = search.children[0];
160
- }
161
- if (search instanceof _viewdesc.WidgetViewDesc && search.widget.type instanceof _ReactWidgetType.ReactWidgetType && search.widget.type.Component === _CursorWrapper.CursorWrapper) {
162
- compositionTopDOM = search.dom.nextSibling;
163
- } else {
164
- for (const childNode of viewDescRef.current.contentDOM.childNodes){
165
- if (children.every((child)=>child.dom !== childNode)) {
166
- compositionTopDOM = childNode;
167
- break;
168
- }
147
+ if (!props.node.isTextblock) return;
148
+ // Because TextNodeViews can't locate the DOM nodes
149
+ // for compositions, we need to override them here
150
+ if (!viewDescRef.current?.contentDOM) return;
151
+ const compositionChildIndex = children.findIndex((child)=>child instanceof _viewdesc.CompositionViewDesc);
152
+ if (compositionChildIndex === -1) return;
153
+ const compositionViewDesc = children[compositionChildIndex];
154
+ if (!(compositionViewDesc instanceof _viewdesc.CompositionViewDesc)) return;
155
+ let compositionTopDOM = null;
156
+ let search = children[compositionChildIndex - 1];
157
+ while(search instanceof _viewdesc.MarkViewDesc){
158
+ search = search.children[0];
159
+ }
160
+ if (search instanceof _viewdesc.WidgetViewDesc && search.widget.type instanceof _ReactWidgetType.ReactWidgetType && search.widget.type.Component === _CursorWrapper.CursorWrapper) {
161
+ compositionTopDOM = search.dom.nextSibling;
162
+ } else {
163
+ for (const childNode of viewDescRef.current.contentDOM.childNodes){
164
+ if (children.every((child)=>child.dom !== childNode)) {
165
+ compositionTopDOM = childNode;
166
+ break;
169
167
  }
170
168
  }
171
- if (!compositionTopDOM) return;
172
- let textDOM = compositionTopDOM;
173
- while(textDOM.firstChild){
174
- textDOM = textDOM.firstChild;
175
- }
176
- if (!textDOM || !(textDOM instanceof Text)) {
177
- console.error(compositionTopDOM, textDOM);
178
- throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
179
- }
180
- compositionViewDesc.dom = compositionTopDOM;
181
- compositionViewDesc.textDOM = textDOM;
182
- compositionViewDesc.text = textDOM.data;
183
- compositionViewDesc.textDOM.pmViewDesc = compositionViewDesc;
184
- view.input.compositionNodes.push(compositionViewDesc);
185
- });
169
+ }
170
+ if (!compositionTopDOM) return;
171
+ let textDOM = compositionTopDOM;
172
+ while(textDOM.firstChild){
173
+ textDOM = textDOM.firstChild;
174
+ }
175
+ if (!textDOM || !(textDOM instanceof Text)) {
176
+ console.error(compositionTopDOM, textDOM);
177
+ throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
178
+ }
179
+ compositionViewDesc.dom = compositionTopDOM;
180
+ compositionViewDesc.textDOM = textDOM;
181
+ compositionViewDesc.text = textDOM.data;
182
+ compositionViewDesc.textDOM.pmViewDesc = compositionViewDesc;
183
+ view.input.compositionNodes.push(compositionViewDesc);
186
184
  });
187
185
  const childContextValue = (0, _react.useMemo)(()=>({
188
186
  parentRef: viewDescRef,
@@ -62,11 +62,12 @@ function beforeInputPlugin(setCursorWrapper) {
62
62
  handleDOMEvents: {
63
63
  compositionstart (view) {
64
64
  if (!(view instanceof _ReactEditorView.ReactEditorView)) return false;
65
+ view.input.composing = true;
65
66
  compositionMarks = view.state.storedMarks;
66
- view.dispatch(view.state.tr.deleteSelection());
67
+ const tr = view.state.tr.deleteSelection().setStoredMarks(null);
68
+ view.dispatch(tr);
67
69
  handleGapCursorComposition(view);
68
70
  const { state } = view;
69
- // const $pos = state.selection.$from;
70
71
  if (compositionMarks?.length) {
71
72
  setCursorWrapper((0, _ReactWidgetType.widget)(state.selection.from, _CursorWrapper.CursorWrapper, {
72
73
  key: "cursor-wrapper",
@@ -74,7 +75,6 @@ function beforeInputPlugin(setCursorWrapper) {
74
75
  side: 1
75
76
  }));
76
77
  }
77
- view.input.composing = true;
78
78
  return true;
79
79
  },
80
80
  compositionupdate () {
@@ -86,7 +86,7 @@ function beforeInputPlugin(setCursorWrapper) {
86
86
  view.input.composing = false;
87
87
  compositionMarks = null;
88
88
  setCursorWrapper(null);
89
- if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc && (view.input.compositionNode instanceof Text || view.input.compositionNode instanceof Element)) {
89
+ if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc) {
90
90
  view.input.compositionNode.remove();
91
91
  }
92
92
  view.input.compositionEndedAt = event.timeStamp;
@@ -157,19 +157,11 @@ function beforeInputPlugin(setCursorWrapper) {
157
157
  } else {
158
158
  tr.delete(start, end);
159
159
  }
160
- // When we insert the text that corresponds to an ongoing composition,
161
- // the relevant TextNodeView will pause re-rendering so that React doesn't
162
- // clobber the composition in the DOM. This means that we have to wait for
163
- // the browser to update the DOM itself before attempting to reconcile
164
- // the selection, so we specifically defer pending effects to the next
165
- // macro task
166
- if (view instanceof _ReactEditorView.ReactEditorView) {
167
- view.deferPendingEffects = true;
168
- }
169
- view.dispatch(tr);
170
- if (view instanceof _ReactEditorView.ReactEditorView) {
171
- view.deferPendingEffects = false;
172
- }
160
+ view.dom.addEventListener("input", ()=>{
161
+ view.dispatch(tr);
162
+ }, {
163
+ once: true
164
+ });
173
165
  break;
174
166
  }
175
167
  case "deleteWordBackward":
@@ -345,14 +345,14 @@ export const ChildNodeViews = /*#__PURE__*/ memo(function ChildNodeViews(param)
345
345
  component: SeparatorHackView,
346
346
  marks: [],
347
347
  offset: lastChild?.offset ?? 0,
348
- index: (lastChild?.index ?? 0) + 2,
348
+ index: (lastChild?.index ?? 0) + 1,
349
349
  key: "trailing-hack-img"
350
350
  }, {
351
351
  type: "hack",
352
352
  component: TrailingHackView,
353
353
  marks: [],
354
354
  offset: lastChild?.offset ?? 0,
355
- index: (lastChild?.index ?? 0) + 1,
355
+ index: (lastChild?.index ?? 0) + 2,
356
356
  key: "trailing-hack-br"
357
357
  });
358
358
  }
@@ -19,9 +19,7 @@ export const CursorWrapper = /*#__PURE__*/ forwardRef(function CursorWrapper(par
19
19
  domSel.collapse(node.parentNode, domIndex(node) + 1);
20
20
  // @ts-expect-error Internal property - domObserver
21
21
  view.domObserver.connectSelection();
22
- setTimeout(()=>{
23
- setShouldRender(false);
24
- });
22
+ setShouldRender(false);
25
23
  }, []);
26
24
  return shouldRender ? /*#__PURE__*/ React.createElement("img", {
27
25
  ref: innerRef,
@@ -45,8 +45,7 @@ export class TextNodeView extends Component {
45
45
  }
46
46
  const pos = getPos();
47
47
  const { from, to } = view.state.selection;
48
- if (!(view.state.selection instanceof TextSelection) || from < pos || // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
49
- to > pos + node.nodeSize) {
48
+ if (!(view.state.selection instanceof TextSelection) || from < pos || to > pos + node.nodeSize) {
50
49
  return false;
51
50
  }
52
51
  return this.containsCompositionNodeText;
@@ -113,22 +112,20 @@ export class TextNodeView extends Component {
113
112
  this.destroy();
114
113
  this.viewDescRef = this.create();
115
114
  }
116
- setTimeout(()=>{
117
- const { view, node } = this.props;
118
- if (!(view instanceof ReactEditorView)) {
119
- this.containsCompositionNodeText = true;
120
- return;
121
- }
122
- const textNode = view.input.compositionNode;
123
- if (!textNode) {
124
- this.containsCompositionNodeText = true;
125
- return;
126
- }
127
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
128
- const text = textNode.nodeValue;
129
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
130
- this.containsCompositionNodeText = node.text === text;
131
- });
115
+ const { view, node } = this.props;
116
+ if (!(view instanceof ReactEditorView)) {
117
+ this.containsCompositionNodeText = true;
118
+ return;
119
+ }
120
+ const textNode = view.input.compositionNode;
121
+ if (!textNode) {
122
+ this.containsCompositionNodeText = true;
123
+ return;
124
+ }
125
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
126
+ const text = textNode.nodeValue;
127
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
128
+ this.containsCompositionNodeText = node.text === text;
132
129
  }
133
130
  shouldComponentUpdate(nextProps) {
134
131
  // When leaving the protected state, force a re-render so React's
@@ -1,9 +1,12 @@
1
- import React, { useContext, useRef } from "react";
1
+ import React, { useContext, useRef, useState } from "react";
2
2
  import { ChildDescriptionsContext } from "../contexts/ChildDescriptionsContext.js";
3
3
  import { useClientLayoutEffect } from "../hooks/useClientLayoutEffect.js";
4
+ import { useEditorEffect } from "../hooks/useEditorEffect.js";
5
+ import { useEditorEventListener } from "../hooks/useEditorEventListener.js";
4
6
  import { TrailingHackViewDesc, sortViewDescs } from "../viewdesc.js";
5
7
  export function TrailingHackView(param) {
6
8
  let { getPos } = param;
9
+ const [shouldRender, setShouldRender] = useState(true);
7
10
  const { siblingsRef, parentRef } = useContext(ChildDescriptionsContext);
8
11
  const viewDescRef = useRef(null);
9
12
  const ref = useRef(null);
@@ -32,6 +35,32 @@ export function TrailingHackView(param) {
32
35
  }
33
36
  siblingsRef.current.sort(sortViewDescs);
34
37
  });
38
+ // At the start of a composition, the browser will automatically delete
39
+ // the trailing hack br element. We need to unmount ourselves _before_
40
+ // that happens, so that React doesn't try to remove the already-removed
41
+ // br node when this component gets unmounted
42
+ useEditorEventListener("compositionstart", (view)=>{
43
+ const { from } = view.state.selection;
44
+ if (from === getPos()) {
45
+ setShouldRender(false);
46
+ }
47
+ });
48
+ // We need to run the same composition check when we first get mounted,
49
+ // in case we got mounted in the same render batch as the beginning of
50
+ // a composition
51
+ useEditorEffect((view)=>{
52
+ if (!view.composing) return;
53
+ const { from } = view.state.selection;
54
+ if (from === getPos()) {
55
+ setShouldRender(false);
56
+ }
57
+ }, [
58
+ getPos
59
+ ]);
60
+ useEditorEventListener("compositionend", ()=>{
61
+ setShouldRender(true);
62
+ });
63
+ if (!shouldRender) return null;
35
64
  return /*#__PURE__*/ React.createElement("br", {
36
65
  ref: ref,
37
66
  className: "ProseMirror-trailingBreak"
@@ -100,20 +100,9 @@ let didWarnValueDefaultValue = false;
100
100
  // running effects. Running effects will reattach selection
101
101
  // change listeners if the EditorView has been destroyed.
102
102
  if (view instanceof ReactEditorView && !view.isDestroyed) {
103
- if (view.deferPendingEffects) {
104
- setTimeout(()=>{
105
- // Plugins might dispatch transactions from their
106
- // view update lifecycle hooks
107
- flushSyncRef.current = false;
108
- view.commitPendingEffects();
109
- flushSyncRef.current = true;
110
- });
111
- } else {
112
- flushSyncRef.current = false;
113
- view.commitPendingEffects();
114
- flushSyncRef.current = true;
115
- }
103
+ flushSyncRef.current = false;
116
104
  view.commitPendingEffects();
105
+ flushSyncRef.current = true;
117
106
  }
118
107
  });
119
108
  view.update(directEditorProps);
@@ -134,45 +134,43 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
134
134
  for (const child of children){
135
135
  child.parent = viewDesc;
136
136
  }
137
- setTimeout(()=>{
138
- // Because TextNodeViews can't locate the DOM nodes
139
- // for compositions, we need to override them here
140
- if (!viewDescRef.current?.contentDOM) return;
141
- const children = viewDescRef.current?.children;
142
- const compositionChildIndex = children.findIndex((child)=>child instanceof CompositionViewDesc);
143
- if (compositionChildIndex === -1) return;
144
- const compositionViewDesc = children[compositionChildIndex];
145
- if (!(compositionViewDesc instanceof CompositionViewDesc)) return;
146
- let compositionTopDOM = null;
147
- let search = children[compositionChildIndex - 1];
148
- while(search instanceof MarkViewDesc){
149
- search = search.children[0];
150
- }
151
- if (search instanceof WidgetViewDesc && search.widget.type instanceof ReactWidgetType && search.widget.type.Component === CursorWrapper) {
152
- compositionTopDOM = search.dom.nextSibling;
153
- } else {
154
- for (const childNode of viewDescRef.current.contentDOM.childNodes){
155
- if (children.every((child)=>child.dom !== childNode)) {
156
- compositionTopDOM = childNode;
157
- break;
158
- }
137
+ if (!props.node.isTextblock) return;
138
+ // Because TextNodeViews can't locate the DOM nodes
139
+ // for compositions, we need to override them here
140
+ if (!viewDescRef.current?.contentDOM) return;
141
+ const compositionChildIndex = children.findIndex((child)=>child instanceof CompositionViewDesc);
142
+ if (compositionChildIndex === -1) return;
143
+ const compositionViewDesc = children[compositionChildIndex];
144
+ if (!(compositionViewDesc instanceof CompositionViewDesc)) return;
145
+ let compositionTopDOM = null;
146
+ let search = children[compositionChildIndex - 1];
147
+ while(search instanceof MarkViewDesc){
148
+ search = search.children[0];
149
+ }
150
+ if (search instanceof WidgetViewDesc && search.widget.type instanceof ReactWidgetType && search.widget.type.Component === CursorWrapper) {
151
+ compositionTopDOM = search.dom.nextSibling;
152
+ } else {
153
+ for (const childNode of viewDescRef.current.contentDOM.childNodes){
154
+ if (children.every((child)=>child.dom !== childNode)) {
155
+ compositionTopDOM = childNode;
156
+ break;
159
157
  }
160
158
  }
161
- if (!compositionTopDOM) return;
162
- let textDOM = compositionTopDOM;
163
- while(textDOM.firstChild){
164
- textDOM = textDOM.firstChild;
165
- }
166
- if (!textDOM || !(textDOM instanceof Text)) {
167
- console.error(compositionTopDOM, textDOM);
168
- throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
169
- }
170
- compositionViewDesc.dom = compositionTopDOM;
171
- compositionViewDesc.textDOM = textDOM;
172
- compositionViewDesc.text = textDOM.data;
173
- compositionViewDesc.textDOM.pmViewDesc = compositionViewDesc;
174
- view.input.compositionNodes.push(compositionViewDesc);
175
- });
159
+ }
160
+ if (!compositionTopDOM) return;
161
+ let textDOM = compositionTopDOM;
162
+ while(textDOM.firstChild){
163
+ textDOM = textDOM.firstChild;
164
+ }
165
+ if (!textDOM || !(textDOM instanceof Text)) {
166
+ console.error(compositionTopDOM, textDOM);
167
+ throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
168
+ }
169
+ compositionViewDesc.dom = compositionTopDOM;
170
+ compositionViewDesc.textDOM = textDOM;
171
+ compositionViewDesc.text = textDOM.data;
172
+ compositionViewDesc.textDOM.pmViewDesc = compositionViewDesc;
173
+ view.input.compositionNodes.push(compositionViewDesc);
176
174
  });
177
175
  const childContextValue = useMemo(()=>({
178
176
  parentRef: viewDescRef,
@@ -52,11 +52,12 @@ export function beforeInputPlugin(setCursorWrapper) {
52
52
  handleDOMEvents: {
53
53
  compositionstart (view) {
54
54
  if (!(view instanceof ReactEditorView)) return false;
55
+ view.input.composing = true;
55
56
  compositionMarks = view.state.storedMarks;
56
- view.dispatch(view.state.tr.deleteSelection());
57
+ const tr = view.state.tr.deleteSelection().setStoredMarks(null);
58
+ view.dispatch(tr);
57
59
  handleGapCursorComposition(view);
58
60
  const { state } = view;
59
- // const $pos = state.selection.$from;
60
61
  if (compositionMarks?.length) {
61
62
  setCursorWrapper(widget(state.selection.from, CursorWrapper, {
62
63
  key: "cursor-wrapper",
@@ -64,7 +65,6 @@ export function beforeInputPlugin(setCursorWrapper) {
64
65
  side: 1
65
66
  }));
66
67
  }
67
- view.input.composing = true;
68
68
  return true;
69
69
  },
70
70
  compositionupdate () {
@@ -76,7 +76,7 @@ export function beforeInputPlugin(setCursorWrapper) {
76
76
  view.input.composing = false;
77
77
  compositionMarks = null;
78
78
  setCursorWrapper(null);
79
- if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc && (view.input.compositionNode instanceof Text || view.input.compositionNode instanceof Element)) {
79
+ if (view.input.compositionNode && !view.input.compositionNode.pmViewDesc) {
80
80
  view.input.compositionNode.remove();
81
81
  }
82
82
  view.input.compositionEndedAt = event.timeStamp;
@@ -147,19 +147,11 @@ export function beforeInputPlugin(setCursorWrapper) {
147
147
  } else {
148
148
  tr.delete(start, end);
149
149
  }
150
- // When we insert the text that corresponds to an ongoing composition,
151
- // the relevant TextNodeView will pause re-rendering so that React doesn't
152
- // clobber the composition in the DOM. This means that we have to wait for
153
- // the browser to update the DOM itself before attempting to reconcile
154
- // the selection, so we specifically defer pending effects to the next
155
- // macro task
156
- if (view instanceof ReactEditorView) {
157
- view.deferPendingEffects = true;
158
- }
159
- view.dispatch(tr);
160
- if (view instanceof ReactEditorView) {
161
- view.deferPendingEffects = false;
162
- }
150
+ view.dom.addEventListener("input", ()=>{
151
+ view.dispatch(tr);
152
+ }, {
153
+ once: true
154
+ });
163
155
  break;
164
156
  }
165
157
  case "deleteWordBackward":