@zzish/math-rich-input 0.1.55 → 0.1.57

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.
package/dist/index.js CHANGED
@@ -169,6 +169,57 @@ function _arrayLikeToArray(arr, len) {
169
169
  function _nonIterableSpread() {
170
170
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
171
171
  }
172
+ function _createForOfIteratorHelper(o, allowArrayLike) {
173
+ var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
174
+ if (!it) {
175
+ if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
176
+ if (it) o = it;
177
+ var i = 0;
178
+ var F = function () {};
179
+ return {
180
+ s: F,
181
+ n: function () {
182
+ if (i >= o.length) return {
183
+ done: true
184
+ };
185
+ return {
186
+ done: false,
187
+ value: o[i++]
188
+ };
189
+ },
190
+ e: function (e) {
191
+ throw e;
192
+ },
193
+ f: F
194
+ };
195
+ }
196
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
197
+ }
198
+ var normalCompletion = true,
199
+ didErr = false,
200
+ err;
201
+ return {
202
+ s: function () {
203
+ it = it.call(o);
204
+ },
205
+ n: function () {
206
+ var step = it.next();
207
+ normalCompletion = step.done;
208
+ return step;
209
+ },
210
+ e: function (e) {
211
+ didErr = true;
212
+ err = e;
213
+ },
214
+ f: function () {
215
+ try {
216
+ if (!normalCompletion && it.return != null) it.return();
217
+ } finally {
218
+ if (didErr) throw err;
219
+ }
220
+ }
221
+ };
222
+ }
172
223
 
173
224
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
174
225
 
@@ -21972,6 +22023,229 @@ function plainTextOffsetToGlobalOffset(editableDiv, plainTextOffset) {
21972
22023
  */
21973
22024
  var SELECTION_MARK = MARK$1;
21974
22025
 
22026
+ /**
22027
+ * Capture browser editing intent in the logical text coordinates used by MathRichInput.
22028
+ *
22029
+ * The component deliberately reports a measured replacement range instead of asking a host to diff the
22030
+ * old and new strings. A host can therefore retain two equal names independently and reject a stale
22031
+ * event before it changes protected content.
22032
+ */
22033
+
22034
+ var ZERO_WIDTH_SPACE = "\u200B";
22035
+ var BLOCK_TAGS = new Set(["p", "div", "section", "article", "blockquote", "pre", "li", "ul", "ol", "h1", "h2", "h3", "h4", "h5", "h6", "table", "thead", "tbody", "tfoot", "tr", "td", "th", "hr"]);
22036
+ var INERT_TAGS = new Set(["script", "style", "template", "head", "title"]);
22037
+
22038
+ /** Return whether a node is the rendered KaTeX island that cannot be edited character by character. */
22039
+ function isMathNode(node) {
22040
+ var _node$classList, _node$classList2;
22041
+ return node && node.nodeType === 1 && (((_node$classList = node.classList) === null || _node$classList === void 0 ? void 0 : _node$classList.contains("katex")) || ((_node$classList2 = node.classList) === null || _node$classList2 === void 0 ? void 0 : _node$classList2.contains("katex-error")));
22042
+ }
22043
+
22044
+ /** Return whether a tag contributes a logical line boundary to the source text. */
22045
+ function isBlock(node) {
22046
+ if (!node || node.nodeType !== 1) return false;
22047
+ return BLOCK_TAGS.has(node.tagName.toLowerCase());
22048
+ }
22049
+ function isInert(node) {
22050
+ if (!node || node.nodeType !== 1) return false;
22051
+ return INERT_TAGS.has(node.tagName.toLowerCase());
22052
+ }
22053
+ function trimZeroWidth(value) {
22054
+ return value.split(ZERO_WIDTH_SPACE).join("");
22055
+ }
22056
+
22057
+ /** Build the text coordinate string without reading attributes or rendered math markup. */
22058
+ function editableText(root) {
22059
+ if (!root) return "";
22060
+ var pieces = [];
22061
+ var pendingBoundary = false;
22062
+ var walk = function walk(node) {
22063
+ if (isInert(node)) return;
22064
+ if (isMathNode(node)) {
22065
+ var _node$querySelector;
22066
+ if (pendingBoundary && pieces.length && pieces[pieces.length - 1] !== "\n") pieces.push("\n");
22067
+ var latex = ((_node$querySelector = node.querySelector) === null || _node$querySelector === void 0 || (_node$querySelector = _node$querySelector.call(node, 'annotation[encoding="application/x-tex"]')) === null || _node$querySelector === void 0 ? void 0 : _node$querySelector.textContent) || "";
22068
+ pieces.push(latex);
22069
+ pendingBoundary = false;
22070
+ return;
22071
+ }
22072
+ if (node.nodeType === 3) {
22073
+ var text = trimZeroWidth(node.nodeValue || "");
22074
+ if (text && pendingBoundary && pieces.length && pieces[pieces.length - 1] !== "\n") pieces.push("\n");
22075
+ if (text) pieces.push(text);
22076
+ if (text) pendingBoundary = false;
22077
+ return;
22078
+ }
22079
+ if (node.nodeType !== 1 && node.nodeType !== 11) return;
22080
+ if (node.nodeType === 1 && node.tagName.toLowerCase() === "br") {
22081
+ pieces.push("\n");
22082
+ pendingBoundary = false;
22083
+ return;
22084
+ }
22085
+ var block = isBlock(node);
22086
+ if (block && pieces.length) pendingBoundary = true;
22087
+ var _iterator = _createForOfIteratorHelper(node.childNodes || []),
22088
+ _step;
22089
+ try {
22090
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
22091
+ var child = _step.value;
22092
+ walk(child);
22093
+ }
22094
+ } catch (err) {
22095
+ _iterator.e(err);
22096
+ } finally {
22097
+ _iterator.f();
22098
+ }
22099
+ if (block) pendingBoundary = true;
22100
+ };
22101
+ var _iterator2 = _createForOfIteratorHelper(root.childNodes || []),
22102
+ _step2;
22103
+ try {
22104
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
22105
+ var child = _step2.value;
22106
+ walk(child);
22107
+ }
22108
+ } catch (err) {
22109
+ _iterator2.e(err);
22110
+ } finally {
22111
+ _iterator2.f();
22112
+ }
22113
+ return pieces.join("");
22114
+ }
22115
+ function textBeforeEndpoint(root, container, offset) {
22116
+ if (!root || !container || !root.contains(container)) return null;
22117
+ var range = document.createRange();
22118
+ range.selectNodeContents(root);
22119
+ try {
22120
+ range.setEnd(container, offset);
22121
+ } catch (_error) {
22122
+ return null;
22123
+ }
22124
+ var clone = document.createElement("span");
22125
+ clone.appendChild(range.cloneContents());
22126
+ return editableText(clone).length;
22127
+ }
22128
+
22129
+ /** Read a collapsed or selected DOM range in logical UTF-16 offsets. */
22130
+ function selectionOffsets(root) {
22131
+ var _window$getSelection, _window;
22132
+ var selection = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : (_window$getSelection = (_window = window).getSelection) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.call(_window);
22133
+ if (!root || !selection || selection.rangeCount === 0) return null;
22134
+ var range = selection.getRangeAt(0);
22135
+ var from = textBeforeEndpoint(root, range.startContainer, range.startOffset);
22136
+ var to = textBeforeEndpoint(root, range.endContainer, range.endOffset);
22137
+ if (from === null || to === null) return null;
22138
+ return from <= to ? {
22139
+ from: from,
22140
+ to: to
22141
+ } : {
22142
+ from: to,
22143
+ to: from
22144
+ };
22145
+ }
22146
+ function expandSurrogateBoundary(text, from, to) {
22147
+ var before = text.charCodeAt(from - 1);
22148
+ var after = text.charCodeAt(from);
22149
+ if (before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff) {
22150
+ return {
22151
+ from: from - 1,
22152
+ to: Math.max(to, from + 1)
22153
+ };
22154
+ }
22155
+ var at = text.charCodeAt(to - 1);
22156
+ var next = text.charCodeAt(to);
22157
+ if (at >= 0xd800 && at <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) {
22158
+ return {
22159
+ from: Math.min(from, to - 1),
22160
+ to: to + 1
22161
+ };
22162
+ }
22163
+ return {
22164
+ from: from,
22165
+ to: to
22166
+ };
22167
+ }
22168
+ function textFromTransfer(dataTransfer) {
22169
+ var _dataTransfer$getData, _dataTransfer$getData2;
22170
+ if (!dataTransfer) return "";
22171
+ var plain = ((_dataTransfer$getData = dataTransfer.getData) === null || _dataTransfer$getData === void 0 ? void 0 : _dataTransfer$getData.call(dataTransfer, "text/plain")) || "";
22172
+ if (plain) return plain;
22173
+ var html = ((_dataTransfer$getData2 = dataTransfer.getData) === null || _dataTransfer$getData2 === void 0 ? void 0 : _dataTransfer$getData2.call(dataTransfer, "text/html")) || "";
22174
+ if (!html || typeof document === "undefined") return "";
22175
+ var probe = document.createElement("div");
22176
+ probe.innerHTML = html;
22177
+ return editableText(probe);
22178
+ }
22179
+
22180
+ /**
22181
+ * Convert a browser `beforeinput` event into an explicit replacement.
22182
+ *
22183
+ * A null return means the browser supplied no safe range or the operation is outside the supported text
22184
+ * edits. The caller still emits the intent, without an edit, so a protected host can refuse it loudly.
22185
+ */
22186
+ function transactionFromBeforeInput(root, event) {
22187
+ var _event$data;
22188
+ var compositionStart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
22189
+ var before = editableText(root);
22190
+ var selection = selectionOffsets(root);
22191
+ if (!selection) return {
22192
+ before: before,
22193
+ intent: intentForInputType(event === null || event === void 0 ? void 0 : event.inputType),
22194
+ edit: null
22195
+ };
22196
+ var inputType = (event === null || event === void 0 ? void 0 : event.inputType) || "insertText";
22197
+ var intent = intentForInputType(inputType);
22198
+ var from = selection.from,
22199
+ to = selection.to;
22200
+ var insert = (_event$data = event === null || event === void 0 ? void 0 : event.data) !== null && _event$data !== void 0 ? _event$data : "";
22201
+ if (inputType === "insertFromPaste" || inputType === "insertFromDrop") {
22202
+ insert = textFromTransfer(event === null || event === void 0 ? void 0 : event.dataTransfer);
22203
+ } else if (inputType === "insertLineBreak" || inputType === "insertParagraph") {
22204
+ insert = "\n";
22205
+ } else if (inputType === "deleteContentBackward" && from === to) {
22206
+ var _expandSurrogateBound = expandSurrogateBoundary(before, Math.max(0, from - 1), from);
22207
+ from = _expandSurrogateBound.from;
22208
+ to = _expandSurrogateBound.to;
22209
+ } else if (inputType === "deleteContentForward" && from === to) {
22210
+ var _expandSurrogateBound2 = expandSurrogateBoundary(before, from, Math.min(before.length, to + 1));
22211
+ from = _expandSurrogateBound2.from;
22212
+ to = _expandSurrogateBound2.to;
22213
+ } else if (inputType.startsWith("delete") && from === to) {
22214
+ return {
22215
+ before: before,
22216
+ intent: intent,
22217
+ edit: null
22218
+ };
22219
+ }
22220
+ var base = compositionStart && intent === "composition" ? compositionStart : {
22221
+ before: before,
22222
+ from: from,
22223
+ to: to
22224
+ };
22225
+ return {
22226
+ before: base.before,
22227
+ intent: intent,
22228
+ edit: {
22229
+ before: base.before,
22230
+ from: base.from,
22231
+ to: base.to,
22232
+ insert: insert
22233
+ }
22234
+ };
22235
+ }
22236
+
22237
+ /** Give hosts a stable vocabulary while preserving the browser's native inputType for diagnostics. */
22238
+ function intentForInputType(inputType) {
22239
+ if (!inputType) return "input";
22240
+ if (inputType.includes("Composition")) return "composition";
22241
+ if (inputType.includes("Paste")) return "paste";
22242
+ if (inputType.includes("Drop")) return "drop";
22243
+ if (inputType.includes("Cut")) return "cut";
22244
+ if (inputType === "historyUndo") return "undo";
22245
+ if (inputType === "historyRedo") return "redo";
22246
+ return "input";
22247
+ }
22248
+
21975
22249
  var MIME_TYPE_PLAIN_TEXT = "text/plain";
21976
22250
  var MIME_TYPE_HTML = "text/html";
21977
22251
  var MIME_TYPE_TEX = "application/x-tex";
@@ -22010,6 +22284,7 @@ var EMPTY_INLINE_FORMATTING_REG_EXP = /<(B|STRONG|I|EM|U|SUB|SUP)>(?:\s|&nbsp;|\
22010
22284
  // const LATEX_MARKER_END = "</annotation>"
22011
22285
 
22012
22286
  var MAX_HISTORY_LENGTH = 100;
22287
+ var CONTROLLED_VALUE_ECHO_TIMEOUT_MS = 50;
22013
22288
  var DEFAULT_OPTIONS = {
22014
22289
  useExpertMode: false,
22015
22290
  selectedTab: null
@@ -22177,26 +22452,49 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22177
22452
  var options = _this2.props.options || DEFAULT_OPTIONS;
22178
22453
  if (isExpertMode === null) isExpertMode = options.isExpertMode;
22179
22454
  if (selectedTab === null) selectedTab = options.selectedTab;
22180
-
22181
- // Add this to the history
22182
- _this2.undoHistory.push({
22455
+ var transaction = applyOptions.transaction || null;
22456
+ var historyState = {
22183
22457
  value: value,
22184
22458
  mimeType: mimeType,
22185
- rangeParams: rangeParams
22186
- });
22187
- if (_this2.undoHistory.length > MAX_HISTORY_LENGTH) _this2.undoHistory.shift();
22459
+ rangeParams: rangeParams,
22460
+ transaction: transaction
22461
+ };
22462
+ if (applyOptions.recordHistory !== false) {
22463
+ // Add this to the history. The transaction is retained beside the rendered value so a host can
22464
+ // restore annotation snapshots on undo/redo instead of interpreting an inverse spelling anew.
22465
+ _this2.undoHistory.push(historyState);
22466
+ if (_this2.undoHistory.length > MAX_HISTORY_LENGTH) _this2.undoHistory.shift();
22467
+ }
22188
22468
 
22189
22469
  // Debug what's being passed to parent
22190
22470
  // console.log("APPLY CHANGES:", { value, mimeType });
22191
22471
 
22192
22472
  // Call the parent handler to apply the changes
22193
- _this2.onChangeCallback({
22473
+ _this2.onChangeCallback(_objectSpread2(_objectSpread2(_objectSpread2({
22194
22474
  value: value,
22195
22475
  mimeType: mimeType
22196
- }, {
22476
+ }, transaction !== null && transaction !== void 0 && transaction.edit ? {
22477
+ edit: transaction.edit
22478
+ } : {}), transaction !== null && transaction !== void 0 && transaction.intent ? {
22479
+ intent: transaction.intent
22480
+ } : {}), transaction !== null && transaction !== void 0 && transaction.history ? {
22481
+ history: transaction.history
22482
+ } : {}), {
22197
22483
  isExpertMode: isExpertMode,
22198
22484
  selectedTab: selectedTab
22199
22485
  });
22486
+ if (applyOptions.skipControlledRender === true) {
22487
+ var pendingNativeValue = value;
22488
+ // Give a scheduled controlled echo one short render window. If the host
22489
+ // never echoes the value, release the gate rather than preserving a
22490
+ // rejected native edit indefinitely.
22491
+ setTimeout(function () {
22492
+ if (_this2.skipNextControlledValueRender === true && _this2.skipNextControlledValue === pendingNativeValue) {
22493
+ _this2.skipNextControlledValueRender = false;
22494
+ _this2.skipNextControlledValue = null;
22495
+ }
22496
+ }, CONTROLLED_VALUE_ECHO_TIMEOUT_MS);
22497
+ }
22200
22498
  debugMathRichInput("applyChangesToComponent:after-onChange", {
22201
22499
  afterComponentUpdateData: _this2.afterComponentUpdateData,
22202
22500
  selection: getSelectionSnapshot(_this2.editableDiv)
@@ -22206,7 +22504,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22206
22504
  if (_this2.initialHistoryState == null) _this2.initialHistoryState = {
22207
22505
  value: _this2.props.value,
22208
22506
  mimeType: _this2.props.mimeType,
22209
- rangeParams: _this2._getRangeParams()
22507
+ rangeParams: _this2._getRangeParams(),
22508
+ transaction: null
22210
22509
  };
22211
22510
  });
22212
22511
  _defineProperty(_this2, "resetHistoryRedo", function () {
@@ -22225,23 +22524,60 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22225
22524
  return true;
22226
22525
  });
22227
22526
  _defineProperty(_this2, "undo", function () {
22228
- var currentState = _this2.undoHistory.pop();
22229
- var previousState = _this2.undoHistory.pop();
22230
- if (previousState === null || previousState === undefined) {
22231
- previousState = _this2.initialHistoryState;
22232
- }
22233
- if (_this2.redoHistory.length > 0) {
22234
- var redoLast = _this2.redoHistory[_this2.redoHistory.length - 1];
22235
- if (!_this2.deepEqual(redoLast, currentState)) _this2.redoHistory.push(currentState);
22236
- } else _this2.redoHistory.push(currentState);
22237
- _this2.applyChangesToComponent(previousState.value, previousState.mimeType, previousState.rangeParams);
22527
+ var currentState = _this2.undoHistory[_this2.undoHistory.length - 1];
22528
+ if (!currentState) return;
22529
+ _this2.undoHistory.pop();
22530
+ var previousState = _this2.undoHistory[_this2.undoHistory.length - 1] || _this2.initialHistoryState;
22531
+ if (!previousState) return;
22532
+ _this2.redoHistory.push(currentState);
22533
+ var transaction = _this2.historyTransaction(currentState, "undo");
22534
+ _this2.applyChangesToComponent(previousState.value, previousState.mimeType, previousState.rangeParams, null, null, {
22535
+ transaction: transaction,
22536
+ recordHistory: false
22537
+ });
22238
22538
  });
22239
22539
  _defineProperty(_this2, "redo", function () {
22240
22540
  var redoState = _this2.redoHistory.pop();
22241
- if (redoState !== null & redoState !== undefined) {
22242
- _this2.applyChangesToComponent(redoState.value, redoState.mimeType, redoState.rangeParams);
22541
+ if (redoState !== null && redoState !== undefined) {
22542
+ _this2.applyChangesToComponent(redoState.value, redoState.mimeType, redoState.rangeParams, null, null, {
22543
+ transaction: _this2.historyTransaction(redoState, "redo"),
22544
+ recordHistory: false
22545
+ });
22546
+ _this2.undoHistory.push(redoState);
22243
22547
  }
22244
22548
  });
22549
+ /** Build an explicit forward/inverse transaction for one history entry. */
22550
+ _defineProperty(_this2, "historyTransaction", function (state, direction) {
22551
+ var _original$history;
22552
+ var original = state === null || state === void 0 ? void 0 : state.transaction;
22553
+ if (!(original !== null && original !== void 0 && (_original$history = original.history) !== null && _original$history !== void 0 && _original$history.id) || !original.edit) {
22554
+ return {
22555
+ intent: direction,
22556
+ history: original !== null && original !== void 0 && original.history ? {
22557
+ id: original.history.id,
22558
+ direction: direction
22559
+ } : undefined
22560
+ };
22561
+ }
22562
+ var edit = original.edit;
22563
+ var after = edit.before.slice(0, edit.from) + edit.insert + edit.before.slice(edit.to);
22564
+ var inverse = _objectSpread2({
22565
+ before: after,
22566
+ from: edit.from,
22567
+ to: edit.from + edit.insert.length,
22568
+ insert: edit.before.slice(edit.from, edit.to)
22569
+ }, edit.boundary ? {
22570
+ boundary: edit.boundary
22571
+ } : {});
22572
+ return {
22573
+ intent: direction,
22574
+ history: {
22575
+ id: original.history.id,
22576
+ direction: direction
22577
+ },
22578
+ edit: direction === "undo" ? inverse : edit
22579
+ };
22580
+ });
22245
22581
  _defineProperty(_this2, "_setRangeParams", function (params) {
22246
22582
  try {
22247
22583
  debugMathRichInput("MathRichInput:_setRangeParams:start", {
@@ -22282,6 +22618,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22282
22618
  restore(50);
22283
22619
  });
22284
22620
  _defineProperty(_this2, "handleKeyDownBackspace", function (e) {
22621
+ var beforeText = editableText(_this2.editableDiv);
22285
22622
  var rangeParams = _this2._getRangeParams();
22286
22623
  var start_node_index = rangeParams.startNodeIndex;
22287
22624
  var end_node_index = rangeParams.endNodeIndex;
@@ -22318,7 +22655,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22318
22655
  startGlobalOffset: 0,
22319
22656
  endGlobalOffset: 0
22320
22657
  };
22321
- _this2.applyChangesToComponent("", _this2.getMimeType(false, false), _new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
22658
+ _this2.applyChangesToComponent("", _this2.getMimeType(false, false), _new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
22659
+ transaction: {
22660
+ edit: {
22661
+ before: beforeText,
22662
+ from: 0,
22663
+ to: beforeText.length,
22664
+ insert: ""
22665
+ },
22666
+ intent: "cut",
22667
+ history: {
22668
+ id: "mri-".concat(++_this2.historySequence),
22669
+ direction: "forward"
22670
+ }
22671
+ }
22672
+ });
22322
22673
  e.preventDefault();
22323
22674
  return;
22324
22675
  }
@@ -23175,6 +23526,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23175
23526
  try {
23176
23527
  // console.log("handleInput")
23177
23528
  if (_this2.isComposing) return;
23529
+ var transaction = _this2.pendingTransaction;
23530
+ _this2.pendingTransaction = null;
23178
23531
  debugMathRichInput("handleInput:start", {
23179
23532
  inputType: event && event.nativeEvent ? event.nativeEvent.inputType : null,
23180
23533
  data: event && event.nativeEvent ? event.nativeEvent.data : null,
@@ -23211,7 +23564,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23211
23564
  _raw_text = removeEncodingIfPlainText(_raw_text, _mimeType, _this2.enableHtml());
23212
23565
  _raw_text = _this2.stripEmptyInlineFormattingTags(_raw_text);
23213
23566
  _this2.applyChangesToComponent(_raw_text, _mimeType, defaultParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23214
- skipControlledRender: true
23567
+ skipControlledRender: true,
23568
+ transaction: transaction
23215
23569
  });
23216
23570
  return;
23217
23571
  } else if (params.startNodeIndex === null || params.startNodeIndex === undefined) {
@@ -23246,7 +23600,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23246
23600
  };
23247
23601
  _this2.setOldRangeParams(validParams);
23248
23602
  _this2.applyChangesToComponent(raw_text, mimeType, validParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23249
- skipControlledRender: true
23603
+ skipControlledRender: true,
23604
+ transaction: transaction
23250
23605
  });
23251
23606
  debugMathRichInput("handleInput:after-apply", {
23252
23607
  raw_text: raw_text,
@@ -23343,6 +23698,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23343
23698
  });
23344
23699
  _defineProperty(_this2, "handlePaste", function (event) {
23345
23700
  try {
23701
+ _this2.pendingTransaction = null;
23346
23702
  var clipboardData = event.clipboardData || window.clipboardData;
23347
23703
  if (!clipboardData) {
23348
23704
  return;
@@ -23367,6 +23723,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23367
23723
  console.warn("Could not get range params for paste");
23368
23724
  return;
23369
23725
  }
23726
+ var beforePaste = editableText(_this2.editableDiv);
23727
+ var pasteSelection = _this2.captureSelectionOffsets();
23370
23728
 
23371
23729
  // Store old range params for cursor positioning (like equation editor)
23372
23730
  _this2.setOldRangeParams(rangeParams);
@@ -23592,7 +23950,34 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23592
23950
  var cleanedRawText = removeEncodingIfPlainText(newRawText, actualMimeType, _this2.enableHtml());
23593
23951
 
23594
23952
  // Apply the change using the component's standard flow
23595
- _this2.applyChangesToComponent(cleanedRawText, actualMimeType, newRangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23953
+ var insertionText = function () {
23954
+ if (!/<[^>]+>/.test(finalText)) return finalText;
23955
+ var probe = document.createElement("div");
23956
+ probe.innerHTML = finalText;
23957
+ return editableText(probe).replace(/\ufffc/g, "");
23958
+ }();
23959
+ var transaction = pasteSelection ? {
23960
+ edit: {
23961
+ before: beforePaste,
23962
+ from: pasteSelection.from,
23963
+ to: pasteSelection.to,
23964
+ insert: insertionText
23965
+ },
23966
+ intent: "paste",
23967
+ history: {
23968
+ id: "mri-".concat(++_this2.historySequence),
23969
+ direction: "forward"
23970
+ }
23971
+ } : {
23972
+ intent: "paste",
23973
+ history: {
23974
+ id: "mri-".concat(++_this2.historySequence),
23975
+ direction: "forward"
23976
+ }
23977
+ };
23978
+ _this2.applyChangesToComponent(cleanedRawText, actualMimeType, newRangeParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23979
+ transaction: transaction
23980
+ });
23596
23981
 
23597
23982
  // A paste cannot rely on being re-rendered. It removed the selected content from the DOM itself,
23598
23983
  // and if the host already holds the value being applied — the same words pasted back into the
@@ -23953,10 +24338,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23953
24338
  }
23954
24339
  }
23955
24340
  var rangeParams = _this2._getRangeParams();
23956
- var removedEmptyTags = !insertedTypingStylePlaceholder && _this2.cleanupEmptyInlineFormattingElements();
23957
- if (removedEmptyTags && rangeParams !== null && rangeParams !== undefined) {
24341
+ if (!insertedTypingStylePlaceholder) _this2.cleanupEmptyInlineFormattingElements();
24342
+ if (rangeParams !== null && rangeParams !== undefined) {
23958
24343
  _this2._setRangeParams(rangeParams);
23959
- _this2.applyCurrentEditableDomToComponent(rangeParams);
24344
+ _this2.applyCurrentEditableDomToComponent(rangeParams, {
24345
+ edit: {
24346
+ before: editableText(_this2.editableDiv),
24347
+ from: 0,
24348
+ to: 0,
24349
+ insert: ""
24350
+ },
24351
+ intent: "format",
24352
+ history: {
24353
+ id: "mri-".concat(++_this2.historySequence),
24354
+ direction: "forward"
24355
+ }
24356
+ });
23960
24357
  }
23961
24358
  _this2.setActiveButtonsIfChanged(_this2.getActiveButtonsFromSelection(rangeParams));
23962
24359
  return success;
@@ -24001,6 +24398,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24001
24398
  _defineProperty(_this2, "insertRawText", function (new_text) {
24002
24399
  var selectInsertedText = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
24003
24400
  try {
24401
+ var beforeText = editableText(_this2.editableDiv);
24004
24402
  var rangeParams = _this2._getRangeParams();
24005
24403
  var node = _this2._findNodeWithIndex(rangeParams.endNodeIndex);
24006
24404
  var offset = rangeParams.endOffset;
@@ -24032,7 +24430,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24032
24430
  // console.log(new_rangeParams)
24033
24431
 
24034
24432
  new_raw_text = removeEncodingIfPlainText(new_raw_text, _this2.props.mimeType, _this2.enableHtml());
24035
- _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
24433
+ _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
24434
+ transaction: {
24435
+ edit: {
24436
+ before: beforeText,
24437
+ from: rangeParams.startGlobalOffset,
24438
+ to: rangeParams.endGlobalOffset,
24439
+ insert: new_text
24440
+ },
24441
+ intent: "input",
24442
+ history: {
24443
+ id: "mri-".concat(++_this2.historySequence),
24444
+ direction: "forward"
24445
+ }
24446
+ }
24447
+ });
24036
24448
  } catch (error) {
24037
24449
  console.error(error);
24038
24450
  }
@@ -24160,6 +24572,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24160
24572
  });
24161
24573
  _defineProperty(_this2, "removeNewLine", function () {
24162
24574
  try {
24575
+ var beforeText = editableText(_this2.editableDiv);
24163
24576
  var rangeParams = _this2._getRangeParams();
24164
24577
  var node = _this2._findNodeWithIndex(rangeParams.endNodeIndex);
24165
24578
  var prevNode = _this2._findNodeWithIndex(rangeParams.endNodeIndex - 1);
@@ -24176,13 +24589,28 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24176
24589
  new_rangeParams.srartGlobalOffset--;
24177
24590
  new_rangeParams.endGlobalOffset--;
24178
24591
  new_raw_text = removeEncodingIfPlainText(new_raw_text, _this2.props.mimeType, _this2.enableHtml());
24179
- _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
24592
+ _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
24593
+ transaction: {
24594
+ edit: {
24595
+ before: beforeText,
24596
+ from: Math.max(0, rangeParams.endGlobalOffset - 1),
24597
+ to: rangeParams.endGlobalOffset,
24598
+ insert: ""
24599
+ },
24600
+ intent: "input",
24601
+ history: {
24602
+ id: "mri-".concat(++_this2.historySequence),
24603
+ direction: "forward"
24604
+ }
24605
+ }
24606
+ });
24180
24607
  } catch (error) {
24181
24608
  console.error(error);
24182
24609
  }
24183
24610
  });
24184
24611
  _defineProperty(_this2, "insertNewLine", function () {
24185
24612
  try {
24613
+ var beforeText = editableText(_this2.editableDiv);
24186
24614
  var rangeParams = _this2._getRangeParams();
24187
24615
  var node = _this2._findNodeWithIndex(rangeParams.endNodeIndex);
24188
24616
  var offset = rangeParams.endOffset;
@@ -24218,13 +24646,28 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24218
24646
  // console.log("New range params after line insert")
24219
24647
  // console.log(new_rangeParams)
24220
24648
 
24221
- _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
24649
+ _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
24650
+ transaction: {
24651
+ edit: {
24652
+ before: beforeText,
24653
+ from: rangeParams.startGlobalOffset,
24654
+ to: rangeParams.endGlobalOffset,
24655
+ insert: "\n"
24656
+ },
24657
+ intent: "input",
24658
+ history: {
24659
+ id: "mri-".concat(++_this2.historySequence),
24660
+ direction: "forward"
24661
+ }
24662
+ }
24663
+ });
24222
24664
  } catch (error) {
24223
24665
  console.error(error);
24224
24666
  }
24225
24667
  });
24226
24668
  _defineProperty(_this2, "insertAccentLetter", function (event, old_char, new_char) {
24227
24669
  try {
24670
+ var beforeText = editableText(_this2.editableDiv);
24228
24671
  var rangeParams = _this2._getRangeParams();
24229
24672
  var node = _this2._findNodeWithIndex(rangeParams.startNodeIndex);
24230
24673
  var offset = rangeParams.startOffset;
@@ -24248,7 +24691,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24248
24691
  new_rangeParams = _objectSpread2({}, rangeParams);
24249
24692
  }
24250
24693
  new_raw_text = removeEncodingIfPlainText(new_raw_text, _this2.props.mimeType, _this2.enableHtml());
24251
- _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
24694
+ _this2.applyChangesToComponent(new_raw_text, _this2.props.mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
24695
+ transaction: {
24696
+ edit: {
24697
+ before: beforeText,
24698
+ from: old_char === null ? rangeParams.startGlobalOffset : Math.max(0, rangeParams.startGlobalOffset - 1),
24699
+ to: old_char === null ? rangeParams.startGlobalOffset : rangeParams.startGlobalOffset,
24700
+ insert: new_char
24701
+ },
24702
+ intent: "input",
24703
+ history: {
24704
+ id: "mri-".concat(++_this2.historySequence),
24705
+ direction: "forward"
24706
+ }
24707
+ }
24708
+ });
24252
24709
  } catch (error) {
24253
24710
  console.error(error);
24254
24711
  }
@@ -24356,10 +24813,66 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24356
24813
  }
24357
24814
  });
24358
24815
  _defineProperty(_this2, "handleCompositionStart", function (event) {
24816
+ var _offsets$from, _offsets$to;
24817
+ var offsets = _this2.captureSelectionOffsets();
24818
+ _this2.compositionStart = {
24819
+ before: editableText(_this2.editableDiv),
24820
+ from: (_offsets$from = offsets === null || offsets === void 0 ? void 0 : offsets.from) !== null && _offsets$from !== void 0 ? _offsets$from : 0,
24821
+ to: (_offsets$to = offsets === null || offsets === void 0 ? void 0 : offsets.to) !== null && _offsets$to !== void 0 ? _offsets$to : 0
24822
+ };
24359
24823
  _this2.isComposing = true;
24360
24824
  });
24361
24825
  _defineProperty(_this2, "handleCompositionEnd", function (event) {
24362
24826
  _this2.isComposing = false;
24827
+ _this2.compositionStart = null;
24828
+ });
24829
+ _defineProperty(_this2, "captureSelectionOffsets", function () {
24830
+ try {
24831
+ var selection = window.getSelection();
24832
+ if (!selection || selection.rangeCount === 0) return null;
24833
+ var range = selection.getRangeAt(0);
24834
+ var before = editableText(_this2.editableDiv);
24835
+ var endpoint = function endpoint(container, offset) {
24836
+ var probe = document.createRange();
24837
+ probe.selectNodeContents(_this2.editableDiv);
24838
+ probe.setEnd(container, offset);
24839
+ var wrapper = document.createElement("span");
24840
+ wrapper.appendChild(probe.cloneContents());
24841
+ return editableText(wrapper).length;
24842
+ };
24843
+ var from = endpoint(range.startContainer, range.startOffset);
24844
+ var to = endpoint(range.endContainer, range.endOffset);
24845
+ return {
24846
+ before: before,
24847
+ from: Math.min(from, to),
24848
+ to: Math.max(from, to)
24849
+ };
24850
+ } catch (_error) {
24851
+ return null;
24852
+ }
24853
+ });
24854
+ /** Capture an input intent before the browser mutates the contenteditable DOM. */
24855
+ _defineProperty(_this2, "handleBeforeInput", function (event) {
24856
+ try {
24857
+ var captured = transactionFromBeforeInput(_this2.editableDiv, event, _this2.compositionStart ? {
24858
+ before: _this2.compositionStart.before,
24859
+ from: _this2.compositionStart.from,
24860
+ to: _this2.compositionStart.to
24861
+ } : null);
24862
+ var id = "mri-".concat(++_this2.historySequence);
24863
+ _this2.pendingTransaction = {
24864
+ edit: captured.edit || undefined,
24865
+ intent: captured.intent,
24866
+ history: {
24867
+ id: id,
24868
+ direction: "forward"
24869
+ }
24870
+ };
24871
+ } catch (_error) {
24872
+ _this2.pendingTransaction = {
24873
+ intent: "programmatic"
24874
+ };
24875
+ }
24363
24876
  });
24364
24877
  // Comprehensive LaTeX pattern detection and auto-wrapping in math tags
24365
24878
  // Comprehensive LaTeX pattern detection for paste auto-wrapping
@@ -24505,6 +25018,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24505
25018
  _this2.initialHistoryState = null;
24506
25019
  _this2.undoHistory = [];
24507
25020
  _this2.redoHistory = [];
25021
+ _this2.historySequence = 0;
25022
+ _this2.pendingTransaction = null;
25023
+ _this2.compositionStart = null;
24508
25024
  _this2.accent = "";
24509
25025
 
24510
25026
  // keeps track of whether user is composing in an IME - see https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
@@ -24685,7 +25201,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24685
25201
  value: function getEditableHtmlForRender(html) {
24686
25202
  var currentRawText = this.editableDiv ? this.getCurrentRawTextForProps(this.props) : null;
24687
25203
  var liveEditableHtml = this.editableDiv ? this.editableDiv.innerHTML : null;
24688
- var canPreserveNativeDom = this.state.hasFocus === true && this.editableDiv !== null && this.editableDiv !== undefined && (this.afterComponentUpdateData === null || this.afterComponentUpdateData === undefined) && this.lastRenderedEditableHtml !== null && currentRawText === this.props.value;
25204
+ var pendingNativeInputMatchesLiveDom = this.skipNextControlledValueRender === true && currentRawText === this.skipNextControlledValue;
25205
+ var canPreserveNativeDom = this.state.hasFocus === true && this.editableDiv !== null && this.editableDiv !== undefined && (this.afterComponentUpdateData === null || this.afterComponentUpdateData === undefined) && this.lastRenderedEditableHtml !== null && (currentRawText === this.props.value || pendingNativeInputMatchesLiveDom);
24689
25206
  var preserveRangeParams = canPreserveNativeDom ? this.getRangeParamsPreservingInlinePlaceholder() : null;
24690
25207
  debugMathRichInput("render:editable-html", {
24691
25208
  canPreserveNativeDom: canPreserveNativeDom,
@@ -24696,6 +25213,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24696
25213
  liveEditableHtml: liveEditableHtml,
24697
25214
  lastRenderedEditableHtml: this.lastRenderedEditableHtml,
24698
25215
  currentRawText: currentRawText,
25216
+ pendingNativeInputMatchesLiveDom: pendingNativeInputMatchesLiveDom,
24699
25217
  preserveRangeParams: preserveRangeParams,
24700
25218
  afterComponentUpdateData: this.afterComponentUpdateData,
24701
25219
  hasFocus: this.state.hasFocus,
@@ -24939,7 +25457,19 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24939
25457
  var rangeParams = this._getRangeParams();
24940
25458
  if (rangeParams) {
24941
25459
  this.setOldRangeParams(rangeParams);
24942
- this.applyCurrentEditableDomToComponent(rangeParams);
25460
+ this.applyCurrentEditableDomToComponent(rangeParams, {
25461
+ edit: {
25462
+ before: editableText(this.editableDiv),
25463
+ from: 0,
25464
+ to: 0,
25465
+ insert: ""
25466
+ },
25467
+ intent: "format",
25468
+ history: {
25469
+ id: "mri-".concat(++this.historySequence),
25470
+ direction: "forward"
25471
+ }
25472
+ });
24943
25473
  }
24944
25474
  debugMathRichInput("moveTrailingWhitespaceOutsideInlineStyle:after", {
24945
25475
  style: style,
@@ -24955,6 +25485,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24955
25485
  key: "applyCurrentEditableDomToComponent",
24956
25486
  value: function applyCurrentEditableDomToComponent() {
24957
25487
  var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
25488
+ var transaction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
24958
25489
  if (!this.editableDiv) return;
24959
25490
  if (rangeParams === null || rangeParams === undefined) {
24960
25491
  rangeParams = this._getRangeParams();
@@ -24969,7 +25500,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24969
25500
  rawText = removeEncodingIfPlainText(rawText, mimeType, this.enableHtml());
24970
25501
  rawText = this.stripEmptyInlineFormattingTags(rawText);
24971
25502
  this.applyChangesToComponent(rawText, mimeType, rangeParams, this.props.useExpertMode, this.props.selectedTab, {
24972
- skipControlledRender: true
25503
+ skipControlledRender: true,
25504
+ transaction: transaction
24973
25505
  });
24974
25506
  }
24975
25507
  }, {
@@ -25020,6 +25552,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
25020
25552
  });
25021
25553
  this.editableDiv.addEventListener("keydown", this.handleKeyDown);
25022
25554
  this.editableDiv.addEventListener("keyup", this.handleKeyUp);
25555
+ this.editableDiv.addEventListener("beforeinput", this.handleBeforeInput);
25023
25556
  this.editableDiv.addEventListener("paste", this.handlePaste);
25024
25557
  this.editableDiv.addEventListener("copy", this.handleCopy);
25025
25558
  if (this.props.autofocus === true) this.editableDiv.focus();
@@ -25033,6 +25566,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
25033
25566
  try {
25034
25567
  this.editableDiv.removeEventListener("keydown", this.handleKeyDown);
25035
25568
  this.editableDiv.removeEventListener("keyup", this.handleKeyUp);
25569
+ this.editableDiv.removeEventListener("beforeinput", this.handleBeforeInput);
25036
25570
  this.editableDiv.removeEventListener("paste", this.handlePaste);
25037
25571
  this.editableDiv.removeEventListener("copy", this.handleCopy);
25038
25572
  } catch (error) {
@@ -25043,18 +25577,29 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
25043
25577
  key: "shouldComponentUpdate",
25044
25578
  value: function shouldComponentUpdate(nextProps, nextState) {
25045
25579
  if (this.skipNextControlledValueRender === true) {
25046
- var canSkip = nextProps.value === this.skipNextControlledValue && nextState === this.state;
25580
+ var controlledValueArrived = nextProps.value === this.skipNextControlledValue;
25581
+ var canSkip = controlledValueArrived && nextState === this.state;
25047
25582
  debugMathRichInput("shouldComponentUpdate:native-input-gate", {
25048
25583
  canSkip: canSkip,
25049
25584
  nextValue: nextProps.value,
25050
25585
  skipNextControlledValue: this.skipNextControlledValue,
25586
+ controlledValueArrived: controlledValueArrived,
25051
25587
  currentPropsValue: this.props.value,
25052
25588
  stateChanged: nextState !== this.state,
25053
25589
  innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
25054
25590
  selection: getSelectionSnapshot(this.editableDiv)
25055
25591
  });
25056
- this.skipNextControlledValueRender = false;
25057
- this.skipNextControlledValue = null;
25592
+
25593
+ // A controlled host may commit an unrelated render before it echoes the
25594
+ // value from this native input event. Keep the gate armed through that
25595
+ // stale render so getEditableHtmlForRender can preserve the browser DOM
25596
+ // and its selection. The time-bounded fallback armed by
25597
+ // applyChangesToComponent keeps a host that rejects the value from
25598
+ // leaving the gate armed indefinitely.
25599
+ if (controlledValueArrived) {
25600
+ this.skipNextControlledValueRender = false;
25601
+ this.skipNextControlledValue = null;
25602
+ }
25058
25603
  if (canSkip) return false;
25059
25604
  }
25060
25605
  if (this.shouldSkipRedundantFocusedRender(nextProps, nextState)) {
@@ -25201,6 +25746,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
25201
25746
  try {
25202
25747
  if (rangeParams === null || rangeParams === undefined) rangeParams = this._getRangeParams();
25203
25748
  if (rangeParams.startOffset !== 0) console.warn("Unexpected startOffset " + rangeParams.startOffset + " in insertAfterSmallSpace");
25749
+ var beforeText = editableText(this.editableDiv);
25204
25750
  var node = this._findNodeWithIndex(rangeParams.startNodeIndex);
25205
25751
  if (node === null || node === undefined || node.nodeValue === null) {
25206
25752
  console.warn("Could not find small-space text node");
@@ -25225,7 +25771,20 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
25225
25771
  var new_raw_text = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
25226
25772
  new_raw_text = removeEncodingIfPlainText(new_raw_text, this.props.mimeType, this.enableHtml());
25227
25773
  this.applyChangesToComponent(new_raw_text, this.props.mimeType, new_rangeParams, this.props.useExpertMode, this.props.selectedTab, {
25228
- skipControlledRender: true
25774
+ skipControlledRender: true,
25775
+ transaction: {
25776
+ edit: {
25777
+ before: beforeText,
25778
+ from: rangeParams.startGlobalOffset,
25779
+ to: rangeParams.startGlobalOffset,
25780
+ insert: new_char
25781
+ },
25782
+ intent: "input",
25783
+ history: {
25784
+ id: "mri-".concat(++this.historySequence),
25785
+ direction: "forward"
25786
+ }
25787
+ }
25229
25788
  });
25230
25789
  } catch (error) {
25231
25790
  console.error(error);