@zzish/math-rich-input 0.1.51 → 0.1.52

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
@@ -20283,9 +20283,6 @@ var Toolbar = /*#__PURE__*/function (_React$Component) {
20283
20283
  var _this;
20284
20284
  _classCallCheck(this, Toolbar);
20285
20285
  _this = _callSuper(this, Toolbar, [props]);
20286
- _defineProperty(_this, "handleButtonClick", function (event, buttonName) {
20287
- _this.suppliedClickHandler(event, buttonName);
20288
- });
20289
20286
  _defineProperty(_this, "showAccentBar", function (event) {
20290
20287
  if (!_this.state.showAccents) _this.accentLetter = _this.suppliedAccentLetterFetcher();
20291
20288
  _this.setState({
@@ -20305,6 +20302,8 @@ var Toolbar = /*#__PURE__*/function (_React$Component) {
20305
20302
  });
20306
20303
  });
20307
20304
  _defineProperty(_this, "handleButtonClick", function (event, buttonName) {
20305
+ event.preventDefault();
20306
+ event.stopPropagation();
20308
20307
  _this.suppliedClickHandler(event, buttonName);
20309
20308
  });
20310
20309
  _defineProperty(_this, "handleAccentButtonClick", function (event, oldCharacter, newCharacter) {
@@ -20479,6 +20478,9 @@ var Toolbar = /*#__PURE__*/function (_React$Component) {
20479
20478
  }, "\xE1"), enableMath && /*#__PURE__*/React__default["default"].createElement("button", {
20480
20479
  name: "Equation",
20481
20480
  className: "Toolbar-button TB-wide",
20481
+ onClick: function onClick(e) {
20482
+ return e.preventDefault();
20483
+ },
20482
20484
  onMouseDown: function onMouseDown(e) {
20483
20485
  return _this2.handleButtonClick(e, "Equation");
20484
20486
  }
@@ -20668,6 +20670,73 @@ function determineMimeType(text) {
20668
20670
  {this.test("$a bcd> e$", "application/x-tex")}
20669
20671
  */
20670
20672
 
20673
+ var DEBUG_MATH_RICH_INPUT = false;
20674
+ function getNodePath(node) {
20675
+ var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
20676
+ if (!node) return null;
20677
+ var parts = [];
20678
+ var current = node;
20679
+ while (current && current !== root && parts.length < 8) {
20680
+ if (current.nodeType === 3) {
20681
+ parts.unshift("#text(\"".concat(current.nodeValue, "\")"));
20682
+ } else {
20683
+ var name = current.nodeName ? current.nodeName.toLowerCase() : "unknown";
20684
+ var className = current.className && typeof current.className === "string" ? ".".concat(current.className.split(" ").filter(Boolean).join(".")) : "";
20685
+ parts.unshift("".concat(name).concat(className));
20686
+ }
20687
+ current = current.parentNode;
20688
+ }
20689
+ if (root) parts.unshift("root");
20690
+ return parts.join(" > ");
20691
+ }
20692
+ function describeNode(node) {
20693
+ var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
20694
+ if (!node) return null;
20695
+ return {
20696
+ nodeName: node.nodeName,
20697
+ nodeType: node.nodeType,
20698
+ text: node.nodeType === 3 && typeof node.nodeValue === "string" ? node.nodeValue : undefined,
20699
+ textLength: node.nodeType === 3 && typeof node.nodeValue === "string" ? node.nodeValue.length : undefined,
20700
+ childCount: node.childNodes ? node.childNodes.length : undefined,
20701
+ path: getNodePath(node, root)
20702
+ };
20703
+ }
20704
+ function getSelectionSnapshot() {
20705
+ var editableDiv = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
20706
+ if (typeof window === "undefined" || !window.getSelection) return null;
20707
+ var selection = window.getSelection();
20708
+ if (!selection) return null;
20709
+ var snapshot = {
20710
+ activeElement: typeof document !== "undefined" && document.activeElement ? {
20711
+ nodeName: document.activeElement.nodeName,
20712
+ className: document.activeElement.className
20713
+ } : null,
20714
+ rangeCount: selection.rangeCount,
20715
+ isCollapsed: selection.isCollapsed,
20716
+ anchorOffset: selection.anchorOffset,
20717
+ focusOffset: selection.focusOffset,
20718
+ anchorNode: describeNode(selection.anchorNode, editableDiv),
20719
+ focusNode: describeNode(selection.focusNode, editableDiv)
20720
+ };
20721
+ if (selection.rangeCount > 0) {
20722
+ var range = selection.getRangeAt(0);
20723
+ snapshot.range = {
20724
+ collapsed: range.collapsed,
20725
+ startOffset: range.startOffset,
20726
+ endOffset: range.endOffset,
20727
+ startContainer: describeNode(range.startContainer, editableDiv),
20728
+ endContainer: describeNode(range.endContainer, editableDiv)
20729
+ };
20730
+ }
20731
+ return snapshot;
20732
+ }
20733
+ function debugMathRichInput(label) {
20734
+ return;
20735
+ }
20736
+ if (typeof window !== "undefined") {
20737
+ window.__MRI_DEBUG__ = DEBUG_MATH_RICH_INPUT;
20738
+ }
20739
+
20671
20740
  var RAW_TEXT_MATH_START_TAG_TEX = "\\[";
20672
20741
  var RAW_TEXT_MATH_END_TAG_TEX = "\\]";
20673
20742
  var RAW_TEXT_MATH_START_TAG_HTML_MATH = "<math>";
@@ -21309,6 +21378,55 @@ function findFirstTextNode(node) {
21309
21378
  }
21310
21379
  return null;
21311
21380
  }
21381
+ function findLastTextNode(node) {
21382
+ if (isTextNode(node)) return node;
21383
+ var nodes = node.childNodes;
21384
+ for (var i = nodes.length - 1; i >= 0; i--) {
21385
+ var found_node = findLastTextNode(nodes[i]);
21386
+ if (found_node !== null) return found_node;
21387
+ }
21388
+ return null;
21389
+ }
21390
+ function getTextPositionFromElementRangeEndpoint(container, offset) {
21391
+ if (isTextNode(container)) return {
21392
+ node: container,
21393
+ offset: offset
21394
+ };
21395
+ var nodes = container.childNodes;
21396
+ if (nodes === null || nodes === undefined || nodes.length === 0) return null;
21397
+
21398
+ // A collapsed selection inside contentEditable may report the editable SPAN
21399
+ // itself as the container and the child index as the offset. The previous
21400
+ // implementation always picked the first descendant text node, which moved
21401
+ // the caret to the beginning after React re-rendered the controlled input.
21402
+ if (offset > 0) {
21403
+ var previousChild = nodes[Math.min(offset - 1, nodes.length - 1)];
21404
+ var previousTextNode = findLastTextNode(previousChild);
21405
+ if (previousTextNode !== null) {
21406
+ return {
21407
+ node: previousTextNode,
21408
+ offset: previousTextNode.nodeValue.length
21409
+ };
21410
+ }
21411
+ }
21412
+ if (offset < nodes.length) {
21413
+ var nextTextNode = findFirstTextNode(nodes[offset]);
21414
+ if (nextTextNode !== null) {
21415
+ return {
21416
+ node: nextTextNode,
21417
+ offset: 0
21418
+ };
21419
+ }
21420
+ }
21421
+ var fallbackTextNode = findFirstTextNode(container);
21422
+ if (fallbackTextNode !== null) {
21423
+ return {
21424
+ node: fallbackTextNode,
21425
+ offset: 0
21426
+ };
21427
+ }
21428
+ return null;
21429
+ }
21312
21430
  function findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, globalOffset) {
21313
21431
  var result = {
21314
21432
  offset: 0
@@ -21350,6 +21468,10 @@ function findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, globalOffset
21350
21468
  function getRangeParams(editableDiv) {
21351
21469
  // console.log ("getRangeParams")
21352
21470
  // console.log("Inner html: "+editableDiv.innerHTML)
21471
+ debugMathRichInput("getRangeParams:start", {
21472
+ innerHTML: editableDiv ? editableDiv.innerHTML : null,
21473
+ selection: getSelectionSnapshot(editableDiv)
21474
+ });
21353
21475
  var isSupported = typeof window.getSelection !== "undefined";
21354
21476
  if (!isSupported) {
21355
21477
  console.error("Can't get range params, browser does not support window.getSelection");
@@ -21361,15 +21483,27 @@ function getRangeParams(editableDiv) {
21361
21483
  return null;
21362
21484
  }
21363
21485
  var range = selection.getRangeAt(0);
21486
+ debugMathRichInput("getRangeParams:range-before-normalize", {
21487
+ startContainer: describeNode(range.startContainer, editableDiv),
21488
+ startOffset: range.startOffset,
21489
+ endContainer: describeNode(range.endContainer, editableDiv),
21490
+ endOffset: range.endOffset
21491
+ });
21364
21492
 
21365
21493
  // logAllDescendants(editableDiv)
21366
- // If the range is not within a text node, move it to a text node
21367
- // This typically happens when the end of a range is at the start of a new line
21494
+ // If the range is not within a text node, move it to the text node implied
21495
+ // by the element-container offset. This typically happens when the browser
21496
+ // reports a collapsed contentEditable selection as (SPAN, childIndex).
21368
21497
  if (range.startContainer.nodeType !== 3) {
21369
- var textNode = findFirstTextNode(range.startContainer);
21370
- if (textNode !== null) {
21371
- console.warn("Adjusting start pos in getRangeParams as container was of type: " + range.startContainer.nodeName);
21372
- range.setStart(textNode, 0);
21498
+ var textPosition = getTextPositionFromElementRangeEndpoint(range.startContainer, range.startOffset);
21499
+ if (textPosition !== null) {
21500
+ debugMathRichInput("getRangeParams:normalize-start-element", {
21501
+ from: describeNode(range.startContainer, editableDiv),
21502
+ fromOffset: range.startOffset,
21503
+ to: describeNode(textPosition.node, editableDiv),
21504
+ toOffset: textPosition.offset
21505
+ });
21506
+ range.setStart(textPosition.node, textPosition.offset);
21373
21507
  } else {
21374
21508
  // Handle empty content case - return default range params
21375
21509
  // This is normal behavior after selecting all and deleting content
@@ -21385,10 +21519,15 @@ function getRangeParams(editableDiv) {
21385
21519
  }
21386
21520
  }
21387
21521
  if (range.endContainer.nodeType !== 3) {
21388
- var _textNode = findFirstTextNode(range.endContainer);
21389
- if (_textNode !== null) {
21390
- console.warn("Adjusting end pos in getRangeParams as container was of type: " + range.endContainer.nodeName);
21391
- range.setEnd(_textNode, 0);
21522
+ var _textPosition = getTextPositionFromElementRangeEndpoint(range.endContainer, range.endOffset);
21523
+ if (_textPosition !== null) {
21524
+ debugMathRichInput("getRangeParams:normalize-end-element", {
21525
+ from: describeNode(range.endContainer, editableDiv),
21526
+ fromOffset: range.endOffset,
21527
+ to: describeNode(_textPosition.node, editableDiv),
21528
+ toOffset: _textPosition.offset
21529
+ });
21530
+ range.setEnd(_textPosition.node, _textPosition.offset);
21392
21531
  } else {
21393
21532
  // Handle empty content case - return default range params
21394
21533
  // This is normal behavior after selecting all and deleting content
@@ -21434,11 +21573,20 @@ function getRangeParams(editableDiv) {
21434
21573
  startGlobalOffset: startResult.offset,
21435
21574
  endGlobalOffset: endResult.offset
21436
21575
  };
21576
+ debugMathRichInput("getRangeParams:result", {
21577
+ rangeParams: rangeParams,
21578
+ selection: getSelectionSnapshot(editableDiv)
21579
+ });
21437
21580
  return rangeParams;
21438
21581
  }
21439
21582
  function setRangeParamsFromGlobalOffsets(editableDiv, params) {
21440
21583
  var new_range = null;
21441
21584
  try {
21585
+ debugMathRichInput("setRangeParamsFromGlobalOffsets:start", {
21586
+ params: params,
21587
+ innerHTML: editableDiv ? editableDiv.innerHTML : null,
21588
+ selectionBefore: getSelectionSnapshot(editableDiv)
21589
+ });
21442
21590
  if (params === null || params === undefined) throw new Error("Can't set range params: params === " + params);
21443
21591
  var startResult = findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, params.startGlobalOffset);
21444
21592
  var endResult = findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, params.endGlobalOffset);
@@ -21458,6 +21606,18 @@ function setRangeParamsFromGlobalOffsets(editableDiv, params) {
21458
21606
  var selection = window.getSelection();
21459
21607
  selection.removeAllRanges();
21460
21608
  selection.addRange(new_range);
21609
+ debugMathRichInput("setRangeParamsFromGlobalOffsets:after", {
21610
+ params: params,
21611
+ startResult: {
21612
+ node: describeNode(startResult.node, editableDiv),
21613
+ offset: startResult.offset
21614
+ },
21615
+ endResult: {
21616
+ node: describeNode(endResult.node, editableDiv),
21617
+ offset: endResult.offset
21618
+ },
21619
+ selectionAfter: getSelectionSnapshot(editableDiv)
21620
+ });
21461
21621
  } catch (error) {
21462
21622
  console.error(error);
21463
21623
  console.log("Trying to set range:");
@@ -21471,6 +21631,11 @@ function setRangeParams(editableDiv, params) {
21471
21631
 
21472
21632
  var new_range = null;
21473
21633
  try {
21634
+ debugMathRichInput("setRangeParams:start", {
21635
+ params: params,
21636
+ innerHTML: editableDiv ? editableDiv.innerHTML : null,
21637
+ selectionBefore: getSelectionSnapshot(editableDiv)
21638
+ });
21474
21639
  if (params === null || params === undefined) {
21475
21640
  console.error("Can't set range params: params === " + params);
21476
21641
  return;
@@ -21530,6 +21695,7 @@ function setRangeParams(editableDiv, params) {
21530
21695
  startNode = findFirstTextNode(editableDiv);
21531
21696
  startOffset = 0;
21532
21697
  }
21698
+ if (startNode === null) return;
21533
21699
  if (!isTextNode(startNode)) {
21534
21700
  console.warn("Start node found for range start is not a text a node");
21535
21701
  console.log(editableDiv.childNodes);
@@ -21545,6 +21711,7 @@ function setRangeParams(editableDiv, params) {
21545
21711
  endNode = findFirstTextNode(editableDiv);
21546
21712
  endOffset = 0;
21547
21713
  }
21714
+ if (endNode === null) return;
21548
21715
  if (!isTextNode(endNode)) {
21549
21716
  console.warn("End node found for range start is not a text a node");
21550
21717
  console.log(editableDiv.childNodes);
@@ -21557,7 +21724,7 @@ function setRangeParams(editableDiv, params) {
21557
21724
  }
21558
21725
  new_range = document.createRange();
21559
21726
  if (startOffset > startNode.nodeValue.length) {
21560
- console.warn("endOffset > endNode.nodeValue.length when setting range: " + startOffset + " > " + startNode.nodeValue.length + " - " + stateNode.nodeType + " - " + startNode.nodeValue);
21727
+ console.warn("endOffset > endNode.nodeValue.length when setting range: " + startOffset + " > " + startNode.nodeValue.length + " - " + startNode.nodeType + " - " + startNode.nodeValue);
21561
21728
  startOffset = startNode.nodeValue.length;
21562
21729
  }
21563
21730
  new_range.setStart(startNode, startOffset);
@@ -21572,6 +21739,14 @@ function setRangeParams(editableDiv, params) {
21572
21739
  var selection = window.getSelection();
21573
21740
  selection.removeAllRanges();
21574
21741
  selection.addRange(new_range);
21742
+ debugMathRichInput("setRangeParams:after", {
21743
+ params: params,
21744
+ startNode: describeNode(startNode, editableDiv),
21745
+ startOffset: startOffset,
21746
+ endNode: describeNode(endNode, editableDiv),
21747
+ endOffset: endOffset,
21748
+ selectionAfter: getSelectionSnapshot(editableDiv)
21749
+ });
21575
21750
  } catch (error) {
21576
21751
  console.error(error);
21577
21752
  console.log("Trying to set range:");
@@ -21798,6 +21973,29 @@ var SMALL_SPACE_LENGTH = SMALL_SPACE.length;
21798
21973
  // const SMALL_SPACE_REG_EXP = new RegExp(SMALL_SPACE, "g")
21799
21974
 
21800
21975
  var MARK = getMark();
21976
+ var INLINE_STYLE_COMMANDS = {
21977
+ bold: "bold",
21978
+ italic: "italic",
21979
+ underline: "underline",
21980
+ subscript: "subscript",
21981
+ superscript: "superscript"
21982
+ };
21983
+ var INLINE_STYLE_TAGS = {
21984
+ bold: ["b", "strong"],
21985
+ italic: ["i", "em"],
21986
+ underline: ["u"],
21987
+ subscript: ["sub"],
21988
+ superscript: ["sup"]
21989
+ };
21990
+ var INLINE_STYLE_TAG_NAMES = ["b", "strong", "i", "em", "u", "sub", "sup"];
21991
+ var INLINE_STYLE_WRAPPER_TAGS = {
21992
+ bold: "strong",
21993
+ italic: "em",
21994
+ underline: "u",
21995
+ subscript: "sub",
21996
+ superscript: "sup"
21997
+ };
21998
+ var EMPTY_INLINE_FORMATTING_REG_EXP = /<(B|STRONG|I|EM|U|SUB|SUP)>(?:\s|&nbsp;|\u00a0|\u200b|\ufeff)*<\/\1>/gi;
21801
21999
 
21802
22000
  // const LATEX_MARKER_START = "<annotation encoding=\"application/x-tex\">"
21803
22001
  // const LATEX_MARKER_END = "</annotation>"
@@ -21939,12 +22137,34 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21939
22137
  return countNodeTypes(_this2.editableDiv);
21940
22138
  });
21941
22139
  _defineProperty(_this2, "applyChangesToComponent", function (value, mimeType, rangeParams, isExpertMode, selectedTab) {
22140
+ var applyOptions = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
21942
22141
  // rangeParams, isExpertMode and selectedTab can be null, in which case we will set them
21943
22142
  if (rangeParams === null) rangeParams = _this2.lastSetRangeParams;
21944
- _this2.afterComponentUpdateData = {
22143
+ debugMathRichInput("applyChangesToComponent:start", {
22144
+ value: value,
22145
+ mimeType: mimeType,
21945
22146
  rangeParams: rangeParams,
21946
- value: value
21947
- };
22147
+ propsValue: _this2.props.value,
22148
+ propsMimeType: _this2.props.mimeType,
22149
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
22150
+ selection: getSelectionSnapshot(_this2.editableDiv),
22151
+ skipControlledRender: applyOptions.skipControlledRender === true
22152
+ });
22153
+ if (applyOptions.skipControlledRender === true) {
22154
+ // Native contenteditable input has already mutated the DOM and kept the
22155
+ // browser selection in the right place. Re-rendering the same value via
22156
+ // dangerouslySetInnerHTML can replace text nodes under React 19 and move
22157
+ // the caret to the beginning. Programmatic edits still use the normal
22158
+ // render-and-restore path.
22159
+ _this2.skipNextControlledValueRender = true;
22160
+ _this2.skipNextControlledValue = value;
22161
+ _this2.afterComponentUpdateData = null;
22162
+ } else {
22163
+ _this2.afterComponentUpdateData = {
22164
+ rangeParams: rangeParams,
22165
+ value: value
22166
+ };
22167
+ }
21948
22168
  var options = _this2.props.options || DEFAULT_OPTIONS;
21949
22169
  if (isExpertMode === null) isExpertMode = options.isExpertMode;
21950
22170
  if (selectedTab === null) selectedTab = options.selectedTab;
@@ -21968,6 +22188,10 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21968
22188
  isExpertMode: isExpertMode,
21969
22189
  selectedTab: selectedTab
21970
22190
  });
22191
+ debugMathRichInput("applyChangesToComponent:after-onChange", {
22192
+ afterComponentUpdateData: _this2.afterComponentUpdateData,
22193
+ selection: getSelectionSnapshot(_this2.editableDiv)
22194
+ });
21971
22195
  });
21972
22196
  _defineProperty(_this2, "initialiseHistory", function () {
21973
22197
  if (_this2.initialHistoryState == null) _this2.initialHistoryState = {
@@ -22011,14 +22235,43 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22011
22235
  });
22012
22236
  _defineProperty(_this2, "_setRangeParams", function (params) {
22013
22237
  try {
22238
+ debugMathRichInput("MathRichInput:_setRangeParams:start", {
22239
+ params: params,
22240
+ selectionBefore: getSelectionSnapshot(_this2.editableDiv)
22241
+ });
22014
22242
  setRangeParams(_this2.editableDiv, params);
22015
22243
  _this2.setOldRangeParams(params);
22016
- _this2.lastSetRangeParmas = params;
22244
+ _this2.lastSetRangeParams = params;
22245
+ debugMathRichInput("MathRichInput:_setRangeParams:after", {
22246
+ params: params,
22247
+ oldRangeParams: _this2.oldRangeParams,
22248
+ selectionAfter: getSelectionSnapshot(_this2.editableDiv)
22249
+ });
22017
22250
  } catch (error) {
22018
22251
  console.error(error);
22019
22252
  // console.log(this.editableDiv.childNodes);
22020
22253
  }
22021
22254
  });
22255
+ _defineProperty(_this2, "restoreRangeAfterBrowserWork", function (params, reason) {
22256
+ if (params === null || params === undefined) return;
22257
+ var restore = function restore(delay) {
22258
+ window.setTimeout(function () {
22259
+ if (_this2.state.showEquationEditor) return;
22260
+ if (!_this2.state.hasFocus && document.activeElement !== _this2.editableDiv) {
22261
+ return;
22262
+ }
22263
+ debugMathRichInput("restoreRangeAfterBrowserWork", {
22264
+ reason: reason,
22265
+ delay: delay,
22266
+ params: params,
22267
+ selectionBefore: getSelectionSnapshot(_this2.editableDiv)
22268
+ });
22269
+ _this2._setRangeParams(params);
22270
+ }, delay);
22271
+ };
22272
+ restore(0);
22273
+ restore(50);
22274
+ });
22022
22275
  _defineProperty(_this2, "handleKeyDownBackspace", function (e) {
22023
22276
  var rangeParams = _this2._getRangeParams();
22024
22277
  var start_node_index = rangeParams.startNodeIndex;
@@ -22146,7 +22399,11 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22146
22399
  _defineProperty(_this2, "isWordModifier", function (e) {
22147
22400
  var _this2$getPlatformMod = _this2.getPlatformModifiers(),
22148
22401
  isMac = _this2$getPlatformMod.isMac;
22149
- return isMac ? e.altKey : e.ctrlKey;
22402
+ // Borough production is pinned to math-rich-input 0.1.48, where Option +
22403
+ // Arrow did not get custom word navigation. Keep Mac Option + Arrow as
22404
+ // normal character movement to preserve the editor's existing UX; Command +
22405
+ // Arrow is handled by isLineModifier below.
22406
+ return isMac ? false : e.ctrlKey;
22150
22407
  });
22151
22408
  // Check if line navigation modifier is pressed
22152
22409
  _defineProperty(_this2, "isLineModifier", function (e) {
@@ -22603,6 +22860,17 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22603
22860
  });
22604
22861
  _defineProperty(_this2, "handleKeyDown", function (e) {
22605
22862
  try {
22863
+ debugMathRichInput("handleKeyDown:start", {
22864
+ key: e.key,
22865
+ altKey: e.altKey,
22866
+ metaKey: e.metaKey,
22867
+ ctrlKey: e.ctrlKey,
22868
+ shiftKey: e.shiftKey,
22869
+ hasFocus: _this2.state.hasFocus,
22870
+ propsValue: _this2.props.value,
22871
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
22872
+ selection: getSelectionSnapshot(_this2.editableDiv)
22873
+ });
22606
22874
  // Lets set up or update the undo redo history
22607
22875
  _this2.initialiseHistory();
22608
22876
  if ((e.key === "z" || e.key === "Z") && (e.ctrlKey || e.metaKey)) {
@@ -22659,7 +22927,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22659
22927
  }
22660
22928
  }
22661
22929
  }
22662
- if (e.altKey && e.key === "Alt") {
22930
+ var _this2$getPlatformMod3 = _this2.getPlatformModifiers(),
22931
+ isMac = _this2$getPlatformMod3.isMac;
22932
+ if (!isMac && e.altKey && e.key === "Alt") {
22663
22933
  var key = _this2.fetchAccentLetter();
22664
22934
  if (key === "a" || key === "e" || key === "i" || key === "o" || key === "u" || key === "c" || key === "l" || key === "n" || key === "s" || key === "?" || key === "!") {
22665
22935
  // console.log("showing/hiding accent bar");
@@ -22676,9 +22946,11 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22676
22946
  return;
22677
22947
  }
22678
22948
  _this2.keyPressed = e.key; // store for use in methods
22679
- _this2.setState({
22680
- keyPressed: e.key
22681
- });
22949
+ if (_this2.state.showAccentBar) {
22950
+ _this2.setState({
22951
+ keyPressed: e.key
22952
+ });
22953
+ }
22682
22954
 
22683
22955
  // Hide the accent bar if a key is typed except when it is an automatic key repeat press
22684
22956
  // If it is automatic key repeat, then keyPressStartTime will be greater than 0
@@ -22698,10 +22970,16 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22698
22970
  }
22699
22971
  if (e.key === "ArrowLeft") {
22700
22972
  _this2.handleKeyDownArrowLeft(e);
22973
+ debugMathRichInput("handleKeyDown:after-arrow-left", {
22974
+ selection: getSelectionSnapshot(_this2.editableDiv)
22975
+ });
22701
22976
  return;
22702
22977
  }
22703
22978
  if (e.key === "ArrowRight") {
22704
22979
  _this2.handleKeyDownArrowRight(e);
22980
+ debugMathRichInput("handleKeyDown:after-arrow-right", {
22981
+ selection: getSelectionSnapshot(_this2.editableDiv)
22982
+ });
22705
22983
  return;
22706
22984
  }
22707
22985
  if (e.key === "ArrowUp") {
@@ -22794,6 +23072,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22794
23072
  });
22795
23073
  _defineProperty(_this2, "handleKeyUp", function (e) {
22796
23074
  try {
23075
+ debugMathRichInput("handleKeyUp:start", {
23076
+ key: e.key,
23077
+ altKey: e.altKey,
23078
+ metaKey: e.metaKey,
23079
+ ctrlKey: e.ctrlKey,
23080
+ shiftKey: e.shiftKey,
23081
+ selection: getSelectionSnapshot(_this2.editableDiv)
23082
+ });
22797
23083
  _this2.keyPressed = e.key;
22798
23084
  _this2.keyPressStartTime = -1;
22799
23085
 
@@ -22869,8 +23155,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22869
23155
  try {
22870
23156
  // console.log("handleInput")
22871
23157
  if (_this2.isComposing) return;
23158
+ debugMathRichInput("handleInput:start", {
23159
+ inputType: event && event.nativeEvent ? event.nativeEvent.inputType : null,
23160
+ data: event && event.nativeEvent ? event.nativeEvent.data : null,
23161
+ propsValue: _this2.props.value,
23162
+ propsMimeType: _this2.props.mimeType,
23163
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
23164
+ selection: getSelectionSnapshot(_this2.editableDiv)
23165
+ });
23166
+ _this2.cleanupEmptyInlineFormattingElements();
22872
23167
  var raw_text = elementToMarkedRawText(_this2.editableDiv, null, 0, _this2.enableHtml());
22873
23168
  var params = _this2._getRangeParams();
23169
+ debugMathRichInput("handleInput:after-range", {
23170
+ raw_text: raw_text,
23171
+ params: params,
23172
+ selection: getSelectionSnapshot(_this2.editableDiv)
23173
+ });
22874
23174
  if (params === null || params === undefined) {
22875
23175
  // console.log(
22876
23176
  // "handleInput: Empty content detected, using default range params"
@@ -22889,7 +23189,10 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22889
23189
  var _mimeType = _this2.getMimeType(_nodeCounts.html > 0, _nodeCounts.math > 0);
22890
23190
  var _raw_text = elementToMarkedRawText(_this2.editableDiv, null, 0, _this2.enableHtml());
22891
23191
  _raw_text = removeEncodingIfPlainText(_raw_text, _mimeType, _this2.enableHtml());
22892
- _this2.applyChangesToComponent(_raw_text, _mimeType, defaultParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23192
+ _raw_text = _this2.stripEmptyInlineFormattingTags(_raw_text);
23193
+ _this2.applyChangesToComponent(_raw_text, _mimeType, defaultParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23194
+ skipControlledRender: true
23195
+ });
22893
23196
  return;
22894
23197
  } else if (params.startNodeIndex === null || params.startNodeIndex === undefined) {
22895
23198
  // console.log(
@@ -22910,6 +23213,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22910
23213
  var hasMath = hasMathContent || nodeCounts.math > 0;
22911
23214
  var mimeType = _this2.getMimeType(hasHtml, hasMath);
22912
23215
  raw_text = removeEncodingIfPlainText(raw_text, mimeType, _this2.enableHtml());
23216
+ raw_text = _this2.stripEmptyInlineFormattingTags(raw_text);
22913
23217
 
22914
23218
  // Use valid params or fallback to defaults
22915
23219
  var validParams = params || {
@@ -22920,7 +23224,16 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22920
23224
  startGlobalOffset: 0,
22921
23225
  endGlobalOffset: 0
22922
23226
  };
22923
- _this2.applyChangesToComponent(raw_text, mimeType, validParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23227
+ _this2.setOldRangeParams(validParams);
23228
+ _this2.applyChangesToComponent(raw_text, mimeType, validParams, _this2.props.useExpertMode, _this2.props.selectedTab, {
23229
+ skipControlledRender: true
23230
+ });
23231
+ debugMathRichInput("handleInput:after-apply", {
23232
+ raw_text: raw_text,
23233
+ mimeType: mimeType,
23234
+ validParams: validParams,
23235
+ selection: getSelectionSnapshot(_this2.editableDiv)
23236
+ });
22924
23237
  } catch (error) {
22925
23238
  console.error(error);
22926
23239
  }
@@ -22965,44 +23278,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22965
23278
  });
22966
23279
  return;
22967
23280
  }
22968
- var node = _this2._findNodeWithIndex(rangeParams.startNodeIndex);
22969
- var bold = false;
22970
- var italic = false;
22971
- var underline = false;
22972
- var superscript = false;
22973
- var subscript = false;
22974
- do {
22975
- if (node.nodeType === 1) {
22976
- var nodeName = node.nodeName.toLowerCase();
22977
- if (nodeName === "b" || nodeName === "strong") bold = true;else if (nodeName === "i" || nodeName === "em") italic = true;else if (nodeName === "u") underline = true;else if (nodeName === "sub") subscript = true;else if (nodeName === "sup") superscript = true;
22978
- } else if (node.classList !== null && node.classList !== undefined && node.classList.contains("MathRichInput")) break;
22979
- node = node.parentNode;
22980
- } while (node !== null);
22981
- var activeButtons = {
22982
- bold: bold,
22983
- italic: italic,
22984
- underline: underline,
22985
- subscript: subscript,
22986
- superscript: superscript
22987
- };
22988
- // console.log(activeButtons)
22989
-
22990
- var update = true;
22991
- if (_this2.lastSetActiveButtons !== null) {
22992
- update = false;
22993
- for (var style in activeButtons) {
22994
- if (activeButtons[style] !== _this2.lastSetActiveButtons[style]) {
22995
- update = true;
22996
- break;
22997
- }
22998
- }
22999
- }
23000
- if (update) {
23001
- _this2.lastSetActiveButtons = activeButtons;
23002
- _this2.setState({
23003
- activeButtons: activeButtons
23004
- });
23005
- }
23281
+ var activeButtons = _this2.getActiveButtonsFromSelection(rangeParams);
23282
+ _this2.setActiveButtonsIfChanged(activeButtons);
23006
23283
  } catch (error) {
23007
23284
  console.error(error);
23008
23285
  }
@@ -23237,6 +23514,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23237
23514
  var node = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
23238
23515
  try {
23239
23516
  var rangeParams = _this2._getRangeParams();
23517
+ debugMathRichInput("showEquationEditor:start", {
23518
+ moveCursorOnInsert: moveCursorOnInsert,
23519
+ nodeProvided: Boolean(node),
23520
+ rangeParams: rangeParams,
23521
+ oldRangeParams: _this2.oldRangeParams,
23522
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
23523
+ selection: getSelectionSnapshot(_this2.editableDiv)
23524
+ });
23240
23525
  if (rangeParams.startNodeIndex < 0) {
23241
23526
  console.error("Could not find position of cursor in node list");
23242
23527
  _this2.setOldRangeParams({
@@ -23279,6 +23564,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23279
23564
  _this2.editingExistingEquation = false;
23280
23565
  _this2.editingNode = null;
23281
23566
  }
23567
+ _this2.setOldRangeParams(rangeParams);
23282
23568
  _this2.markedRawText = elementToMarkedRawText(_this2.editableDiv, node, offset, _this2.enableHtml());
23283
23569
  // console.log("Marked text:"+this.markedRawText)
23284
23570
  _this2.moveCursorOnInsert = moveCursorOnInsert;
@@ -23286,11 +23572,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23286
23572
  showEquationEditor: true,
23287
23573
  equationEditorLatex: latex
23288
23574
  });
23575
+ debugMathRichInput("showEquationEditor:after-setState", {
23576
+ rangeParams: rangeParams,
23577
+ markedRawText: _this2.markedRawText,
23578
+ latex: latex,
23579
+ selection: getSelectionSnapshot(_this2.editableDiv)
23580
+ });
23289
23581
  } catch (error) {
23290
23582
  console.error(error);
23291
23583
  }
23292
23584
  });
23293
23585
  _defineProperty(_this2, "handleMouseDown", function (event) {
23586
+ _this2.isMouseDownInEditable = true;
23587
+ window.setTimeout(function () {
23588
+ _this2.isMouseDownInEditable = false;
23589
+ }, 0);
23294
23590
  _this2.showEquationEditorOnMouseUp = false;
23295
23591
  var node = null;
23296
23592
  try {
@@ -23326,6 +23622,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23326
23622
  // Clear selection anchor on mouse click
23327
23623
  _this2.selectionAnchor = null;
23328
23624
  var params = _this2._getRangeParams();
23625
+ _this2.setOldRangeParams(params);
23329
23626
  var startNode = _this2._findNodeWithIndex(params.startNodeIndex);
23330
23627
  var endNode = _this2._findNodeWithIndex(params.endNodeIndex);
23331
23628
  if (startNode === null || endNode === null) {
@@ -23381,7 +23678,15 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23381
23678
  _defineProperty(_this2, "handleEquationEditorInsert", function (latex) {
23382
23679
  try {
23383
23680
  // console.log("Inserting latex: "+latex)
23384
-
23681
+ debugMathRichInput("handleEquationEditorInsert:start", {
23682
+ latex: latex,
23683
+ markedRawText: _this2.markedRawText,
23684
+ oldRangeParams: _this2.getOldRangeParams(),
23685
+ moveCursorOnInsert: _this2.moveCursorOnInsert,
23686
+ propsValue: _this2.props.value,
23687
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
23688
+ selection: getSelectionSnapshot(_this2.editableDiv)
23689
+ });
23385
23690
  var start_pos = _this2.markedRawText.indexOf(MARK);
23386
23691
  var end_pos = _this2.markedRawText.indexOf(MARK, start_pos + MARK.length);
23387
23692
  var oldRangeParams = _this2.getOldRangeParams();
@@ -23423,6 +23728,10 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23423
23728
  _this2.applyChangesToComponent(_new_raw_text, _mimeType2, _new_rangeParams7, _this2.props.useExpertMode, _this2.props.selectedTab);
23424
23729
  _this2.setState({
23425
23730
  showEquationEditor: false
23731
+ }, function () {
23732
+ _this2.editableDiv.focus();
23733
+ _this2._setRangeParams(_new_rangeParams7);
23734
+ _this2.restoreRangeAfterBrowserWork(_new_rangeParams7, "equation-empty");
23426
23735
  });
23427
23736
  return;
23428
23737
  }
@@ -23441,28 +23750,48 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23441
23750
  endOffset: SMALL_SPACE_LENGTH
23442
23751
  };
23443
23752
  } else {
23444
- // Position cursor after the inserted math element using globalOffset
23445
- // Find the position right after the last math tag in the content
23446
- var mathTagMatches = _toConsumableArray(new_raw_text.matchAll(/<math>[^<]*<\/math>/gi));
23447
- if (mathTagMatches.length > 0) {
23448
- var lastMatch = mathTagMatches[mathTagMatches.length - 1];
23449
- var afterMathOffset = lastMatch.index + lastMatch[0].length;
23753
+ // In this editor a rendered equation counts as one logical character
23754
+ // in global-offset space. Put the caret immediately after that math
23755
+ // character instead of guessing rendered child-node indexes.
23756
+ var _oldRangeParams = _this2.getOldRangeParams();
23757
+ if (_oldRangeParams !== null && _oldRangeParams.startGlobalOffset !== null && _oldRangeParams.startGlobalOffset !== undefined) {
23758
+ var afterInsertedMathOffset = _oldRangeParams.startGlobalOffset + 1;
23450
23759
  new_rangeParams = {
23451
- startGlobalOffset: afterMathOffset,
23452
- endGlobalOffset: afterMathOffset
23760
+ startGlobalOffset: afterInsertedMathOffset,
23761
+ endGlobalOffset: afterInsertedMathOffset
23762
+ };
23763
+ } else if (_oldRangeParams !== null && _oldRangeParams.startNodeIndex !== null && _oldRangeParams.startNodeIndex !== undefined) {
23764
+ new_rangeParams = {
23765
+ startNodeIndex: _oldRangeParams.startNodeIndex + 2,
23766
+ startOffset: SMALL_SPACE_LENGTH,
23767
+ endNodeIndex: _oldRangeParams.startNodeIndex + 2,
23768
+ endOffset: SMALL_SPACE_LENGTH
23453
23769
  };
23454
23770
  }
23455
- // If no math tags found, keep old range params
23771
+ // If old range params are unavailable, keep the current fallback.
23456
23772
  }
23457
23773
  }
23458
23774
  // console.log("New cursor pos: "+new_rangeParams.startNodeIndex+"->"+new_rangeParams.startOffset)
23459
23775
  _this2.setOldRangeParams(new_rangeParams);
23776
+ debugMathRichInput("handleEquationEditorInsert:before-apply", {
23777
+ new_raw_text: new_raw_text,
23778
+ new_rangeParams: new_rangeParams,
23779
+ selection: getSelectionSnapshot(_this2.editableDiv)
23780
+ });
23460
23781
  var nodeCounts = _this2._countNodeTypes();
23461
23782
  var mimeType = _this2.getMimeType(nodeCounts.html > 0, true);
23462
23783
  new_raw_text = removeEncodingIfPlainText(new_raw_text, mimeType, _this2.enableHtml());
23463
23784
  _this2.applyChangesToComponent(new_raw_text, mimeType, new_rangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
23464
23785
  _this2.setState({
23465
23786
  showEquationEditor: false
23787
+ }, function () {
23788
+ _this2.editableDiv.focus();
23789
+ _this2._setRangeParams(new_rangeParams);
23790
+ _this2.restoreRangeAfterBrowserWork(new_rangeParams, "equation-insert");
23791
+ debugMathRichInput("handleEquationEditorInsert:after-close-restore", {
23792
+ new_rangeParams: new_rangeParams,
23793
+ selection: getSelectionSnapshot(_this2.editableDiv)
23794
+ });
23466
23795
  });
23467
23796
  } catch (error) {
23468
23797
  console.error(error);
@@ -23479,13 +23808,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23479
23808
  _defineProperty(_this2, "styleText", function (event, style) {
23480
23809
  try {
23481
23810
  style = style.toLowerCase();
23811
+ var command = _this2.getStyleCommand(style);
23812
+ var activeBefore = _this2.getActiveButtonsFromSelection();
23813
+ var selection = document.getSelection();
23814
+ var isCollapsed = selection === null || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed;
23482
23815
  var success = false;
23816
+ var insertedTypingStylePlaceholder = false;
23483
23817
 
23484
23818
  // This first part is a workaround whereby executing subscript or superscript
23485
23819
  // within an existing subscript or superscript makes the text even smaller instead
23486
23820
  // of toggling it off. Thus we manually toggle it off with some jigerry pokery!
23487
- if (style === "subscript" && _this2.state.activeButtons.subscript || style === "superscript" && _this2.state.activeButtons.superscript) {
23488
- var selection = document.getSelection();
23821
+ if (isCollapsed && activeBefore[style] && _this2.moveTrailingWhitespaceOutsideInlineStyle(style, command)) {
23822
+ success = true;
23823
+ } else if (isCollapsed && !activeBefore[style] && selection && selection.rangeCount > 0) {
23824
+ success = _this2.insertInlineStylePlaceholder(style);
23825
+ insertedTypingStylePlaceholder = success;
23826
+ } else if (style === "subscript" && activeBefore.subscript || style === "superscript" && activeBefore.superscript) {
23489
23827
  var range = selection.getRangeAt(0);
23490
23828
  if (range.endContainer === range.startContainer && range.endOffset === range.startOffset) {
23491
23829
  success = document.execCommand("insertText", false, " ");
@@ -23494,78 +23832,21 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23494
23832
  selection.addRange(range);
23495
23833
  }
23496
23834
  success = document.execCommand("removeFormat", false, null);
23497
- if (_this2.state.activeButtons.bold) success = document.execCommand("bold", false, null);
23498
- if (_this2.state.activeButtons.italic) success = document.execCommand("italic", false, null);
23499
- if (_this2.state.activeButtons.underline) success = document.execCommand("underline", false, null);
23835
+ if (activeBefore.bold) success = document.execCommand("bold", false, null);
23836
+ if (activeBefore.italic) success = document.execCommand("italic", false, null);
23837
+ if (activeBefore.underline) success = document.execCommand("underline", false, null);
23500
23838
  } else {
23501
- var _selection = document.getSelection();
23502
- if (_selection && _selection.rangeCount > 0) {
23503
- var _range = _selection.getRangeAt(0);
23504
- if (_range.collapsed) {
23505
- // For cursor position (no selection), use insertHTML with styled wrapper
23506
- // This creates a formatted element that will maintain styling for future typing
23507
- var isToggleOff = _this2.state.activeButtons[style];
23508
- if (!isToggleOff) {
23509
- // Turning style ON - insert a styled wrapper element
23510
- var wrapperTag;
23511
- switch (style) {
23512
- case "bold":
23513
- wrapperTag = "strong";
23514
- break;
23515
- case "italic":
23516
- wrapperTag = "em";
23517
- break;
23518
- case "underline":
23519
- wrapperTag = "u";
23520
- break;
23521
- case "subscript":
23522
- wrapperTag = "sub";
23523
- break;
23524
- case "superscript":
23525
- wrapperTag = "sup";
23526
- break;
23527
- default:
23528
- wrapperTag = "span";
23529
- break;
23530
- }
23531
-
23532
- // Insert an invisible character wrapped in the styled element
23533
- // This provides a "landing zone" for future typing
23534
- var styledElement = "<".concat(wrapperTag, ">\u200B</").concat(wrapperTag, ">");
23535
- success = document.execCommand("insertHTML", false, styledElement);
23536
-
23537
- // Move cursor inside the styled element
23538
- setTimeout(function () {
23539
- var newSelection = document.getSelection();
23540
- if (newSelection && newSelection.rangeCount > 0) {
23541
- var newRange = newSelection.getRangeAt(0);
23542
- var styledNode = newRange.startContainer.parentElement;
23543
- if (styledNode && styledNode.tagName.toLowerCase() === wrapperTag) {
23544
- newRange.setStart(styledNode, 0);
23545
- newRange.setEnd(styledNode, 0);
23546
- newSelection.removeAllRanges();
23547
- newSelection.addRange(newRange);
23548
- }
23549
- }
23550
- }, 0);
23551
- } else {
23552
- // Turning style OFF - just apply execCommand normally
23553
- success = document.execCommand(style, false, null);
23554
- }
23555
- } else {
23556
- // For text selection, use regular execCommand
23557
- success = document.execCommand(style, false, null);
23558
- }
23839
+ if (selection && selection.rangeCount > 0) {
23840
+ success = document.execCommand(command, false, null);
23559
23841
  }
23560
23842
  }
23561
- var buttonState = _this2.state.activeButtons[style];
23562
- if (buttonState === null || buttonState === undefined) return success;
23563
- buttonState = !buttonState;
23564
- var new_activeButtons = _objectSpread2({}, _this2.state.activeButtons);
23565
- new_activeButtons[style] = buttonState;
23566
- _this2.setState({
23567
- activeButtons: new_activeButtons
23568
- });
23843
+ var rangeParams = _this2._getRangeParams();
23844
+ var removedEmptyTags = !insertedTypingStylePlaceholder && _this2.cleanupEmptyInlineFormattingElements();
23845
+ if (removedEmptyTags && rangeParams !== null && rangeParams !== undefined) {
23846
+ _this2._setRangeParams(rangeParams);
23847
+ _this2.applyCurrentEditableDomToComponent(rangeParams);
23848
+ }
23849
+ _this2.setActiveButtonsIfChanged(_this2.getActiveButtonsFromSelection(rangeParams));
23569
23850
  return success;
23570
23851
  } catch (error) {
23571
23852
  console.error(error);
@@ -23573,8 +23854,15 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23573
23854
  });
23574
23855
  _defineProperty(_this2, "handleToolbarButtonClick", function (event, buttonName) {
23575
23856
  try {
23857
+ debugMathRichInput("handleToolbarButtonClick:start", {
23858
+ buttonName: buttonName,
23859
+ eventType: event ? event.type : null,
23860
+ selection: getSelectionSnapshot(_this2.editableDiv),
23861
+ oldRangeParams: _this2.oldRangeParams
23862
+ });
23576
23863
  if (buttonName === "Equation") {
23577
23864
  _this2.showEquationEditor(true);
23865
+ return;
23578
23866
  }
23579
23867
  var style = buttonName;
23580
23868
  var success = _this2.styleText(event, style);
@@ -23886,13 +24174,36 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23886
24174
  });
23887
24175
  _defineProperty(_this2, "handleFocus", function (event) {
23888
24176
  var onFocus = _this2.props.onFocus;
24177
+ var currentRangeParams = null;
24178
+ try {
24179
+ currentRangeParams = _this2._getRangeParams();
24180
+ } catch (error) {
24181
+ currentRangeParams = null;
24182
+ }
24183
+ var savedRangeParams = _this2.getOldRangeParams();
24184
+ var shouldRestoreSavedRange = !_this2.isMouseDownInEditable && _this2.props.value !== "" && _this2.rangeIsAtStart(currentRangeParams) && _this2.rangeHasNonZeroPosition(savedRangeParams);
24185
+ debugMathRichInput("handleFocus:start", {
24186
+ propsValue: _this2.props.value,
24187
+ oldRangeParams: _this2.oldRangeParams,
24188
+ currentRangeParams: currentRangeParams,
24189
+ shouldRestoreSavedRange: shouldRestoreSavedRange,
24190
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
24191
+ selection: getSelectionSnapshot(_this2.editableDiv)
24192
+ });
23889
24193
  if (onFocus != null) {
23890
24194
  onFocus();
23891
24195
  }
23892
24196
  try {
23893
- _this2._setRangeParams(_this2.getOldRangeParams());
23894
24197
  _this2.setState({
23895
24198
  hasFocus: true
24199
+ }, function () {
24200
+ if (shouldRestoreSavedRange) {
24201
+ _this2._setRangeParams(savedRangeParams);
24202
+ _this2.restoreRangeAfterBrowserWork(savedRangeParams, "focus-restore");
24203
+ }
24204
+ debugMathRichInput("handleFocus:after-setState", {
24205
+ selection: getSelectionSnapshot(_this2.editableDiv)
24206
+ });
23896
24207
  });
23897
24208
  } catch (error) {
23898
24209
  console.error(error);
@@ -23900,6 +24211,22 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23900
24211
  });
23901
24212
  _defineProperty(_this2, "handleBlur", function (event) {
23902
24213
  var onBlur = _this2.props.onBlur;
24214
+ var currentRangeParams = null;
24215
+ try {
24216
+ currentRangeParams = _this2._getRangeParams();
24217
+ if (_this2.rangeHasNonZeroPosition(currentRangeParams)) {
24218
+ _this2.setOldRangeParams(currentRangeParams);
24219
+ }
24220
+ } catch (error) {
24221
+ currentRangeParams = null;
24222
+ }
24223
+ debugMathRichInput("handleBlur:start", {
24224
+ propsValue: _this2.props.value,
24225
+ oldRangeParams: _this2.oldRangeParams,
24226
+ currentRangeParams: currentRangeParams,
24227
+ innerHTML: _this2.editableDiv ? _this2.editableDiv.innerHTML : null,
24228
+ selection: getSelectionSnapshot(_this2.editableDiv)
24229
+ });
23903
24230
  if (onBlur != null) {
23904
24231
  onBlur();
23905
24232
  }
@@ -23909,6 +24236,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23909
24236
  hasFocus: false,
23910
24237
  showAccentBar: false
23911
24238
  });
24239
+ debugMathRichInput("handleBlur:after-setState", {
24240
+ selection: getSelectionSnapshot(_this2.editableDiv)
24241
+ });
23912
24242
  } catch (error) {
23913
24243
  console.error(error);
23914
24244
  }
@@ -24040,18 +24370,20 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24040
24370
 
24041
24371
  // Elements that need to be set before the equation editor is shown
24042
24372
  _this2.markedRawText = null;
24043
- _this2.oldRangeParams = {
24044
- startNodeIndex: 0,
24045
- startOffset: 0,
24046
- endNodeIndex: 0,
24047
- endOffset: 0
24048
- };
24373
+ // No selection has been captured yet. Do not default this to the start of
24374
+ // the editor: React 19 focus/click ordering makes handleFocus restore that
24375
+ // synthetic 0,0 range and moves the caret to the beginning on every click.
24376
+ _this2.oldRangeParams = null;
24049
24377
  _this2.moveCursorOnInsert = false;
24050
24378
 
24051
24379
  // Elements defining the current state of range
24052
24380
  _this2.afterComponentUpdateData = null; // Set this to update the selection after rendering
24053
- _this2.lastSetRangeParmas = null; // This is set automatically each time the range params are set with setRangeParams
24054
-
24381
+ _this2.lastSetRangeParams = null; // This is set automatically each time the range params are set with setRangeParams
24382
+ _this2.skipNextControlledValueRender = false;
24383
+ _this2.skipNextControlledValue = null;
24384
+ _this2.isMouseDownInEditable = false;
24385
+ _this2.lastRenderedEditableHtml = null;
24386
+ _this2.preserveNativeDomRangeParams = null;
24055
24387
  _this2.lastSetActiveButtons = null;
24056
24388
  _this2.onChangeCallback = _this2.props.onChange;
24057
24389
 
@@ -24141,12 +24473,403 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24141
24473
  var params = _objectSpread2({}, this.oldRangeParams);
24142
24474
  return params;
24143
24475
  }
24476
+ }, {
24477
+ key: "rangeIsAtStart",
24478
+ value: function rangeIsAtStart(params) {
24479
+ if (params === null || params === undefined) return false;
24480
+ if (params.startGlobalOffset !== null && params.startGlobalOffset !== undefined && params.endGlobalOffset !== null && params.endGlobalOffset !== undefined) {
24481
+ return params.startGlobalOffset === 0 && params.endGlobalOffset === 0;
24482
+ }
24483
+ return params.startNodeIndex === 0 && params.endNodeIndex === 0 && params.startOffset === 0 && params.endOffset === 0;
24484
+ }
24485
+ }, {
24486
+ key: "rangeHasNonZeroPosition",
24487
+ value: function rangeHasNonZeroPosition(params) {
24488
+ if (params === null || params === undefined) return false;
24489
+ if (params.startGlobalOffset !== null && params.startGlobalOffset !== undefined) {
24490
+ return params.startGlobalOffset > 0;
24491
+ }
24492
+ return params.startNodeIndex > 0 || params.endNodeIndex > 0 || params.startOffset > 0 || params.endOffset > 0;
24493
+ }
24494
+ }, {
24495
+ key: "getCurrentRawTextForProps",
24496
+ value: function getCurrentRawTextForProps(nextProps) {
24497
+ if (!this.editableDiv) return null;
24498
+ try {
24499
+ var rawText = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
24500
+ rawText = this.stripEmptyInlineFormattingTags(rawText);
24501
+ return removeEncodingIfPlainText(rawText, nextProps.mimeType, this.enableHtml());
24502
+ } catch (error) {
24503
+ debugMathRichInput("getCurrentRawTextForProps:error", {
24504
+ error: error,
24505
+ selection: getSelectionSnapshot(this.editableDiv)
24506
+ });
24507
+ return null;
24508
+ }
24509
+ }
24510
+ }, {
24511
+ key: "shouldSkipRedundantFocusedRender",
24512
+ value: function shouldSkipRedundantFocusedRender(nextProps, nextState) {
24513
+ if (!this.editableDiv) return false;
24514
+ if (nextState !== this.state) return false;
24515
+ if (nextState.hasFocus !== true) return false;
24516
+ if (this.afterComponentUpdateData !== null && this.afterComponentUpdateData !== undefined) {
24517
+ return false;
24518
+ }
24519
+ var currentRawText = this.getCurrentRawTextForProps(nextProps);
24520
+ var shouldSkip = currentRawText === nextProps.value;
24521
+ debugMathRichInput("shouldComponentUpdate:redundant-focused-check", {
24522
+ shouldSkip: shouldSkip,
24523
+ currentRawText: currentRawText,
24524
+ nextValue: nextProps.value,
24525
+ currentPropsValue: this.props.value,
24526
+ nextMimeType: nextProps.mimeType,
24527
+ currentMimeType: this.props.mimeType,
24528
+ selection: getSelectionSnapshot(this.editableDiv)
24529
+ });
24530
+ if (shouldSkip) {
24531
+ var currentRangeParams = this.getRangeParamsPreservingInlinePlaceholder();
24532
+ if (this.rangeHasNonZeroPosition(currentRangeParams)) {
24533
+ this.setOldRangeParams(currentRangeParams);
24534
+ }
24535
+ }
24536
+ return shouldSkip;
24537
+ }
24538
+ }, {
24539
+ key: "isSelectionInsideInlinePlaceholder",
24540
+ value: function isSelectionInsideInlinePlaceholder() {
24541
+ if (typeof document === "undefined") return false;
24542
+ var selection = document.getSelection ? document.getSelection() : null;
24543
+ if (!selection || selection.rangeCount === 0) return false;
24544
+ var range = selection.getRangeAt(0);
24545
+ if (!range.collapsed) return false;
24546
+ var node = range.startContainer;
24547
+ if (!node || node.nodeType !== 3) return false;
24548
+ if (node.nodeValue !== SMALL_SPACE) return false;
24549
+ if (range.startOffset !== SMALL_SPACE_LENGTH) return false;
24550
+ var parent = node.parentNode;
24551
+ if (!parent || parent.nodeType !== 1) return false;
24552
+ var tagName = parent.nodeName.toLowerCase();
24553
+ if (!INLINE_STYLE_TAG_NAMES.includes(tagName)) return false;
24554
+ return parent.textContent === SMALL_SPACE;
24555
+ }
24556
+ }, {
24557
+ key: "getRangeParamsPreservingInlinePlaceholder",
24558
+ value: function getRangeParamsPreservingInlinePlaceholder() {
24559
+ var selectionInsideInlinePlaceholder = this.isSelectionInsideInlinePlaceholder();
24560
+ var rangeParams = this._getRangeParams();
24561
+ if (!selectionInsideInlinePlaceholder || !rangeParams) {
24562
+ return rangeParams;
24563
+ }
24564
+
24565
+ // Global offsets ignore the zero-width placeholder; node index preserves it.
24566
+ return _objectSpread2(_objectSpread2({}, rangeParams), {}, {
24567
+ startGlobalOffset: null,
24568
+ endGlobalOffset: null
24569
+ });
24570
+ }
24571
+ }, {
24572
+ key: "getEditableHtmlForRender",
24573
+ value: function getEditableHtmlForRender(html) {
24574
+ var currentRawText = this.editableDiv ? this.getCurrentRawTextForProps(this.props) : null;
24575
+ var liveEditableHtml = this.editableDiv ? this.editableDiv.innerHTML : null;
24576
+ 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;
24577
+ var preserveRangeParams = canPreserveNativeDom ? this.getRangeParamsPreservingInlinePlaceholder() : null;
24578
+ debugMathRichInput("render:editable-html", {
24579
+ canPreserveNativeDom: canPreserveNativeDom,
24580
+ selectionInsideInlinePlaceholder: this.isSelectionInsideInlinePlaceholder(),
24581
+ propsValue: this.props.value,
24582
+ generatedHtml: html,
24583
+ renderedHtml: canPreserveNativeDom ? liveEditableHtml : html,
24584
+ liveEditableHtml: liveEditableHtml,
24585
+ lastRenderedEditableHtml: this.lastRenderedEditableHtml,
24586
+ currentRawText: currentRawText,
24587
+ preserveRangeParams: preserveRangeParams,
24588
+ afterComponentUpdateData: this.afterComponentUpdateData,
24589
+ hasFocus: this.state.hasFocus,
24590
+ selection: getSelectionSnapshot(this.editableDiv)
24591
+ });
24592
+ if (canPreserveNativeDom) {
24593
+ this.lastRenderedEditableHtml = liveEditableHtml;
24594
+ this.preserveNativeDomRangeParams = preserveRangeParams;
24595
+ return liveEditableHtml;
24596
+ }
24597
+ this.lastRenderedEditableHtml = html;
24598
+ this.preserveNativeDomRangeParams = null;
24599
+ return html;
24600
+ }
24601
+ }, {
24602
+ key: "stripEmptyInlineFormattingTags",
24603
+ value: function stripEmptyInlineFormattingTags(rawText) {
24604
+ if (rawText === null || rawText === undefined) return rawText;
24605
+ var cleaned = rawText;
24606
+ var previous = null;
24607
+ while (previous !== cleaned) {
24608
+ previous = cleaned;
24609
+ cleaned = cleaned.replace(EMPTY_INLINE_FORMATTING_REG_EXP, "");
24610
+ }
24611
+ return cleaned;
24612
+ }
24613
+ }, {
24614
+ key: "cleanupEmptyInlineFormattingElements",
24615
+ value: function cleanupEmptyInlineFormattingElements() {
24616
+ if (!this.editableDiv) return false;
24617
+ var removed = false;
24618
+ this.editableDiv.querySelectorAll("b,strong,i,em,u,sub,sup").forEach(function (node) {
24619
+ var text = node.textContent || "";
24620
+ var hasMeaningfulText = text.replace(/[\s\u00a0\u200b\ufeff]/g, "") !== "";
24621
+ var hasProtectedContent = node.querySelector("br,img,math,.katex") !== null;
24622
+ if (!hasMeaningfulText && !hasProtectedContent) {
24623
+ node.remove();
24624
+ removed = true;
24625
+ }
24626
+ });
24627
+ if (removed) {
24628
+ this.editableDiv.normalize();
24629
+ }
24630
+ return removed;
24631
+ }
24632
+ }, {
24633
+ key: "getStyleCommand",
24634
+ value: function getStyleCommand(style) {
24635
+ return INLINE_STYLE_COMMANDS[style] || style;
24636
+ }
24637
+ }, {
24638
+ key: "insertInlineStylePlaceholder",
24639
+ value: function insertInlineStylePlaceholder(style) {
24640
+ var wrapperTag = INLINE_STYLE_WRAPPER_TAGS[style] || "span";
24641
+ var selection = document.getSelection();
24642
+ if (!selection || selection.rangeCount === 0) return false;
24643
+ var range = selection.getRangeAt(0);
24644
+ if (!range.collapsed) return false;
24645
+ range.deleteContents();
24646
+ var wrapper = document.createElement(wrapperTag);
24647
+ var textNode = document.createTextNode(SMALL_SPACE);
24648
+ wrapper.appendChild(textNode);
24649
+ range.insertNode(wrapper);
24650
+ var nextRange = document.createRange();
24651
+ nextRange.setStart(textNode, SMALL_SPACE_LENGTH);
24652
+ nextRange.collapse(true);
24653
+ selection.removeAllRanges();
24654
+ selection.addRange(nextRange);
24655
+ var rangeParams = this.getRangeParamsPreservingInlinePlaceholder();
24656
+ if (rangeParams) this.setOldRangeParams(rangeParams);
24657
+ debugMathRichInput("insertInlineStylePlaceholder:after", {
24658
+ style: style,
24659
+ wrapperTag: wrapperTag,
24660
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24661
+ rangeParams: rangeParams,
24662
+ selection: getSelectionSnapshot(this.editableDiv)
24663
+ });
24664
+ return true;
24665
+ }
24666
+ }, {
24667
+ key: "getActiveButtonsFromCommandState",
24668
+ value: function getActiveButtonsFromCommandState() {
24669
+ var query = function query(command) {
24670
+ try {
24671
+ return document.queryCommandState(command);
24672
+ } catch (error) {
24673
+ return false;
24674
+ }
24675
+ };
24676
+ return {
24677
+ bold: query("bold"),
24678
+ italic: query("italic"),
24679
+ underline: query("underline"),
24680
+ subscript: query("subscript"),
24681
+ superscript: query("superscript")
24682
+ };
24683
+ }
24684
+ }, {
24685
+ key: "isSelectionCollapsed",
24686
+ value: function isSelectionCollapsed() {
24687
+ var selection = document.getSelection();
24688
+ if (!selection || selection.rangeCount === 0) return true;
24689
+ return selection.getRangeAt(0).collapsed;
24690
+ }
24691
+ }, {
24692
+ key: "getActiveButtonsFromAncestorState",
24693
+ value: function getActiveButtonsFromAncestorState() {
24694
+ var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24695
+ if (rangeParams === null || rangeParams === undefined) {
24696
+ rangeParams = this._getRangeParams();
24697
+ }
24698
+ var activeButtons = {
24699
+ bold: false,
24700
+ italic: false,
24701
+ underline: false,
24702
+ subscript: false,
24703
+ superscript: false
24704
+ };
24705
+ if (rangeParams === null || rangeParams === undefined || rangeParams.startNodeIndex < 0) {
24706
+ return activeButtons;
24707
+ }
24708
+ var node = this._findNodeWithIndex(rangeParams.startNodeIndex);
24709
+ if (node === null || node === undefined) return activeButtons;
24710
+ do {
24711
+ if (node.nodeType === 1) {
24712
+ var nodeName = node.nodeName.toLowerCase();
24713
+ if (nodeName === "b" || nodeName === "strong") activeButtons.bold = true;else if (nodeName === "i" || nodeName === "em") activeButtons.italic = true;else if (nodeName === "u") activeButtons.underline = true;else if (nodeName === "sub") activeButtons.subscript = true;else if (nodeName === "sup") activeButtons.superscript = true;
24714
+ } else if (node.classList !== null && node.classList !== undefined && node.classList.contains("MathRichInput")) break;
24715
+ node = node.parentNode;
24716
+ } while (node !== null);
24717
+ return activeButtons;
24718
+ }
24719
+ }, {
24720
+ key: "getActiveButtonsFromSelection",
24721
+ value: function getActiveButtonsFromSelection() {
24722
+ var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24723
+ var commandState = this.getActiveButtonsFromCommandState();
24724
+ var ancestorState = this.getActiveButtonsFromAncestorState(rangeParams);
24725
+ return {
24726
+ bold: commandState.bold || ancestorState.bold,
24727
+ italic: commandState.italic || ancestorState.italic,
24728
+ underline: commandState.underline || ancestorState.underline,
24729
+ subscript: commandState.subscript || ancestorState.subscript,
24730
+ superscript: commandState.superscript || ancestorState.superscript
24731
+ };
24732
+ }
24733
+ }, {
24734
+ key: "setActiveButtonsIfChanged",
24735
+ value: function setActiveButtonsIfChanged(activeButtons) {
24736
+ var update = true;
24737
+ if (this.lastSetActiveButtons !== null) {
24738
+ update = false;
24739
+ for (var style in activeButtons) {
24740
+ if (activeButtons[style] !== this.lastSetActiveButtons[style]) {
24741
+ update = true;
24742
+ break;
24743
+ }
24744
+ }
24745
+ }
24746
+ if (update) {
24747
+ this.lastSetActiveButtons = activeButtons;
24748
+ this.setState({
24749
+ activeButtons: activeButtons
24750
+ });
24751
+ }
24752
+ }
24753
+ }, {
24754
+ key: "getInlineStyleAncestor",
24755
+ value: function getInlineStyleAncestor(style) {
24756
+ var tags = INLINE_STYLE_TAGS[style];
24757
+ if (!tags) return null;
24758
+ var selection = document.getSelection();
24759
+ if (!selection || selection.rangeCount === 0) return null;
24760
+ var node = selection.getRangeAt(0).startContainer;
24761
+ if (node.nodeType === Node.TEXT_NODE) node = node.parentNode;
24762
+ while (node && node !== this.editableDiv) {
24763
+ if (node.nodeType === Node.ELEMENT_NODE && tags.includes(node.nodeName.toLowerCase())) {
24764
+ return node;
24765
+ }
24766
+ node = node.parentNode;
24767
+ }
24768
+ return null;
24769
+ }
24770
+ }, {
24771
+ key: "isRangeAtEndOfNode",
24772
+ value: function isRangeAtEndOfNode(range, node) {
24773
+ try {
24774
+ var afterRange = document.createRange();
24775
+ afterRange.setStart(range.startContainer, range.startOffset);
24776
+ afterRange.setEnd(node, node.childNodes.length);
24777
+ var textAfterCaret = afterRange.toString().replace(/[\s\u00a0\u200b\ufeff]/g, "");
24778
+ if (textAfterCaret === "") return true;
24779
+ } catch (error) {
24780
+ // Fall back to boundary comparison below if the browser rejects the range.
24781
+ }
24782
+ var nodeEndRange = document.createRange();
24783
+ nodeEndRange.selectNodeContents(node);
24784
+ nodeEndRange.collapse(false);
24785
+ return range.compareBoundaryPoints(Range.START_TO_START, nodeEndRange) === 0;
24786
+ }
24787
+ }, {
24788
+ key: "moveTrailingWhitespaceOutsideInlineStyle",
24789
+ value: function moveTrailingWhitespaceOutsideInlineStyle(style, command) {
24790
+ var selection = document.getSelection();
24791
+ if (!selection || selection.rangeCount === 0) return false;
24792
+ var range = selection.getRangeAt(0);
24793
+ if (!range.collapsed) return false;
24794
+ var ancestor = this.getInlineStyleAncestor(style);
24795
+ if (!ancestor || !this.isRangeAtEndOfNode(range, ancestor)) return false;
24796
+ if (range.startContainer.nodeType !== Node.TEXT_NODE) return false;
24797
+ var textNode = range.startContainer;
24798
+ var text = textNode.nodeValue || "";
24799
+ var moveStart = range.startOffset;
24800
+ while (moveStart > 0) {
24801
+ var _char = text.charAt(moveStart - 1);
24802
+ if (_char !== " " && _char !== "\xA0") break;
24803
+ moveStart -= 1;
24804
+ }
24805
+ if (moveStart === range.startOffset) return false;
24806
+ var movedText = text.substring(moveStart, range.startOffset);
24807
+ textNode.nodeValue = text.substring(0, moveStart) + text.substring(range.startOffset);
24808
+ var outsideNode = ancestor.nextSibling;
24809
+ if (!outsideNode || outsideNode.nodeType !== Node.TEXT_NODE) {
24810
+ outsideNode = document.createTextNode(movedText);
24811
+ ancestor.parentNode.insertBefore(outsideNode, ancestor.nextSibling);
24812
+ } else {
24813
+ outsideNode.nodeValue = movedText + outsideNode.nodeValue;
24814
+ }
24815
+ var nextRange = document.createRange();
24816
+ nextRange.setStart(outsideNode, movedText.length);
24817
+ nextRange.collapse(true);
24818
+ selection.removeAllRanges();
24819
+ selection.addRange(nextRange);
24820
+ try {
24821
+ if (document.queryCommandState(command)) {
24822
+ document.execCommand(command, false, null);
24823
+ }
24824
+ } catch (error) {
24825
+ // DOM position is the source of truth here; command state is best effort.
24826
+ }
24827
+ var rangeParams = this._getRangeParams();
24828
+ if (rangeParams) {
24829
+ this.setOldRangeParams(rangeParams);
24830
+ this.applyCurrentEditableDomToComponent(rangeParams);
24831
+ }
24832
+ debugMathRichInput("moveTrailingWhitespaceOutsideInlineStyle:after", {
24833
+ style: style,
24834
+ command: command,
24835
+ movedText: movedText,
24836
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24837
+ rangeParams: rangeParams,
24838
+ selection: getSelectionSnapshot(this.editableDiv)
24839
+ });
24840
+ return true;
24841
+ }
24842
+ }, {
24843
+ key: "applyCurrentEditableDomToComponent",
24844
+ value: function applyCurrentEditableDomToComponent() {
24845
+ var rangeParams = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24846
+ if (!this.editableDiv) return;
24847
+ if (rangeParams === null || rangeParams === undefined) {
24848
+ rangeParams = this._getRangeParams();
24849
+ }
24850
+ if (rangeParams === null || rangeParams === undefined) return;
24851
+ var rawText = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
24852
+ rawText = this.stripEmptyInlineFormattingTags(rawText);
24853
+ var nodeCounts = this._countNodeTypes();
24854
+ var hasMath = /<math>/i.test(rawText) || nodeCounts.math > 0;
24855
+ var hasHtml = nodeCounts.html > 0 || /<[^>]+>/i.test(rawText);
24856
+ var mimeType = this.getMimeType(hasHtml, hasMath);
24857
+ rawText = removeEncodingIfPlainText(rawText, mimeType, this.enableHtml());
24858
+ rawText = this.stripEmptyInlineFormattingTags(rawText);
24859
+ this.applyChangesToComponent(rawText, mimeType, rangeParams, this.props.useExpertMode, this.props.selectedTab, {
24860
+ skipControlledRender: true
24861
+ });
24862
+ }
24144
24863
  }, {
24145
24864
  key: "_getRangeParams",
24146
24865
  value: function _getRangeParams() {
24147
24866
  var editableDiv = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
24148
24867
  if (editableDiv === null) editableDiv = this.editableDiv;
24149
24868
  var rangeParams = getRangeParams(editableDiv);
24869
+ debugMathRichInput("MathRichInput:_getRangeParams", {
24870
+ rangeParams: rangeParams,
24871
+ selection: getSelectionSnapshot(editableDiv)
24872
+ });
24150
24873
  return rangeParams;
24151
24874
  }
24152
24875
  }, {
@@ -24175,6 +24898,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24175
24898
  value: function componentDidMount() {
24176
24899
  try {
24177
24900
  setupKatex();
24901
+ debugMathRichInput("componentDidMount", {
24902
+ propsValue: this.props.value,
24903
+ propsMimeType: this.props.mimeType,
24904
+ autofocus: this.props.autofocus,
24905
+ autoFocus: this.props.autoFocus,
24906
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24907
+ selection: getSelectionSnapshot(this.editableDiv)
24908
+ });
24178
24909
  this.editableDiv.addEventListener("keydown", this.handleKeyDown);
24179
24910
  this.editableDiv.addEventListener("keyup", this.handleKeyUp);
24180
24911
  this.editableDiv.addEventListener("paste", this.handlePaste);
@@ -24196,14 +24927,70 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24196
24927
  console.error(error);
24197
24928
  }
24198
24929
  }
24930
+ }, {
24931
+ key: "shouldComponentUpdate",
24932
+ value: function shouldComponentUpdate(nextProps, nextState) {
24933
+ if (this.skipNextControlledValueRender === true) {
24934
+ var canSkip = nextProps.value === this.skipNextControlledValue && nextState === this.state;
24935
+ debugMathRichInput("shouldComponentUpdate:native-input-gate", {
24936
+ canSkip: canSkip,
24937
+ nextValue: nextProps.value,
24938
+ skipNextControlledValue: this.skipNextControlledValue,
24939
+ currentPropsValue: this.props.value,
24940
+ stateChanged: nextState !== this.state,
24941
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24942
+ selection: getSelectionSnapshot(this.editableDiv)
24943
+ });
24944
+ this.skipNextControlledValueRender = false;
24945
+ this.skipNextControlledValue = null;
24946
+ if (canSkip) return false;
24947
+ }
24948
+ if (this.shouldSkipRedundantFocusedRender(nextProps, nextState)) {
24949
+ debugMathRichInput("shouldComponentUpdate:skip-redundant-focused-render", {
24950
+ nextValue: nextProps.value,
24951
+ currentPropsValue: this.props.value,
24952
+ selection: getSelectionSnapshot(this.editableDiv)
24953
+ });
24954
+ return false;
24955
+ }
24956
+ return true;
24957
+ }
24199
24958
  }, {
24200
24959
  key: "componentDidUpdate",
24201
24960
  value: function componentDidUpdate() {
24202
24961
  try {
24962
+ debugMathRichInput("componentDidUpdate:start", {
24963
+ propsValue: this.props.value,
24964
+ propsMimeType: this.props.mimeType,
24965
+ afterComponentUpdateData: this.afterComponentUpdateData,
24966
+ hasFocus: this.state.hasFocus,
24967
+ showEquationEditor: this.state.showEquationEditor,
24968
+ innerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
24969
+ selection: getSelectionSnapshot(this.editableDiv)
24970
+ });
24203
24971
  if (this.afterComponentUpdateData !== null && this.afterComponentUpdateData !== undefined && this.afterComponentUpdateData.value === this.props.value) {
24972
+ debugMathRichInput("componentDidUpdate:restore-before", {
24973
+ rangeParams: this.afterComponentUpdateData.rangeParams,
24974
+ selection: getSelectionSnapshot(this.editableDiv)
24975
+ });
24204
24976
  this._setRangeParams(this.afterComponentUpdateData.rangeParams);
24205
24977
  this.afterComponentUpdateData = null;
24206
24978
  this.updateActiveButtons();
24979
+ debugMathRichInput("componentDidUpdate:restore-after", {
24980
+ selection: getSelectionSnapshot(this.editableDiv)
24981
+ });
24982
+ }
24983
+ if (this.preserveNativeDomRangeParams !== null && this.preserveNativeDomRangeParams !== undefined && this.state.hasFocus === true) {
24984
+ var rangeParams = this.preserveNativeDomRangeParams;
24985
+ this.preserveNativeDomRangeParams = null;
24986
+ debugMathRichInput("componentDidUpdate:preserve-restore-before", {
24987
+ rangeParams: rangeParams,
24988
+ selection: getSelectionSnapshot(this.editableDiv)
24989
+ });
24990
+ this._setRangeParams(rangeParams);
24991
+ debugMathRichInput("componentDidUpdate:preserve-restore-after", {
24992
+ selection: getSelectionSnapshot(this.editableDiv)
24993
+ });
24207
24994
  }
24208
24995
  } catch (error) {
24209
24996
  console.error(error);
@@ -24216,20 +25003,31 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24216
25003
  if (rangeParams === null || rangeParams === undefined) rangeParams = this._getRangeParams();
24217
25004
  if (rangeParams.startOffset !== 0) console.warn("Unexpected startOffset " + rangeParams.startOffset + " in insertAfterSmallSpace");
24218
25005
  var node = this._findNodeWithIndex(rangeParams.startNodeIndex);
24219
- var raw_text = elementToMarkedRawText(this.editableDiv, node, 1,
24220
- // after small space
24221
- this.enableHtml());
24222
- var new_raw_text = removeMarks(insertCharacterBeforeMarks(raw_text, new_char));
25006
+ if (node === null || node === undefined || node.nodeValue === null) {
25007
+ console.warn("Could not find small-space text node");
25008
+ return;
25009
+ }
25010
+
25011
+ // The initial empty editor contains a hidden zero-width space. Handling
25012
+ // the first printable key by re-rendering through React replaces the
25013
+ // text node and drops Chrome's selection to the start under React 19.
25014
+ // Mutate the already-focused DOM node, then skip the matching controlled
25015
+ // render exactly like the normal contenteditable input path.
25016
+ node.nodeValue = node.nodeValue.substring(0, 1) + new_char + node.nodeValue.substring(1);
24223
25017
  var new_rangeParams = {
24224
25018
  startNodeIndex: rangeParams.startNodeIndex,
24225
- startNodeOffset: 1 + new_char.length,
25019
+ startOffset: 1 + new_char.length,
24226
25020
  endNodeIndex: rangeParams.startNodeIndex,
24227
- endNodeOffset: 1 + new_char.length,
25021
+ endOffset: 1 + new_char.length,
24228
25022
  startGlobalOffset: rangeParams.startGlobalOffset + new_char.length,
24229
25023
  endGlobalOffset: rangeParams.endGlobalOffset + new_char.length
24230
25024
  };
25025
+ this._setRangeParams(new_rangeParams);
25026
+ var new_raw_text = elementToMarkedRawText(this.editableDiv, null, 0, this.enableHtml());
24231
25027
  new_raw_text = removeEncodingIfPlainText(new_raw_text, this.props.mimeType, this.enableHtml());
24232
- this.applyChangesToComponent(new_raw_text, this.props.mimeType, new_rangeParams, this.props.useExpertMode, this.props.selectedTab);
25028
+ this.applyChangesToComponent(new_raw_text, this.props.mimeType, new_rangeParams, this.props.useExpertMode, this.props.selectedTab, {
25029
+ skipControlledRender: true
25030
+ });
24233
25031
  } catch (error) {
24234
25032
  console.error(error);
24235
25033
  }
@@ -24243,6 +25041,23 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24243
25041
  console.log(this.props);
24244
25042
  }
24245
25043
  var html = rawTextToHtml(katex$1, this.props.value || "", this.props.mimeType);
25044
+ var editableHtml = this.getEditableHtmlForRender(html);
25045
+ debugMathRichInput("render", {
25046
+ propsValue: this.props.value,
25047
+ propsMimeType: this.props.mimeType,
25048
+ html: html,
25049
+ editableHtml: editableHtml,
25050
+ state: {
25051
+ hasFocus: this.state.hasFocus,
25052
+ showEquationEditor: this.state.showEquationEditor,
25053
+ showAccentBar: this.state.showAccentBar,
25054
+ keyPressed: this.state.keyPressed
25055
+ },
25056
+ afterComponentUpdateData: this.afterComponentUpdateData,
25057
+ oldRangeParams: this.oldRangeParams,
25058
+ editableInnerHTML: this.editableDiv ? this.editableDiv.innerHTML : null,
25059
+ selection: getSelectionSnapshot(this.editableDiv)
25060
+ });
24246
25061
 
24247
25062
  // Debug render math
24248
25063
  // if (this.props.value && this.props.value.includes("<math>")) {
@@ -24301,7 +25116,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24301
25116
  onCompositionStart: this.handleCompositionStart,
24302
25117
  onCompositionEnd: this.handleCompositionEnd,
24303
25118
  dangerouslySetInnerHTML: {
24304
- __html: html
25119
+ __html: editableHtml
24305
25120
  },
24306
25121
  "data-placeholder": this.props.placeholder || ""
24307
25122
  }))), this.state.showEquationEditor && /*#__PURE__*/React__default["default"].createElement(EquationEditorModal, {
@@ -24320,7 +25135,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
24320
25135
  function getAllTextNodes(node) {
24321
25136
  var nodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
24322
25137
  if (node === null) {
24323
- console.warn("Deverloper supplied a null node. Ignoring");
25138
+ console.warn("Developer supplied a null node. Ignoring");
24324
25139
  return nodes;
24325
25140
  }
24326
25141
  if (nodes === null) {
@@ -24345,8 +25160,9 @@ function getAllTextNodes(node) {
24345
25160
  return nodes;
24346
25161
  }
24347
25162
  function renderMathInNode(node) {
25163
+ if (node === null || node === undefined) return;
24348
25164
  try {
24349
- var textNodes = getAllTextNodes(node);
25165
+ var textNodes = getAllTextNodes(node, []);
24350
25166
  for (var i = 0; i < textNodes.length; i++) {
24351
25167
  try {
24352
25168
  var text = textNodes[i].nodeValue;