@zzish/math-rich-input 0.1.48 → 0.1.50

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
@@ -18845,7 +18845,6 @@ var EquationEditor = /*#__PURE__*/function (_React$Component) {
18845
18845
  });
18846
18846
  });
18847
18847
  _defineProperty(_this, "handleKeyDown", function (e) {
18848
- // console.log(e.key)
18849
18848
  try {
18850
18849
  if (!_this.props.useExpertMode && e.key === "Enter") {
18851
18850
  _this.handleInsertCallback(_this.state.latex);
@@ -19108,7 +19107,6 @@ var EquationEditor = /*#__PURE__*/function (_React$Component) {
19108
19107
  key: "mathquillDidMount",
19109
19108
  value: function mathquillDidMount(mathQuill) {
19110
19109
  this.mathQuill = mathQuill;
19111
- mathQuill.focus();
19112
19110
  }
19113
19111
  }, {
19114
19112
  key: "componentDidMount",
@@ -19309,6 +19307,33 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19309
19307
  _defineProperty(_this, "handleClose", function () {
19310
19308
  _this.handleCloseCallback();
19311
19309
  });
19310
+ _defineProperty(_this, "focusEquationEditor", function () {
19311
+ console.log("🔍 Focusing equation editor...");
19312
+ if (_this.equationEditor) {
19313
+ console.log("🔍 EquationEditor ref found");
19314
+ if (_this.state.useExpertMode) {
19315
+ // Focus textarea in expert mode
19316
+ console.log("🔍 Expert mode - focusing latexInput");
19317
+ if (_this.equationEditor.latexInput) {
19318
+ _this.equationEditor.latexInput.focus();
19319
+ console.log("✅ LatexInput focused");
19320
+ } else {
19321
+ console.log("❌ LatexInput not found");
19322
+ }
19323
+ } else {
19324
+ // Focus MathQuill in normal mode
19325
+ console.log("🔍 Normal mode - focusing mathQuill");
19326
+ if (_this.equationEditor.mathQuill) {
19327
+ _this.equationEditor.mathQuill.focus();
19328
+ console.log("✅ MathQuill focused");
19329
+ } else {
19330
+ console.log("❌ MathQuill not found");
19331
+ }
19332
+ }
19333
+ } else {
19334
+ console.log("❌ EquationEditor ref not found");
19335
+ }
19336
+ });
19312
19337
  _this.state = {
19313
19338
  useExpertMode: GLOBAL_lastSelectedExpertMode
19314
19339
  };
@@ -19319,7 +19344,25 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19319
19344
  _inherits(EquationEditorModal, _React$Component);
19320
19345
  return _createClass(EquationEditorModal, [{
19321
19346
  key: "componentDidMount",
19322
- value: function componentDidMount() {}
19347
+ value: function componentDidMount() {
19348
+ var _this2 = this;
19349
+ // Auto-focus when modal opens
19350
+ // Use setTimeout to ensure child components are fully mounted
19351
+ setTimeout(function () {
19352
+ _this2.focusEquationEditor();
19353
+ }, 100);
19354
+ }
19355
+ }, {
19356
+ key: "componentDidUpdate",
19357
+ value: function componentDidUpdate(prevProps, prevState) {
19358
+ var _this3 = this;
19359
+ // Re-focus when expert mode changes
19360
+ if (prevState.useExpertMode !== this.state.useExpertMode) {
19361
+ setTimeout(function () {
19362
+ _this3.focusEquationEditor();
19363
+ }, 100);
19364
+ }
19365
+ }
19323
19366
  }, {
19324
19367
  key: "isMobile",
19325
19368
  value: function isMobile() {
@@ -19336,7 +19379,7 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19336
19379
  }, {
19337
19380
  key: "render",
19338
19381
  value: function render() {
19339
- var _this2 = this;
19382
+ var _this4 = this;
19340
19383
  var useClass = "EquationEditorModal";
19341
19384
  if (this.isMobile()) useClass = "EquationEditorModal-mobile";
19342
19385
  return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
@@ -19345,7 +19388,7 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19345
19388
  }), /*#__PURE__*/React__default["default"].createElement("div", {
19346
19389
  className: useClass,
19347
19390
  ref: function ref(r) {
19348
- return _this2.modalDiv = r;
19391
+ return _this4.modalDiv = r;
19349
19392
  }
19350
19393
  }, !this.isMobile() && /*#__PURE__*/React__default["default"].createElement("div", {
19351
19394
  className: "title"
@@ -19356,17 +19399,17 @@ var EquationEditorModal = /*#__PURE__*/function (_React$Component) {
19356
19399
  }, /*#__PURE__*/React__default["default"].createElement("input", {
19357
19400
  type: "checkbox",
19358
19401
  ref: function ref(r) {
19359
- return _this2.expertModeSwitch = r;
19402
+ return _this4.expertModeSwitch = r;
19360
19403
  },
19361
19404
  onChange: function onChange(e) {
19362
- return _this2.handleExpertModeSwitchChange(e);
19405
+ return _this4.handleExpertModeSwitchChange(e);
19363
19406
  },
19364
19407
  checked: this.state.useExpertMode
19365
19408
  }), /*#__PURE__*/React__default["default"].createElement("span", {
19366
19409
  className: "slider round"
19367
19410
  })))), /*#__PURE__*/React__default["default"].createElement(EquationEditor, {
19368
19411
  ref: function ref(r) {
19369
- return _this2.equationEditor = r;
19412
+ return _this4.equationEditor = r;
19370
19413
  },
19371
19414
  latex: this.props.latex,
19372
19415
  useExpertMode: this.state.useExpertMode,
@@ -20503,7 +20546,7 @@ var BLOCK_TEX_REG_EXP = /\$\$[\s\S]+?\$\$|\\\[[\s\S]+?\\\]/g;
20503
20546
 
20504
20547
  // const HTML_MATH_REG_EXP = /<math>.*?<\/math>/gi;
20505
20548
  var HTML_MATH_REG_EXP = /<math>[\s\S]*?<\/math>/gi;
20506
- var SPAN_OPEN_BASE = "<span class=\"base\">";
20549
+ var SPAN_OPEN_BASE = '<span class="base">';
20507
20550
  var SPAN_CLOSE = "</span>";
20508
20551
  var MARK$1 = String.fromCharCode(0);
20509
20552
  // const MARK = "_M_A_R_K_"
@@ -20642,11 +20685,25 @@ function getLatex(node) {
20642
20685
  // Example:
20643
20686
  // <annotation encoding="application/x-tex">L' = {L}{\sqrt{1-\frac{v^2}{c^2}}}</annotation>
20644
20687
  var annotation = getFirstDescendentByTagName(node, "annotation");
20688
+
20689
+ // Defensive checks for corrupt KaTeX nodes
20690
+ if (!annotation) {
20691
+ console.warn("KaTeX node missing annotation element");
20692
+ return "error";
20693
+ }
20694
+ if (!annotation.childNodes || annotation.childNodes.length === 0) {
20695
+ console.warn("KaTeX annotation element has no child nodes");
20696
+ return "error";
20697
+ }
20698
+ if (!annotation.childNodes[0] || !annotation.childNodes[0].nodeValue) {
20699
+ console.warn("KaTeX annotation missing text content");
20700
+ return "error";
20701
+ }
20645
20702
  var latex = annotation.childNodes[0].nodeValue;
20646
20703
  return latex;
20647
20704
  } catch (error) {
20648
- console.error(error);
20649
- console.log(node);
20705
+ console.error("Error extracting LaTeX from KaTeX node:", error);
20706
+ console.log("Problematic node:", node);
20650
20707
  return "error";
20651
20708
  }
20652
20709
  }
@@ -20666,15 +20723,15 @@ var decodeFromHtml = function () {
20666
20723
  var translate_re = /&(#36|quot|apos|lt|gt|amp|#44|#58|#124);/g,
20667
20724
  translate = {
20668
20725
  // 'nbsp': String.fromCharCode(160),
20669
- '#36': '$',
20670
- 'quot': '"',
20671
- 'apos': '\'',
20672
- 'lt': '<',
20673
- 'gt': '>',
20674
- 'amp': '&',
20675
- '#44': ',',
20676
- '#58': ':',
20677
- '#124': '|'
20726
+ "#36": "$",
20727
+ quot: '"',
20728
+ apos: "'",
20729
+ lt: "<",
20730
+ gt: ">",
20731
+ amp: "&",
20732
+ "#44": ",",
20733
+ "#58": ":",
20734
+ "#124": "|"
20678
20735
  },
20679
20736
  translator = function translator($0, $1) {
20680
20737
  return translate[$1];
@@ -20766,6 +20823,11 @@ function addTextForNode(node, arrayOfText) {
20766
20823
  arrayOfText.push(getLatex(node));
20767
20824
  arrayOfText.push(useHtmlMath ? RAW_TEXT_MATH_END_TAG_HTML_MATH : RAW_TEXT_MATH_END_TAG_TEX);
20768
20825
  if (mark) arrayOfText.push(MARK$1);
20826
+ } else if (isBrNode(node)) {
20827
+ // Handle BR tags as line breaks
20828
+ if (mark) arrayOfText.push(MARK$1);
20829
+ arrayOfText.push("\n");
20830
+ if (mark) arrayOfText.push(MARK$1);
20769
20831
  } else {
20770
20832
  console.error("Can't render node in editable div: Unknown node type");
20771
20833
  console.log(node);
@@ -20794,7 +20856,12 @@ function elementToMarkedRawText(element) {
20794
20856
  var useHtmlMath = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
20795
20857
  // console.log("elementToMarkedRawText")
20796
20858
  // console.log(element.innerHTML)
20797
- if (element.innerHTML === "<p><br></p>") throw new Error("We are here");
20859
+
20860
+ // Handle empty content cases gracefully
20861
+ if (element.innerHTML === "<p><br></p>" || element.innerHTML === "<br>" || element.innerHTML === "") {
20862
+ return "";
20863
+ }
20864
+
20798
20865
  // console.log("Node to mark:")
20799
20866
  // console.log(nodeToMark)
20800
20867
  var nodes = element.childNodes;
@@ -20810,7 +20877,7 @@ function elementToMarkedRawText(element) {
20810
20877
  var raw_text = result.join("");
20811
20878
 
20812
20879
  // remove any zero-width spaces we added to the html
20813
- raw_text = raw_text.replace(SMALL_SPACE_REG_EXP, '');
20880
+ raw_text = raw_text.replace(SMALL_SPACE_REG_EXP, "");
20814
20881
  return raw_text;
20815
20882
  }
20816
20883
  function replaceMarksWithMath(markedRawText, latex) {
@@ -21134,6 +21201,9 @@ function findNodeAndOffsetForGlobalOffsetInEditableDiv(editableDiv, globalOffset
21134
21201
  console.log("Result:");
21135
21202
  console.log(result);
21136
21203
  console.log("result.offset > result.node.nodeValue.length");
21204
+ console.log("Node value: '" + result.node.nodeValue + "'");
21205
+ console.log("Node value length: " + result.node.nodeValue.length);
21206
+ console.log("Calculated offset: " + result.offset);
21137
21207
  result.offset = result.node.nodeValue.length;
21138
21208
  }
21139
21209
  return result;
@@ -21164,8 +21234,17 @@ function getRangeParams(editableDiv) {
21164
21234
  console.warn("Adjusting start pos in getRangeParams as container was of type: " + range.startContainer.nodeName);
21165
21235
  range.setStart(textNode, 0);
21166
21236
  } else {
21167
- console.error("Unable to getRangeParams. seletion.startContainer is not a text node. " + "Node type: " + range.startContainer.nodeName);
21168
- return null;
21237
+ // Handle empty content case - return default range params
21238
+ // This is normal behavior after selecting all and deleting content
21239
+ console.log("getRangeParams: Empty content detected, using default range params. " + "Node type: " + range.startContainer.nodeName);
21240
+ return {
21241
+ startNodeIndex: 0,
21242
+ startOffset: 0,
21243
+ endNodeIndex: 0,
21244
+ endOffset: 0,
21245
+ startGlobalOffset: 0,
21246
+ endGlobalOffset: 0
21247
+ };
21169
21248
  }
21170
21249
  }
21171
21250
  if (range.endContainer.nodeType !== 3) {
@@ -21174,8 +21253,17 @@ function getRangeParams(editableDiv) {
21174
21253
  console.warn("Adjusting end pos in getRangeParams as container was of type: " + range.endContainer.nodeName);
21175
21254
  range.setEnd(_textNode, 0);
21176
21255
  } else {
21177
- console.error("Unable to getRangeParams. seletion.endContainer is not a text node. " + "Node type: " + range.endContainer.nodeName);
21178
- return null;
21256
+ // Handle empty content case - return default range params
21257
+ // This is normal behavior after selecting all and deleting content
21258
+ console.log("getRangeParams: Empty content detected, using default range params. " + "Node type: " + range.endContainer.nodeName);
21259
+ return {
21260
+ startNodeIndex: 0,
21261
+ startOffset: 0,
21262
+ endNodeIndex: 0,
21263
+ endOffset: 0,
21264
+ startGlobalOffset: 0,
21265
+ endGlobalOffset: 0
21266
+ };
21179
21267
  }
21180
21268
  }
21181
21269
  // Find the index of the start and end nodes
@@ -21361,6 +21449,11 @@ function sanitiseHtmlForElement(element) {
21361
21449
  var clone = element.cloneNode(true);
21362
21450
  return clone;
21363
21451
  }
21452
+ // Preserve math tags - they should be processed as math content
21453
+ if (element.nodeType === 1 && element.tagName.toLowerCase() === "math") {
21454
+ var _clone = element.cloneNode(true);
21455
+ return _clone;
21456
+ }
21364
21457
  if (element.tagName === "HEAD" || element.tagName === "STYLE" || element.tagName === "LINK") return null;
21365
21458
  var new_element = null;
21366
21459
  if (isBIUNode(element)) {
@@ -21368,10 +21461,10 @@ function sanitiseHtmlForElement(element) {
21368
21461
  new_element = document.createElement(element.tagName);
21369
21462
  } else if (element.nodeType === 1 && (element.tagName === "LI" || element.tagName === "DT" || element.tagName === "DD" || element.tagName === "THEAD" || element.tagName === "TR" || element.tagName === "H1" || element.tagName === "H2" || element.tagName === "H3" || element.tagName === "H4" || element.tagName === "H5" || element.tagName === "H6")) {
21370
21463
  // console.log("Converting descendent: <"+element.tagName+">"+element.textContent)
21371
- new_element = document.createElement('p');
21464
+ new_element = document.createElement("p");
21372
21465
  } else {
21373
21466
  // console.log("Stripping descendent: <"+element.tagName+">"+element.textContent)
21374
- new_element = document.createElement('span');
21467
+ new_element = document.createElement("span");
21375
21468
  }
21376
21469
  if (element.hasChildNodes()) {
21377
21470
  for (var i = 0; i < element.childNodes.length; i++) {
@@ -21388,7 +21481,7 @@ var SPAN_TAG = new RegExp("<span>|</span>", "g");
21388
21481
  // const COMMENT_TAG_REG_EXP = new RegExp("(?=<!--([\s\S]*?)-->","g")
21389
21482
  // const COMMENT_TAG_REG_EXP = new RegExp("<!--[\s\S]*?-->","gm")
21390
21483
  // const MULTI_WHITE_SPACE_REG_EXP = new RegExp("\s{2,}","gm")
21391
- var KATEX_START_HTML = "<span class=\"katex\"";
21484
+ var KATEX_START_HTML = '<span class="katex"';
21392
21485
  var KATEX_END_HTML = "</span></span></span></span>";
21393
21486
  function sanitiseHtml(s) {
21394
21487
  // Remove all <br> tags (as some of these can be inside katex tags)
@@ -21396,7 +21489,7 @@ function sanitiseHtml(s) {
21396
21489
  // console.log("After removing comments:")
21397
21490
  // console.log(s)
21398
21491
  // Next, convert all elements to supported elements
21399
- var div = document.createElement('div');
21492
+ var div = document.createElement("div");
21400
21493
  div.innerHTML = s;
21401
21494
  var sanitisedElement = sanitiseHtmlForElement(div);
21402
21495
  var sanitised = sanitisedElement.innerHTML;
@@ -21483,6 +21576,81 @@ function sanitiseHtml(s) {
21483
21576
  return sanitised;
21484
21577
  }
21485
21578
 
21579
+ // Helper functions for advanced text navigation
21580
+ function isWordBoundary(_char) {
21581
+ // Word boundaries are spaces, punctuation, or start/end of string
21582
+ return !_char || /[\s\.,;:!?\-\(\)\[\]{}'"]/g.test(_char);
21583
+ }
21584
+ function findWordBoundary(text, startPos, direction) {
21585
+ // direction: 1 for forward, -1 for backward
21586
+ var pos = startPos;
21587
+ var len = text.length;
21588
+ if (direction === 1) {
21589
+ // Moving forward - find end of current word, then skip whitespace
21590
+ while (pos < len && !isWordBoundary(text[pos])) {
21591
+ pos++;
21592
+ }
21593
+ while (pos < len && isWordBoundary(text[pos])) {
21594
+ pos++;
21595
+ }
21596
+ } else {
21597
+ // Moving backward - find start of current word, then skip whitespace
21598
+ if (pos > 0) pos--; // Move back one character first
21599
+ while (pos > 0 && isWordBoundary(text[pos])) {
21600
+ pos--;
21601
+ }
21602
+ while (pos > 0 && !isWordBoundary(text[pos - 1])) {
21603
+ pos--;
21604
+ }
21605
+ }
21606
+ return Math.max(0, Math.min(pos, len));
21607
+ }
21608
+ function findLineBoundary(text, startPos, direction) {
21609
+ // direction: 1 for end of line, -1 for start of line
21610
+ var pos = startPos;
21611
+ var len = text.length;
21612
+ if (direction === 1) {
21613
+ // Find end of current line
21614
+ while (pos < len && text[pos] !== "\n") {
21615
+ pos++;
21616
+ }
21617
+ } else {
21618
+ // Find start of current line
21619
+ while (pos > 0 && text[pos - 1] !== "\n") {
21620
+ pos--;
21621
+ }
21622
+ }
21623
+ return Math.max(0, Math.min(pos, len));
21624
+ }
21625
+ function getPlainTextFromEditableDiv(editableDiv) {
21626
+ // Convert the editable div content to plain text for boundary detection
21627
+ var text = elementToMarkedRawText(editableDiv, null, 0, false);
21628
+ text = removeMarks(text);
21629
+ // Remove HTML tags and convert to plain text
21630
+ text = text.replace(/<[^>]*>/g, "");
21631
+ text = text.replace(/&nbsp;/g, " ");
21632
+ text = text.replace(/&amp;/g, "&");
21633
+ text = text.replace(/&lt;/g, "<");
21634
+ text = text.replace(/&gt;/g, ">");
21635
+ text = text.replace(/&quot;/g, '"');
21636
+ text = text.replace(/&#39;/g, "'");
21637
+ return text;
21638
+ }
21639
+ function globalOffsetToPlainTextOffset(editableDiv, globalOffset) {
21640
+ // Convert global offset to plain text offset for boundary detection
21641
+ var plainText = getPlainTextFromEditableDiv(editableDiv);
21642
+ // This is a simplified mapping - in a real implementation you'd want
21643
+ // to account for the difference between HTML and plain text offsets
21644
+ return Math.min(globalOffset, plainText.length);
21645
+ }
21646
+ function plainTextOffsetToGlobalOffset(editableDiv, plainTextOffset) {
21647
+ // Convert plain text offset back to global offset
21648
+ // This is a simplified mapping - in a real implementation you'd want
21649
+ // to account for the difference between HTML and plain text offsets
21650
+ var plainText = getPlainTextFromEditableDiv(editableDiv);
21651
+ return Math.min(plainTextOffset, plainText.length);
21652
+ }
21653
+
21486
21654
  var MIME_TYPE_PLAIN_TEXT = "text/plain";
21487
21655
  var MIME_TYPE_HTML = "text/html";
21488
21656
  var MIME_TYPE_TEX = "application/x-tex";
@@ -21652,6 +21820,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21652
21820
  });
21653
21821
  if (_this2.undoHistory.length > MAX_HISTORY_LENGTH) _this2.undoHistory.shift();
21654
21822
 
21823
+ // Debug what's being passed to parent
21824
+ // console.log("APPLY CHANGES:", { value, mimeType });
21825
+
21655
21826
  // Call the parent handler to apply the changes
21656
21827
  _this2.onChangeCallback({
21657
21828
  value: value,
@@ -21708,7 +21879,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21708
21879
  _this2.lastSetRangeParmas = params;
21709
21880
  } catch (error) {
21710
21881
  console.error(error);
21711
- console.log(_this2.editableDiv.childNodes);
21882
+ // console.log(this.editableDiv.childNodes);
21712
21883
  }
21713
21884
  });
21714
21885
  _defineProperty(_this2, "handleKeyDownBackspace", function (e) {
@@ -21825,47 +21996,317 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
21825
21996
  _defineProperty(_this2, "inputStartsWithSmallSpace", function () {
21826
21997
  return startsWithSmallSpace(_this2.editableDiv);
21827
21998
  });
21999
+ // Platform detection helper
22000
+ _defineProperty(_this2, "getPlatformModifiers", function () {
22001
+ var isMac = /(Mac)/i.test(window.navigator.platform);
22002
+ var isWindows = /(Win)/i.test(window.navigator.platform);
22003
+ return {
22004
+ isMac: isMac,
22005
+ isWindows: isWindows
22006
+ };
22007
+ });
22008
+ // Check if word navigation modifier is pressed
22009
+ _defineProperty(_this2, "isWordModifier", function (e) {
22010
+ var _this2$getPlatformMod = _this2.getPlatformModifiers(),
22011
+ isMac = _this2$getPlatformMod.isMac;
22012
+ return isMac ? e.altKey : e.ctrlKey;
22013
+ });
22014
+ // Check if line navigation modifier is pressed
22015
+ _defineProperty(_this2, "isLineModifier", function (e) {
22016
+ var _this2$getPlatformMod2 = _this2.getPlatformModifiers(),
22017
+ isMac = _this2$getPlatformMod2.isMac;
22018
+ return isMac ? e.metaKey : e.ctrlKey;
22019
+ });
21828
22020
  _defineProperty(_this2, "handleKeyDownArrowLeft", function (e) {
21829
22021
  var rangeParams = _this2._getRangeParams();
21830
- rangeParams.startNodeIndex;
21831
- rangeParams.startOffset;
22022
+ var hasSelection = rangeParams.startGlobalOffset !== rangeParams.endGlobalOffset;
21832
22023
 
21833
- // console.log("handleKeyDownArrowLeft")
21834
- // console.log(rangeParams)
21835
- var new_globalOffset = rangeParams.endGlobalOffset - 1;
22024
+ // If has selection and no shift, move cursor to LEFT end of selection
22025
+ if (!e.shiftKey && hasSelection) {
22026
+ _this2.selectionAnchor = null;
22027
+ var _new_rangeParams2 = {
22028
+ startGlobalOffset: rangeParams.startGlobalOffset,
22029
+ endGlobalOffset: rangeParams.startGlobalOffset
22030
+ };
22031
+ e.preventDefault();
22032
+ _this2._setRangeParams(_new_rangeParams2);
22033
+ return;
22034
+ }
22035
+
22036
+ // For left arrow, current position is at the start of selection (or cursor position)
22037
+ var currentGlobalOffset = hasSelection ? rangeParams.startGlobalOffset : rangeParams.endGlobalOffset;
22038
+ var newGlobalOffset = currentGlobalOffset;
22039
+
22040
+ // Determine movement type based on modifier keys
22041
+ if (_this2.isLineModifier(e)) {
22042
+ // Move to beginning of line
22043
+ var plainText = getPlainTextFromEditableDiv(_this2.editableDiv);
22044
+ var plainTextOffset = globalOffsetToPlainTextOffset(_this2.editableDiv, currentGlobalOffset);
22045
+ var lineStart = findLineBoundary(plainText, plainTextOffset, -1);
22046
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, lineStart);
22047
+ } else if (_this2.isWordModifier(e)) {
22048
+ // Move to beginning of word
22049
+ var _plainText = getPlainTextFromEditableDiv(_this2.editableDiv);
22050
+ var _plainTextOffset = globalOffsetToPlainTextOffset(_this2.editableDiv, currentGlobalOffset);
22051
+ var wordStart = findWordBoundary(_plainText, _plainTextOffset, -1);
22052
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, wordStart);
22053
+ } else {
22054
+ // Regular left arrow: Move one character
22055
+ newGlobalOffset = Math.max(0, currentGlobalOffset - 1);
22056
+ }
22057
+
22058
+ // Handle selection based on shift key
21836
22059
  var new_rangeParams = null;
21837
- if (e.shiftKey && new_globalOffset > rangeParams.startGlobalOffset) {
22060
+ if (e.shiftKey) {
22061
+ // Set anchor if not already set
22062
+ if (_this2.selectionAnchor === null) {
22063
+ // Determine which end was the cursor position
22064
+ if (hasSelection) {
22065
+ // If we had a selection, we need to determine which end the cursor was at
22066
+ // For left arrow, we want to extend from the right end
22067
+ _this2.selectionAnchor = rangeParams.endGlobalOffset;
22068
+ } else {
22069
+ // No previous selection, current position becomes anchor
22070
+ _this2.selectionAnchor = currentGlobalOffset;
22071
+ }
22072
+ }
22073
+
22074
+ // Create selection from anchor to new position
21838
22075
  new_rangeParams = {
21839
- startGlobalOffset: rangeParams.startGlobalOffset,
21840
- endGlobalOffset: new_globalOffset
22076
+ startGlobalOffset: Math.min(_this2.selectionAnchor, newGlobalOffset),
22077
+ endGlobalOffset: Math.max(_this2.selectionAnchor, newGlobalOffset)
21841
22078
  };
21842
22079
  } else {
22080
+ // Move cursor and clear anchor
22081
+ _this2.selectionAnchor = null;
21843
22082
  new_rangeParams = {
21844
- startGlobalOffset: new_globalOffset,
21845
- endGlobalOffset: new_globalOffset
22083
+ startGlobalOffset: newGlobalOffset,
22084
+ endGlobalOffset: newGlobalOffset
21846
22085
  };
21847
22086
  }
21848
- // console.log(new_rangeParams)
21849
22087
  e.preventDefault();
21850
22088
  _this2._setRangeParams(new_rangeParams);
21851
22089
  return;
21852
22090
  });
21853
22091
  _defineProperty(_this2, "handleKeyDownArrowRight", function (e) {
21854
- // console.log("handleKeyDownArrowRight")
21855
22092
  var rangeParams = _this2._getRangeParams();
21856
- rangeParams.startNodeIndex;
21857
- rangeParams.startOffset;
22093
+ var hasSelection = rangeParams.startGlobalOffset !== rangeParams.endGlobalOffset;
22094
+
22095
+ // If has selection and no shift, move cursor to RIGHT end of selection
22096
+ if (!e.shiftKey && hasSelection) {
22097
+ _this2.selectionAnchor = null;
22098
+ var _new_rangeParams3 = {
22099
+ startGlobalOffset: rangeParams.endGlobalOffset,
22100
+ endGlobalOffset: rangeParams.endGlobalOffset
22101
+ };
22102
+ e.preventDefault();
22103
+ _this2._setRangeParams(_new_rangeParams3);
22104
+ return;
22105
+ }
22106
+
22107
+ // For right arrow, current position is at the end of selection (or cursor position)
22108
+ var currentGlobalOffset = rangeParams.endGlobalOffset;
22109
+ var newGlobalOffset = currentGlobalOffset;
22110
+
22111
+ // Determine movement type based on modifier keys
22112
+ if (_this2.isLineModifier(e)) {
22113
+ // Move to end of line
22114
+ var plainText = getPlainTextFromEditableDiv(_this2.editableDiv);
22115
+ var plainTextOffset = globalOffsetToPlainTextOffset(_this2.editableDiv, currentGlobalOffset);
22116
+ var lineEnd = findLineBoundary(plainText, plainTextOffset, 1);
22117
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, lineEnd);
22118
+ } else if (_this2.isWordModifier(e)) {
22119
+ // Move to end of word
22120
+ var _plainText2 = getPlainTextFromEditableDiv(_this2.editableDiv);
22121
+ var _plainTextOffset2 = globalOffsetToPlainTextOffset(_this2.editableDiv, currentGlobalOffset);
22122
+ var wordEnd = findWordBoundary(_plainText2, _plainTextOffset2, 1);
22123
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, wordEnd);
22124
+ } else {
22125
+ // Regular right arrow: Move one character
22126
+ newGlobalOffset = currentGlobalOffset + 1;
22127
+ }
22128
+
22129
+ // Handle selection based on shift key
21858
22130
  var new_rangeParams = null;
21859
- var new_globalOffset = rangeParams.endGlobalOffset + 1;
21860
- if (e.shiftKey && new_globalOffset > rangeParams.startGlobalOffset) {
22131
+ if (e.shiftKey) {
22132
+ // Set anchor if not already set
22133
+ if (_this2.selectionAnchor === null) {
22134
+ // Determine which end was the cursor position
22135
+ if (hasSelection) {
22136
+ // If we had a selection, we need to determine which end the cursor was at
22137
+ // For right arrow, we want to extend from the left end
22138
+ _this2.selectionAnchor = rangeParams.startGlobalOffset;
22139
+ } else {
22140
+ // No previous selection, current position becomes anchor
22141
+ _this2.selectionAnchor = currentGlobalOffset;
22142
+ }
22143
+ }
22144
+
22145
+ // Create selection from anchor to new position
22146
+ new_rangeParams = {
22147
+ startGlobalOffset: Math.min(_this2.selectionAnchor, newGlobalOffset),
22148
+ endGlobalOffset: Math.max(_this2.selectionAnchor, newGlobalOffset)
22149
+ };
22150
+ } else {
22151
+ // Move cursor and clear anchor
22152
+ _this2.selectionAnchor = null;
21861
22153
  new_rangeParams = {
22154
+ startGlobalOffset: newGlobalOffset,
22155
+ endGlobalOffset: newGlobalOffset
22156
+ };
22157
+ }
22158
+ e.preventDefault();
22159
+ _this2._setRangeParams(new_rangeParams);
22160
+ return;
22161
+ });
22162
+ _defineProperty(_this2, "handleKeyDownArrowUp", function (e) {
22163
+ var rangeParams = _this2._getRangeParams();
22164
+ var hasSelection = rangeParams.startGlobalOffset !== rangeParams.endGlobalOffset;
22165
+
22166
+ // If no selection and no shift, just move cursor to start of selection (if any)
22167
+ if (!e.shiftKey && hasSelection) {
22168
+ _this2.selectionAnchor = null;
22169
+ var _new_rangeParams4 = {
21862
22170
  startGlobalOffset: rangeParams.startGlobalOffset,
21863
- endGlobalOffset: new_globalOffset
22171
+ endGlobalOffset: rangeParams.startGlobalOffset
22172
+ };
22173
+ e.preventDefault();
22174
+ _this2._setRangeParams(_new_rangeParams4);
22175
+ return;
22176
+ }
22177
+
22178
+ // For up arrow (backward movement), current position is at the start of selection
22179
+ var currentGlobalOffset = hasSelection ? rangeParams.startGlobalOffset : rangeParams.endGlobalOffset;
22180
+ var newGlobalOffset = currentGlobalOffset;
22181
+
22182
+ // Determine movement type based on modifier keys
22183
+ if (_this2.isLineModifier(e)) {
22184
+ // Move to beginning of document
22185
+ newGlobalOffset = 0;
22186
+ } else {
22187
+ // Regular up arrow: Move to previous line (or beginning if single line)
22188
+ var plainText = getPlainTextFromEditableDiv(_this2.editableDiv);
22189
+ var plainTextOffset = globalOffsetToPlainTextOffset(_this2.editableDiv, currentGlobalOffset);
22190
+ var lineStart = findLineBoundary(plainText, plainTextOffset, -1);
22191
+ if (lineStart === 0) {
22192
+ // Already at first line, move to beginning
22193
+ newGlobalOffset = 0;
22194
+ } else {
22195
+ // Find previous line and try to maintain column position
22196
+ var currentColumn = plainTextOffset - lineStart;
22197
+ var prevLineEnd = lineStart - 1;
22198
+ var prevLineStart = findLineBoundary(plainText, prevLineEnd, -1);
22199
+ var prevLineLength = prevLineEnd - prevLineStart;
22200
+ var targetColumn = Math.min(currentColumn, prevLineLength);
22201
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, prevLineStart + targetColumn);
22202
+ }
22203
+ }
22204
+
22205
+ // Handle selection based on shift key
22206
+ var new_rangeParams = null;
22207
+ if (e.shiftKey) {
22208
+ // Set anchor if not already set
22209
+ if (_this2.selectionAnchor === null) {
22210
+ // Determine which end was the cursor position
22211
+ if (hasSelection) {
22212
+ // If we had a selection, we need to determine which end the cursor was at
22213
+ // For up arrow (backward), we want to extend from the right end
22214
+ _this2.selectionAnchor = rangeParams.endGlobalOffset;
22215
+ } else {
22216
+ // No previous selection, current position becomes anchor
22217
+ _this2.selectionAnchor = currentGlobalOffset;
22218
+ }
22219
+ }
22220
+
22221
+ // Create selection from anchor to new position
22222
+ new_rangeParams = {
22223
+ startGlobalOffset: Math.min(_this2.selectionAnchor, newGlobalOffset),
22224
+ endGlobalOffset: Math.max(_this2.selectionAnchor, newGlobalOffset)
21864
22225
  };
21865
22226
  } else {
22227
+ // Move cursor and clear anchor
22228
+ _this2.selectionAnchor = null;
21866
22229
  new_rangeParams = {
21867
- startGlobalOffset: new_globalOffset,
21868
- endGlobalOffset: new_globalOffset
22230
+ startGlobalOffset: newGlobalOffset,
22231
+ endGlobalOffset: newGlobalOffset
22232
+ };
22233
+ }
22234
+ e.preventDefault();
22235
+ _this2._setRangeParams(new_rangeParams);
22236
+ return;
22237
+ });
22238
+ _defineProperty(_this2, "handleKeyDownArrowDown", function (e) {
22239
+ var rangeParams = _this2._getRangeParams();
22240
+ var hasSelection = rangeParams.startGlobalOffset !== rangeParams.endGlobalOffset;
22241
+
22242
+ // If no selection and no shift, just move cursor to end of selection (if any)
22243
+ if (!e.shiftKey && hasSelection) {
22244
+ _this2.selectionAnchor = null;
22245
+ var _new_rangeParams5 = {
22246
+ startGlobalOffset: rangeParams.endGlobalOffset,
22247
+ endGlobalOffset: rangeParams.endGlobalOffset
22248
+ };
22249
+ e.preventDefault();
22250
+ _this2._setRangeParams(_new_rangeParams5);
22251
+ return;
22252
+ }
22253
+
22254
+ // For down arrow (forward movement), current position is at the end of selection
22255
+ var currentGlobalOffset = rangeParams.endGlobalOffset;
22256
+ var newGlobalOffset = currentGlobalOffset;
22257
+
22258
+ // Determine movement type based on modifier keys
22259
+ if (_this2.isLineModifier(e)) {
22260
+ // Move to end of document
22261
+ var plainText = getPlainTextFromEditableDiv(_this2.editableDiv);
22262
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, plainText.length);
22263
+ } else {
22264
+ // Regular down arrow: Move to next line (or end if single line)
22265
+ var _plainText3 = getPlainTextFromEditableDiv(_this2.editableDiv);
22266
+ var plainTextOffset = globalOffsetToPlainTextOffset(_this2.editableDiv, currentGlobalOffset);
22267
+ var lineStart = findLineBoundary(_plainText3, plainTextOffset, -1);
22268
+ var lineEnd = findLineBoundary(_plainText3, plainTextOffset, 1);
22269
+ if (lineEnd >= _plainText3.length) {
22270
+ // Already at last line, move to end
22271
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, _plainText3.length);
22272
+ } else {
22273
+ // Find next line and try to maintain column position
22274
+ var currentColumn = plainTextOffset - lineStart;
22275
+ var nextLineStart = lineEnd + 1;
22276
+ var nextLineEnd = findLineBoundary(_plainText3, nextLineStart, 1);
22277
+ var nextLineLength = nextLineEnd - nextLineStart;
22278
+ var targetColumn = Math.min(currentColumn, nextLineLength);
22279
+ newGlobalOffset = plainTextOffsetToGlobalOffset(_this2.editableDiv, nextLineStart + targetColumn);
22280
+ }
22281
+ }
22282
+
22283
+ // Handle selection based on shift key
22284
+ var new_rangeParams = null;
22285
+ if (e.shiftKey) {
22286
+ // Set anchor if not already set
22287
+ if (_this2.selectionAnchor === null) {
22288
+ // Determine which end was the cursor position
22289
+ if (hasSelection) {
22290
+ // If we had a selection, we need to determine which end the cursor was at
22291
+ // For down arrow (forward), we want to extend from the left end
22292
+ _this2.selectionAnchor = rangeParams.startGlobalOffset;
22293
+ } else {
22294
+ // No previous selection, current position becomes anchor
22295
+ _this2.selectionAnchor = currentGlobalOffset;
22296
+ }
22297
+ }
22298
+
22299
+ // Create selection from anchor to new position
22300
+ new_rangeParams = {
22301
+ startGlobalOffset: Math.min(_this2.selectionAnchor, newGlobalOffset),
22302
+ endGlobalOffset: Math.max(_this2.selectionAnchor, newGlobalOffset)
22303
+ };
22304
+ } else {
22305
+ // Move cursor and clear anchor
22306
+ _this2.selectionAnchor = null;
22307
+ new_rangeParams = {
22308
+ startGlobalOffset: newGlobalOffset,
22309
+ endGlobalOffset: newGlobalOffset
21869
22310
  };
21870
22311
  }
21871
22312
  e.preventDefault();
@@ -22033,7 +22474,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22033
22474
  return;
22034
22475
  }
22035
22476
  if (!(e.ctrlKey || e.metaKey)) _this2.resetHistoryRedo();
22036
- if ((e.metaKey || e.ctrlKey) && (e.key === "b" || e.key === "i" || e.key === "u" || e.key === "6" || e.key === "^" || e.key === "-" || e.key === "_" || e.key === "+" || e.key === "=") && _this2.enableHtml()) {
22477
+ if ((e.metaKey || e.ctrlKey) && (e.key === "b" || e.key === "i" || e.key === "u" || e.key === "6" || e.key === "^" || e.key === "-" || e.key === "_" || e.key === "+" || e.key === "=") && _this2.enableFontStyling()) {
22037
22478
  if (e.key === "b") _this2.styleText(e, "Bold");
22038
22479
  if (e.key === "i") _this2.styleText(e, "Italic");
22039
22480
  if (e.key === "u") _this2.styleText(e, "Underline");
@@ -22084,12 +22525,12 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22084
22525
  if (e.altKey && e.key === "Alt") {
22085
22526
  var key = _this2.fetchAccentLetter();
22086
22527
  if (key === "a" || key === "e" || key === "i" || key === "o" || key === "u" || key === "c" || key === "l" || key === "n" || key === "s" || key === "?" || key === "!") {
22087
- console.log("showing/hiding accent bar");
22528
+ // console.log("showing/hiding accent bar");
22088
22529
  _this2.setState({
22089
22530
  showAccentBar: !_this2.state.showAccentBar
22090
22531
  });
22091
22532
  } else {
22092
- console.log("hiding accent bar");
22533
+ // console.log("hiding accent bar");
22093
22534
  _this2.setState({
22094
22535
  showAccentBar: false
22095
22536
  });
@@ -22108,7 +22549,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22108
22549
  if (_this2.keyPressStartTime < 0) {
22109
22550
  _this2.keyPressStartTime = Date.now();
22110
22551
  if (_this2.state.showAccentBar) {
22111
- console.log("Hiding accent bar 2");
22552
+ // console.log("Hiding accent bar 2");
22112
22553
  _this2.setState({
22113
22554
  showAccentBar: false
22114
22555
  });
@@ -22126,6 +22567,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22126
22567
  _this2.handleKeyDownArrowRight(e);
22127
22568
  return;
22128
22569
  }
22570
+ if (e.key === "ArrowUp") {
22571
+ _this2.handleKeyDownArrowUp(e);
22572
+ return;
22573
+ }
22574
+ if (e.key === "ArrowDown") {
22575
+ _this2.handleKeyDownArrowDown(e);
22576
+ return;
22577
+ }
22129
22578
  if (e.key === "$") {
22130
22579
  _this2.handleKeyDownDollar(e);
22131
22580
  return;
@@ -22196,7 +22645,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22196
22645
  }
22197
22646
  } catch (error) {
22198
22647
  console.error(error);
22199
- console.log("Key pressed: " + (e.metaKey ? "META+" : "") + (e.ctrlKey ? "CTRL+" : "") + (e.altKey ? "ALT+" : "") + (e.shiftKey ? "SHIFT+" : "") + e.key);
22648
+ // console.log(
22649
+ // "Key pressed: " +
22650
+ // (e.metaKey ? "META+" : "") +
22651
+ // (e.ctrlKey ? "CTRL+" : "") +
22652
+ // (e.altKey ? "ALT+" : "") +
22653
+ // (e.shiftKey ? "SHIFT+" : "") +
22654
+ // e.key
22655
+ // );
22200
22656
  }
22201
22657
  });
22202
22658
  _defineProperty(_this2, "handleKeyUp", function (e) {
@@ -22256,14 +22712,14 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22256
22712
  _new_index = _index + 1;
22257
22713
  _new_offset = SMALL_SPACE_LENGTH;
22258
22714
  }
22259
- var _new_rangeParams2 = {
22715
+ var _new_rangeParams6 = {
22260
22716
  startNodeIndex: _new_index,
22261
22717
  startOffset: _new_offset,
22262
22718
  endNodeIndex: _new_index,
22263
22719
  endOffset: _new_offset
22264
22720
  };
22265
22721
  // console.log(new_rangeParams)
22266
- _this2._setRangeParams(_new_rangeParams2);
22722
+ _this2._setRangeParams(_new_rangeParams6);
22267
22723
  }
22268
22724
  }
22269
22725
  }
@@ -22278,14 +22734,56 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22278
22734
  if (_this2.isComposing) return;
22279
22735
  var raw_text = elementToMarkedRawText(_this2.editableDiv, null, 0, _this2.enableHtml());
22280
22736
  var params = _this2._getRangeParams();
22281
- if (params === null || params === undefined) console.error("Could not get range params from input field");else if (params.startNodeIndex === null || params.startNodeIndex === undefined) console.error("Could not get range params from input field, startNodeIndex===" + params.startNodeIndex);else {
22737
+ if (params === null || params === undefined) {
22738
+ // console.log(
22739
+ // "handleInput: Empty content detected, using default range params"
22740
+ // );
22741
+ // Use default empty range params for graceful degradation
22742
+ var defaultParams = {
22743
+ startNodeIndex: 0,
22744
+ startOffset: 0,
22745
+ endNodeIndex: 0,
22746
+ endOffset: 0,
22747
+ startGlobalOffset: 0,
22748
+ endGlobalOffset: 0
22749
+ };
22750
+ // Continue with default params instead of crashing
22751
+ var _nodeCounts = _this2._countNodeTypes();
22752
+ var _mimeType = _this2.getMimeType(_nodeCounts.html > 0, _nodeCounts.math > 0);
22753
+ var _raw_text = elementToMarkedRawText(_this2.editableDiv, null, 0, _this2.enableHtml());
22754
+ _raw_text = removeEncodingIfPlainText(_raw_text, _mimeType, _this2.enableHtml());
22755
+ _this2.applyChangesToComponent(_raw_text, _mimeType, defaultParams, _this2.props.useExpertMode, _this2.props.selectedTab);
22756
+ return;
22757
+ } else if (params.startNodeIndex === null || params.startNodeIndex === undefined) {
22758
+ // console.log(
22759
+ // "handleInput: Invalid startNodeIndex detected: " +
22760
+ // params.startNodeIndex
22761
+ // );
22762
+ } else {
22282
22763
  // console.log("Current range params:")
22283
22764
  // console.log(params)
22284
22765
  }
22766
+
22767
+ // Check for math content directly in raw text for more reliable detection
22768
+ var hasMathContent = /<math>/i.test(raw_text);
22285
22769
  var nodeCounts = _this2._countNodeTypes();
22286
- var mimeType = _this2.getMimeType(nodeCounts.html > 0, nodeCounts.math > 0);
22770
+
22771
+ // Use raw text math detection if DOM counting fails (e.g., after paste)
22772
+ var hasHtml = nodeCounts.html > 0 || /<[^>]+>/i.test(raw_text);
22773
+ var hasMath = hasMathContent || nodeCounts.math > 0;
22774
+ var mimeType = _this2.getMimeType(hasHtml, hasMath);
22287
22775
  raw_text = removeEncodingIfPlainText(raw_text, mimeType, _this2.enableHtml());
22288
- _this2.applyChangesToComponent(raw_text, mimeType, params, _this2.props.useExpertMode, _this2.props.selectedTab);
22776
+
22777
+ // Use valid params or fallback to defaults
22778
+ var validParams = params || {
22779
+ startNodeIndex: 0,
22780
+ startOffset: 0,
22781
+ endNodeIndex: 0,
22782
+ endOffset: 0,
22783
+ startGlobalOffset: 0,
22784
+ endGlobalOffset: 0
22785
+ };
22786
+ _this2.applyChangesToComponent(raw_text, mimeType, validParams, _this2.props.useExpertMode, _this2.props.selectedTab);
22289
22787
  } catch (error) {
22290
22788
  console.error(error);
22291
22789
  }
@@ -22295,11 +22793,39 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22295
22793
  // console.log("updateActiveButtons")
22296
22794
  var rangeParams = _this2._getRangeParams();
22297
22795
  if (rangeParams === null || rangeParams === undefined) {
22298
- console.warn("Unable to updateActiveButtons. rangeParams: " + rangeParams);
22796
+ // console.log(
22797
+ // "updateActiveButtons: Empty content detected, setting all buttons to inactive"
22798
+ // );
22799
+ // Set all buttons to inactive state for graceful degradation
22800
+ var _activeButtons = {
22801
+ bold: false,
22802
+ italic: false,
22803
+ underline: false,
22804
+ subscript: false,
22805
+ superscript: false
22806
+ };
22807
+ _this2.lastSetActiveButtons = _activeButtons;
22808
+ _this2.setState({
22809
+ activeButtons: _activeButtons
22810
+ });
22299
22811
  return;
22300
22812
  }
22301
22813
  if (rangeParams.startNodeIndex < 0) {
22302
- console.warn("Unable to updateActiveButtons. rangeParams.startNodeIndex: " + rangeParams.startNodeIndex);
22814
+ // console.log(
22815
+ // "updateActiveButtons: Empty content detected (startNodeIndex < 0), setting all buttons to inactive"
22816
+ // );
22817
+ // Set all buttons to inactive state for graceful degradation
22818
+ var _activeButtons2 = {
22819
+ bold: false,
22820
+ italic: false,
22821
+ underline: false,
22822
+ subscript: false,
22823
+ superscript: false
22824
+ };
22825
+ _this2.lastSetActiveButtons = _activeButtons2;
22826
+ _this2.setState({
22827
+ activeButtons: _activeButtons2
22828
+ });
22303
22829
  return;
22304
22830
  }
22305
22831
  var node = _this2._findNodeWithIndex(rangeParams.startNodeIndex);
@@ -22344,6 +22870,43 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22344
22870
  console.error(error);
22345
22871
  }
22346
22872
  });
22873
+ _defineProperty(_this2, "handleCopy", function (event) {
22874
+ try {
22875
+ var selection = window.getSelection();
22876
+ if (selection.rangeCount === 0) return;
22877
+ var range = selection.getRangeAt(0);
22878
+ var container = document.createElement("div");
22879
+ container.appendChild(range.cloneContents());
22880
+
22881
+ // Convert KaTeX nodes to raw LaTeX for clipboard
22882
+ var katexNodes = container.querySelectorAll(".katex");
22883
+ katexNodes.forEach(function (katexNode) {
22884
+ try {
22885
+ var latex = getLatex(katexNode);
22886
+ if (latex && latex !== "error") {
22887
+ // Replace KaTeX node with raw LaTeX in math tags
22888
+ var mathElement = document.createElement("span");
22889
+ mathElement.setAttribute("data-latex", latex);
22890
+ mathElement.textContent = "<math>".concat(latex, "</math>");
22891
+ katexNode.parentNode.replaceChild(mathElement, katexNode);
22892
+ }
22893
+ } catch (err) {
22894
+ console.warn("Failed to extract LaTeX during copy:", err);
22895
+ }
22896
+ });
22897
+
22898
+ // Set clipboard data
22899
+ var htmlContent = container.innerHTML;
22900
+ var textContent = container.textContent;
22901
+ if (event.clipboardData) {
22902
+ event.clipboardData.setData("text/html", htmlContent);
22903
+ event.clipboardData.setData("text/plain", textContent);
22904
+ event.preventDefault();
22905
+ }
22906
+ } catch (error) {
22907
+ console.error("Error during copy operation:", error);
22908
+ }
22909
+ });
22347
22910
  _defineProperty(_this2, "handlePaste", function (event) {
22348
22911
  try {
22349
22912
  var clipboardData = event.clipboardData || window.clipboardData;
@@ -22351,39 +22914,185 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22351
22914
  return;
22352
22915
  }
22353
22916
  event.preventDefault();
22354
- var pasteHtml = clipboardData.getData('text/html');
22355
- var pasteText = clipboardData.getData('text/plain');
22356
- var selection = window.getSelection();
22357
- if (!selection.rangeCount) {
22917
+ var pasteHtml = clipboardData.getData("text/html");
22918
+ var pasteText = clipboardData.getData("text/plain");
22919
+
22920
+ // Get current cursor position
22921
+ var rangeParams = _this2._getRangeParams();
22922
+ if (!rangeParams) {
22923
+ console.warn("Could not get range params for paste");
22358
22924
  return;
22359
22925
  }
22360
- selection.deleteFromDocument();
22926
+
22927
+ // Store old range params for cursor positioning (like equation editor)
22928
+ _this2.setOldRangeParams(rangeParams);
22929
+
22930
+ // Clear any existing selection
22931
+ var selection = window.getSelection();
22932
+ if (selection.rangeCount > 0) {
22933
+ selection.deleteFromDocument();
22934
+ }
22935
+ var finalText = "";
22936
+ var finalMimeType = _this2.props.mimeType || "text/html";
22937
+ var hasMathContent = false;
22938
+
22939
+ // Process HTML paste first
22361
22940
  if (pasteHtml && pasteHtml.length > 0) {
22362
- var markerId = "__PASTE_MARKER_".concat(Date.now(), "_").concat(Math.floor(Math.random() * 10000), "__");
22363
- var markerStart = "<span id='".concat(markerId, "_START'></span>");
22364
- var markerEnd = "<span id='".concat(markerId, "_END'></span>");
22365
- var markedHtml = markerStart + pasteHtml + markerEnd;
22366
- if (typeof sanitiseHtml === 'function') {
22367
- markedHtml = sanitiseHtml(markedHtml);
22941
+ // console.log("PASTE DEBUG - HTML detected:", JSON.stringify(pasteHtml));
22942
+ var processedHtml = pasteHtml;
22943
+
22944
+ // Clean unwanted elements
22945
+ processedHtml = processedHtml.replace(/<meta[^>]*>/gi, "");
22946
+ processedHtml = processedHtml.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
22947
+ processedHtml = processedHtml.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "");
22948
+ processedHtml = processedHtml.replace(/<!\x2D\x2D[\s\S]*?\x2D\x2D>/g, "");
22949
+
22950
+ // Convert data-latex attributes to math tags
22951
+ processedHtml = processedHtml.replace(/<span[^>]*data-latex="([^"]*)"[^>]*>.*?<\/span>/gi, "<math>$1</math>");
22952
+
22953
+ // Convert KaTeX nodes to math tags
22954
+ processedHtml = processedHtml.replace(/<span[^>]*class="katex"[^>]*>.*?<annotation[^>]*encoding="application\/x-tex"[^>]*>([^<]*)<\/annotation>.*?<\/span>/gi, "<math>$1</math>");
22955
+
22956
+ // Extract raw text content, keeping math tags
22957
+ var rawTextContent = processedHtml;
22958
+ rawTextContent = rawTextContent.replace(/<(?!\/?(math)>)[^>]*>/gi, "");
22959
+
22960
+ // console.log(
22961
+ // "PASTE DEBUG - Extracted raw text:",
22962
+ // JSON.stringify(rawTextContent)
22963
+ // );
22964
+
22965
+ // Apply sanitiseHtml for security (like in the old version)
22966
+ if (typeof sanitiseHtml === "function") {
22967
+ rawTextContent = sanitiseHtml(rawTextContent);
22968
+ // console.log("PASTE DEBUG - After sanitiseHtml:", JSON.stringify(rawTextContent));
22368
22969
  }
22369
- _this2._pendingPasteMarkerId = markerId;
22370
- document.execCommand('insertHTML', false, markedHtml);
22371
- setTimeout(function () {
22372
- try {
22373
- var rawText = elementToMarkedRawText(_this2.editableDiv, null, -1, _this2.enableHtml());
22374
- var nodeCounts = _this2._countNodeTypes();
22375
- var mimeType = _this2.getMimeType(nodeCounts.html > 0, nodeCounts.math > 0);
22376
- var cleanedRawText = removeEncodingIfPlainText(rawText, mimeType, _this2.enableHtml());
22377
- _this2.applyChangesToComponent(cleanedRawText, mimeType, _this2._getRangeParams(), _this2.props.useExpertMode, _this2.props.selectedTab);
22378
- } catch (err) {
22379
- console.error(err);
22970
+
22971
+ // Check if we have math content
22972
+ var hasMathTags = /<math>/i.test(rawTextContent);
22973
+ // console.log("PASTE DEBUG - Has math tags:", hasMathTags);
22974
+
22975
+ // Also check for LaTeX patterns in the extracted text (in case HTML doesn't have math tags)
22976
+ var hasLatexPatterns = _this2.detectLatexPatterns(rawTextContent);
22977
+ // console.log("PASTE DEBUG - Has LaTeX patterns:", hasLatexPatterns);
22978
+
22979
+ if ((hasMathTags || hasLatexPatterns) && _this2.enableMath()) {
22980
+ // If we found LaTeX patterns but no math tags, wrap the content
22981
+ if (!hasMathTags && hasLatexPatterns) {
22982
+ finalText = "<math>".concat(rawTextContent, "</math>");
22983
+ // console.log(
22984
+ // "PASTE DEBUG - Wrapping extracted HTML content in math tags"
22985
+ // );
22986
+ } else {
22987
+ finalText = rawTextContent;
22988
+ }
22989
+ finalMimeType = "application/x-zzish-html-math";
22990
+ hasMathContent = true;
22991
+ } else {
22992
+ finalText = rawTextContent;
22993
+ finalMimeType = "text/html";
22994
+ }
22995
+ }
22996
+ // Process plain text paste
22997
+ else if (pasteText && pasteText.length > 0) {
22998
+ // Debug paste content
22999
+ // console.log("PASTE DEBUG - Original text:", JSON.stringify(pasteText));
23000
+ // console.log("PASTE DEBUG - Text length:", pasteText.length);
23001
+
23002
+ // Clean up paste text (remove excessive newlines that might interfere with detection)
23003
+ var cleanedPasteText = pasteText.replace(/\n{3,}/g, "\n").trim();
23004
+ // console.log(
23005
+ // "PASTE DEBUG - Cleaned text:",
23006
+ // JSON.stringify(cleanedPasteText)
23007
+ // );
23008
+
23009
+ // Use comprehensive LaTeX pattern detection
23010
+ var _hasLatexPatterns = _this2.detectLatexPatterns(cleanedPasteText);
23011
+ // console.log("PASTE DEBUG - LaTeX patterns detected:", hasLatexPatterns);
23012
+
23013
+ if (_hasLatexPatterns && _this2.enableMath()) {
23014
+ // Wrap entire paste content in math tags if LaTeX patterns detected
23015
+ finalText = "<math>".concat(cleanedPasteText, "</math>");
23016
+ finalMimeType = "application/x-zzish-html-math";
23017
+ hasMathContent = true;
23018
+ // console.log("PASTE DEBUG - Wrapping in math tags:", finalText);
23019
+ } else {
23020
+ finalText = cleanedPasteText;
23021
+ finalMimeType = "text/plain";
23022
+ // console.log("PASTE DEBUG - Using as plain text:", finalText);
23023
+ }
23024
+ } else {
23025
+ // Nothing to paste
23026
+ return;
23027
+ }
23028
+
23029
+ // Use the component's insert mechanism to handle paste properly
23030
+ if (finalText) {
23031
+ // Get current node and create marked text at cursor position
23032
+ var currentNode = _this2._findNodeWithIndex(rangeParams.startNodeIndex);
23033
+ var markedText = elementToMarkedRawText(_this2.editableDiv, currentNode, rangeParams.startOffset, _this2.enableHtml());
23034
+
23035
+ // Insert the new content at the marked position
23036
+ var newRawText = removeMarks(insertCharacterBeforeMarks(markedText, finalText));
23037
+
23038
+ // Convert any remaining \[...\] LaTeX expressions to <math>...</math> format
23039
+ // This ensures consistency when paste adds <math> tags alongside existing \[...\] expressions
23040
+ if (_this2.enableMath()) {
23041
+ newRawText = newRawText.replace(/\\\[([^\]]*?)\\\]/g, "<math>$1</math>");
23042
+ }
23043
+
23044
+ // Calculate cursor position exactly like equation editor does
23045
+ var newRangeParams = null;
23046
+ if (hasMathContent) {
23047
+ // Use same approach as equation editor for math content
23048
+ var oldRangeParams = _this2.getOldRangeParams();
23049
+ var mathTagCount = (finalText.match(/<math>/gi) || []).length;
23050
+ if (_this2.props.value === "") {
23051
+ // Special case of previously empty text input (like equation editor)
23052
+ newRangeParams = {
23053
+ startNodeIndex: mathTagCount * 2,
23054
+ startOffset: SMALL_SPACE_LENGTH,
23055
+ endNodeIndex: mathTagCount * 2,
23056
+ endOffset: SMALL_SPACE_LENGTH
23057
+ };
23058
+ } else {
23059
+ // Normal case - position cursor after inserted math content (like equation editor)
23060
+ newRangeParams = {
23061
+ startNodeIndex: oldRangeParams.startNodeIndex + mathTagCount * 2,
23062
+ startOffset: SMALL_SPACE_LENGTH,
23063
+ endNodeIndex: oldRangeParams.startNodeIndex + mathTagCount * 2,
23064
+ endOffset: SMALL_SPACE_LENGTH
23065
+ };
22380
23066
  }
22381
- }, 0);
22382
- } else if (pasteText && pasteText.length > 0) {
22383
- _this2.insertRawText(pasteText);
23067
+ } else {
23068
+ // For text content, use global offset like before
23069
+ var newGlobalOffset = rangeParams.startGlobalOffset + finalText.length;
23070
+ newRangeParams = {
23071
+ startGlobalOffset: newGlobalOffset,
23072
+ endGlobalOffset: newGlobalOffset
23073
+ };
23074
+ }
23075
+
23076
+ // Determine final MIME type based on the complete content
23077
+ var actualMimeType = finalMimeType;
23078
+ if (/<math>/i.test(newRawText)) {
23079
+ actualMimeType = "application/x-zzish-html-math";
23080
+ } else if (_this2.enableHtml() && (/<[^>]+>/i.test(newRawText) || actualMimeType === "text/html")) {
23081
+ actualMimeType = "text/html";
23082
+ } else {
23083
+ actualMimeType = "text/plain";
23084
+ }
23085
+
23086
+ // Clean up the raw text for proper encoding
23087
+ var cleanedRawText = removeEncodingIfPlainText(newRawText, actualMimeType, _this2.enableHtml());
23088
+
23089
+ // Apply the change using the component's standard flow
23090
+ _this2.applyChangesToComponent(cleanedRawText, actualMimeType, newRangeParams, _this2.props.useExpertMode, _this2.props.selectedTab);
22384
23091
  }
22385
23092
  } catch (error) {
22386
- console.error(error);
23093
+ console.error("Error in paste handler:", error);
23094
+ // Let default paste behavior handle it
23095
+ return true;
22387
23096
  }
22388
23097
  });
22389
23098
  _defineProperty(_this2, "showEquationEditor", function () {
@@ -22476,6 +23185,9 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22476
23185
  _defineProperty(_this2, "handleMouseUp", function (event) {
22477
23186
  try {
22478
23187
  _this2.initialiseHistory();
23188
+
23189
+ // Clear selection anchor on mouse click
23190
+ _this2.selectionAnchor = null;
22479
23191
  var params = _this2._getRangeParams();
22480
23192
  var startNode = _this2._findNodeWithIndex(params.startNodeIndex);
22481
23193
  var endNode = _this2._findNodeWithIndex(params.endNodeIndex);
@@ -22560,18 +23272,18 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22560
23272
  new_offset = oldRangeParams.startOffset;
22561
23273
  }
22562
23274
  }
22563
- var _new_rangeParams3 = {
23275
+ var _new_rangeParams7 = {
22564
23276
  startNodeIndex: new_index,
22565
23277
  startOffset: new_offset,
22566
23278
  endNodeIndex: new_index,
22567
23279
  endOffset: new_offset
22568
23280
  };
22569
- _this2.setOldRangeParams(_new_rangeParams3);
22570
- var _nodeCounts = _this2._countNodeTypes();
22571
- var _mimeType = null;
22572
- if (_this2.editingExistingEquation) _mimeType = _this2.getMimeType(_nodeCounts.html > 0, _nodeCounts.katex > 1);else _mimeType = _this2.getMimeType(_nodeCounts.html > 0, _nodeCounts.katex > 0);
22573
- _new_raw_text = removeEncodingIfPlainText(_new_raw_text, _mimeType, _this2.enableHtml());
22574
- _this2.applyChangesToComponent(_new_raw_text, _mimeType, _new_rangeParams3, _this2.props.useExpertMode, _this2.props.selectedTab);
23281
+ _this2.setOldRangeParams(_new_rangeParams7);
23282
+ var _nodeCounts2 = _this2._countNodeTypes();
23283
+ var _mimeType2 = null;
23284
+ if (_this2.editingExistingEquation) _mimeType2 = _this2.getMimeType(_nodeCounts2.html > 0, _nodeCounts2.math > 1);else _mimeType2 = _this2.getMimeType(_nodeCounts2.html > 0, _nodeCounts2.math > 0);
23285
+ _new_raw_text = removeEncodingIfPlainText(_new_raw_text, _mimeType2, _this2.enableHtml());
23286
+ _this2.applyChangesToComponent(_new_raw_text, _mimeType2, _new_rangeParams7, _this2.props.useExpertMode, _this2.props.selectedTab);
22575
23287
  _this2.setState({
22576
23288
  showEquationEditor: false
22577
23289
  });
@@ -22583,7 +23295,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22583
23295
  var new_raw_text = replaceMarksWithMath(_this2.markedRawText, latex, _this2.enableHtml());
22584
23296
  var new_rangeParams = _this2.getOldRangeParams();
22585
23297
  if (_this2.moveCursorOnInsert) {
22586
- if (_this2.props.value === "")
23298
+ if (_this2.props.value === "") {
22587
23299
  // Special case of previously empty text input
22588
23300
  new_rangeParams = {
22589
23301
  startNodeIndex: 2,
@@ -22591,13 +23303,20 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22591
23303
  endNodeIndex: 2,
22592
23304
  endOffset: SMALL_SPACE_LENGTH
22593
23305
  };
22594
- // normal case
22595
- else new_rangeParams = {
22596
- startNodeIndex: oldRangeParams.startNodeIndex + 2,
22597
- startOffset: SMALL_SPACE_LENGTH,
22598
- endNodeIndex: oldRangeParams.startNodeIndex + 2,
22599
- endOffset: SMALL_SPACE_LENGTH
22600
- };
23306
+ } else {
23307
+ // Position cursor after the inserted math element using globalOffset
23308
+ // Find the position right after the last math tag in the content
23309
+ var mathTagMatches = _toConsumableArray(new_raw_text.matchAll(/<math>[^<]*<\/math>/gi));
23310
+ if (mathTagMatches.length > 0) {
23311
+ var lastMatch = mathTagMatches[mathTagMatches.length - 1];
23312
+ var afterMathOffset = lastMatch.index + lastMatch[0].length;
23313
+ new_rangeParams = {
23314
+ startGlobalOffset: afterMathOffset,
23315
+ endGlobalOffset: afterMathOffset
23316
+ };
23317
+ }
23318
+ // If no math tags found, keep old range params
23319
+ }
22601
23320
  }
22602
23321
  // console.log("New cursor pos: "+new_rangeParams.startNodeIndex+"->"+new_rangeParams.startOffset)
22603
23322
  _this2.setOldRangeParams(new_rangeParams);
@@ -22624,6 +23343,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22624
23343
  try {
22625
23344
  style = style.toLowerCase();
22626
23345
  var success = false;
23346
+
22627
23347
  // This first part is a workaround whereby executing subscript or superscript
22628
23348
  // within an existing subscript or superscript makes the text even smaller instead
22629
23349
  // of toggling it off. Thus we manually toggle it off with some jigerry pokery!
@@ -22641,8 +23361,65 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22641
23361
  if (_this2.state.activeButtons.italic) success = document.execCommand("italic", false, null);
22642
23362
  if (_this2.state.activeButtons.underline) success = document.execCommand("underline", false, null);
22643
23363
  } else {
22644
- // This is the normal code ...
22645
- success = document.execCommand(style, false, null);
23364
+ var _selection = document.getSelection();
23365
+ if (_selection && _selection.rangeCount > 0) {
23366
+ var _range = _selection.getRangeAt(0);
23367
+ if (_range.collapsed) {
23368
+ // For cursor position (no selection), use insertHTML with styled wrapper
23369
+ // This creates a formatted element that will maintain styling for future typing
23370
+ var isToggleOff = _this2.state.activeButtons[style];
23371
+ if (!isToggleOff) {
23372
+ // Turning style ON - insert a styled wrapper element
23373
+ var wrapperTag;
23374
+ switch (style) {
23375
+ case "bold":
23376
+ wrapperTag = "strong";
23377
+ break;
23378
+ case "italic":
23379
+ wrapperTag = "em";
23380
+ break;
23381
+ case "underline":
23382
+ wrapperTag = "u";
23383
+ break;
23384
+ case "subscript":
23385
+ wrapperTag = "sub";
23386
+ break;
23387
+ case "superscript":
23388
+ wrapperTag = "sup";
23389
+ break;
23390
+ default:
23391
+ wrapperTag = "span";
23392
+ break;
23393
+ }
23394
+
23395
+ // Insert an invisible character wrapped in the styled element
23396
+ // This provides a "landing zone" for future typing
23397
+ var styledElement = "<".concat(wrapperTag, ">\u200B</").concat(wrapperTag, ">");
23398
+ success = document.execCommand("insertHTML", false, styledElement);
23399
+
23400
+ // Move cursor inside the styled element
23401
+ setTimeout(function () {
23402
+ var newSelection = document.getSelection();
23403
+ if (newSelection && newSelection.rangeCount > 0) {
23404
+ var newRange = newSelection.getRangeAt(0);
23405
+ var styledNode = newRange.startContainer.parentElement;
23406
+ if (styledNode && styledNode.tagName.toLowerCase() === wrapperTag) {
23407
+ newRange.setStart(styledNode, 0);
23408
+ newRange.setEnd(styledNode, 0);
23409
+ newSelection.removeAllRanges();
23410
+ newSelection.addRange(newRange);
23411
+ }
23412
+ }
23413
+ }, 0);
23414
+ } else {
23415
+ // Turning style OFF - just apply execCommand normally
23416
+ success = document.execCommand(style, false, null);
23417
+ }
23418
+ } else {
23419
+ // For text selection, use regular execCommand
23420
+ success = document.execCommand(style, false, null);
23421
+ }
23422
+ }
22646
23423
  }
22647
23424
  var buttonState = _this2.state.activeButtons[style];
22648
23425
  if (buttonState === null || buttonState === undefined) return success;
@@ -22942,7 +23719,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22942
23719
  _defineProperty(_this2, "handleAccentButtonClick", function (event, oldCharacter, newCharacter) {
22943
23720
  try {
22944
23721
  _this2.insertAccentLetter(event, oldCharacter, newCharacter);
22945
- console.log("Hiding accent bar 3");
23722
+ // console.log("Hiding accent bar 3");
22946
23723
  _this2.setState({
22947
23724
  showAccentBar: false
22948
23725
  });
@@ -22950,6 +23727,23 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22950
23727
  console.error(error);
22951
23728
  }
22952
23729
  });
23730
+ _defineProperty(_this2, "cleanupCorruptedKatexNodes", function () {
23731
+ try {
23732
+ var katexNodes = _this2.editableDiv.querySelectorAll(".katex, .katex-error");
23733
+ katexNodes.forEach(function (node) {
23734
+ // Check if KaTeX node has proper structure
23735
+ var annotation = node.querySelector('annotation[encoding="application/x-tex"]');
23736
+ if (!annotation || !annotation.textContent) {
23737
+ console.warn("Removing corrupted KaTeX node:", node);
23738
+ // Replace corrupted node with placeholder text
23739
+ var textNode = document.createTextNode("[Math Error]");
23740
+ node.parentNode.replaceChild(textNode, node);
23741
+ }
23742
+ });
23743
+ } catch (error) {
23744
+ console.error("Error cleaning up corrupted KaTeX nodes:", error);
23745
+ }
23746
+ });
22953
23747
  _defineProperty(_this2, "focus", function () {
22954
23748
  _this2.editableDiv.focus();
22955
23749
  });
@@ -22973,7 +23767,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22973
23767
  onBlur();
22974
23768
  }
22975
23769
  try {
22976
- console.log("Hiding accent bar 4");
23770
+ // console.log("Hiding accent bar 4");
22977
23771
  _this2.setState({
22978
23772
  hasFocus: false,
22979
23773
  showAccentBar: false
@@ -22988,6 +23782,96 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22988
23782
  _defineProperty(_this2, "handleCompositionEnd", function (event) {
22989
23783
  _this2.isComposing = false;
22990
23784
  });
23785
+ // Comprehensive LaTeX pattern detection and auto-wrapping in math tags
23786
+ // Comprehensive LaTeX pattern detection for paste auto-wrapping
23787
+ _defineProperty(_this2, "detectLatexPatterns", function (text) {
23788
+ if (!_this2.enableMath() || !text || typeof text !== "string") {
23789
+ // console.log("PATTERN DEBUG - Early return:", {
23790
+ // enableMath: this.enableMath(),
23791
+ // text: text,
23792
+ // type: typeof text,
23793
+ // });
23794
+ return false;
23795
+ }
23796
+
23797
+ // console.log("PATTERN DEBUG - Testing text:", JSON.stringify(text));
23798
+
23799
+ // Define comprehensive LaTeX patterns based on KaTeX documentation
23800
+ var latexPatterns = [
23801
+ // Complex expressions
23802
+ // Matrix environments
23803
+ /\\begin\s*\{\s*(matrix|pmatrix|bmatrix|Bmatrix|vmatrix|Vmatrix|array)\s*\}[\s\S]*?\\end\s*\{\s*\1\s*\}/,
23804
+ // Cases and aligned environments
23805
+ /\\begin\s*\{\s*(cases|aligned|align|gather|split)\s*\}[\s\S]*?\\end\s*\{\s*\1\s*\}/,
23806
+ // Fractions and roots with nested braces
23807
+ /\\frac\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/, /\\sqrt\s*(?:\[[^\]]*\])?\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/,
23808
+ // Text in math mode with braces
23809
+ /\\(text|textrm|textit|textbf|textsf|texttt|mathrm|mathit|mathbf|mathsf|mathtt|mathcal|mathscr|mathfrak|mathbb)\s*\{[^{}]*\}/,
23810
+ // Accents and modifiers with braces
23811
+ /\\(hat|check|breve|acute|grave|tilde|bar|vec|dot|ddot|widehat|widetilde|overline|underline|overbrace|underbrace)\s*\{[^{}]*\}/,
23812
+ // Number sets with braces
23813
+ /\\(mathbb|mathbf)\s*\{[NZQRCH]\}/,
23814
+ // Delimiters and brackets
23815
+ /\\(left|right|big|Big|bigg|Bigg)\s*[\(\)\[\]\\{\\}|\\.]/, /\\[lr](paren|brace|brack|vert|Vert|angle|langle|rangle|floor|ceil)/,
23816
+ // Greek letters (comprehensive list from KaTeX)
23817
+ /\\(alpha|beta|gamma|delta|epsilon|varepsilon|zeta|eta|theta|vartheta|iota|kappa|lambda|mu|nu|xi|omicron|pi|varpi|rho|varrho|sigma|varsigma|tau|upsilon|phi|varphi|chi|psi|omega|Gamma|Delta|Theta|Lambda|Xi|Pi|Sigma|Upsilon|Phi|Psi|Omega)\b/,
23818
+ // Mathematical functions
23819
+ /\\(sin|cos|tan|sec|csc|cot|sinh|cosh|tanh|sech|csch|coth|arcsin|arccos|arctan|arcsec|arccsc|arccot|log|ln|lg|exp|lim|limsup|liminf|sup|inf|max|min|arg|ker|deg|gcd|det|hom|dim)\b/,
23820
+ // Mathematical operators and symbols
23821
+ /\\(pm|mp|times|div|cdot|ast|star|circ|bullet|cap|cup|uplus|sqcap|sqcup|vee|wedge|setminus|wr|diamond|bigtriangleup|bigtriangledown|triangleleft|triangleright|lhd|rhd|unlhd|unrhd|oplus|ominus|otimes|oslash|odot|bigcirc|dagger|ddagger|amalg)\b/,
23822
+ // Relation symbols
23823
+ /\\(leq|geq|equiv|models|prec|succ|sim|perp|preceq|succeq|simeq|mid|ll|gg|asymp|parallel|subset|supset|approx|bowtie|subseteq|supseteq|cong|smile|sqsubset|sqsupset|doteq|frown|sqsubseteq|sqsupseteq|propto|vdash|dashv|in|ni|notin)\b/,
23824
+ // Negated relations
23825
+ /\\(not|neq|ne|notin|nmid|nparallel|nleq|ngeq|nless|ngtr|nprec|nsucc|lneq|gneq|npreceq|nsucceq|precneqq|succneqq|lneqq|gneqq)\b/,
23826
+ // Arrows
23827
+ /\\(leftarrow|gets|rightarrow|to|leftrightarrow|uparrow|downarrow|updownarrow|Leftarrow|Rightarrow|Leftrightarrow|Uparrow|Downarrow|Updownarrow|mapsto|longmapsto|hookleftarrow|hookrightarrow|leftharpoonup|leftharpoondown|rightharpoonup|rightharpoondown|rightleftharpoons|iff)\b/,
23828
+ // Integrals, sums, products
23829
+ /\\(int|iint|iiint|oint|sum|prod|coprod|bigcup|bigcap|bigsqcup|bigvee|bigwedge|bigodot|bigotimes|bigoplus|biguplus)\b/,
23830
+ // Mathematical spacing and formatting
23831
+ /\\(quad|qquad|,|:|;|!|thinspace|medspace|thickspace|enspace|negspace)\b/,
23832
+ // Special characters and symbols
23833
+ /\\(infty|partial|nabla|pm|mp|emptyset|varnothing|complement|forall|exists|nexists|in|notin|ni|Re|Im|wp|angle|measuredangle|sphericalangle|top|bot|vdash|models|vDash|Vdash|Vvdash|nvdash|nvDash|nVdash|nVDash)\b/,
23834
+ // Single letter number sets
23835
+ /\\(N|Z|Q|R|C|H)\b/,
23836
+ // Simple expressions with operators and special characters
23837
+ /\d+\\surd\d+/,
23838
+ // Numbers with square root symbol
23839
+ /\\surd/,
23840
+ // Square root symbol alone
23841
+
23842
+ // Superscripts and subscripts (improved to catch simple formats)
23843
+ /\d+\^\d+/,
23844
+ // Number^number (e.g., 2^2, 10^3)
23845
+ /[a-zA-Z]\^\d+/,
23846
+ // Variable^number (e.g., x^2, y^3)
23847
+ /[a-zA-Z]\^[\{\d+\}]/,
23848
+ // Variable with superscript in braces (e.g., x^{2})
23849
+ /\d+_\d+/,
23850
+ // Number_number (e.g., H_2O becomes H_2O)
23851
+ /[a-zA-Z]_\d+/,
23852
+ // Variable_number (e.g., x_1, y_2)
23853
+ /[a-zA-Z]_[\{\d+\}]/,
23854
+ // Variable with subscript in braces (e.g., x_{12})
23855
+
23856
+ // Common mathematical expressions that should be wrapped
23857
+ /\d+\/\d+\s*[+\-*/]\s*\d+\/\d+/,
23858
+ // Fraction operations
23859
+ /[xy]\^2\s*[+\-]\s*\d+[xy]\s*[+\-]\s*\d+/ // Quadratic expressions
23860
+ ];
23861
+
23862
+ // Check if any LaTeX patterns exist
23863
+ for (var i = 0; i < latexPatterns.length; i++) {
23864
+ var pattern = latexPatterns[i];
23865
+ var matches = pattern.test(text);
23866
+ if (matches) {
23867
+ // console.log(`PATTERN DEBUG - Pattern ${i} matched:`, pattern.source);
23868
+ return true;
23869
+ }
23870
+ }
23871
+
23872
+ // console.log("PATTERN DEBUG - No patterns matched");
23873
+ return false;
23874
+ });
22991
23875
  _this2.state = {
22992
23876
  showEquationEditor: false,
22993
23877
  equationEditorLatex: "",
@@ -22999,8 +23883,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
22999
23883
  subscript: false,
23000
23884
  superscript: false
23001
23885
  },
23002
- keyPressed: "",
23003
- pendingHtmlWithMarker: null
23886
+ keyPressed: ""
23004
23887
  };
23005
23888
 
23006
23889
  // this.handleInput = this.handleInput.bind(this);
@@ -23045,18 +23928,27 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23045
23928
 
23046
23929
  // keeps track of whether user is composing in an IME - see https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
23047
23930
  _this2.isComposing = false;
23931
+
23932
+ // Track selection anchor for proper shift+arrow behavior
23933
+ _this2.selectionAnchor = null;
23048
23934
  return _this2;
23049
23935
  }
23050
23936
  _inherits(MathRichInput, _React$Component);
23051
23937
  return _createClass(MathRichInput, [{
23052
23938
  key: "enableFontStyling",
23053
23939
  value: function enableFontStyling() {
23054
- if (this.props.outputTex !== null && this.props.outputTex !== undefined && this.props.outputTex === true) return false;else if (this.props.enableFontStyling !== null && this.props.enableFontStyling !== undefined) return this.props.enableFontStyling;else return true;
23940
+ // Allow explicit override even in outputTex mode
23941
+ if (this.props.enableFontStyling !== null && this.props.enableFontStyling !== undefined) return this.props.enableFontStyling;
23942
+ // Don't disable font styling in outputTex mode anymore
23943
+ else return true;
23055
23944
  }
23056
23945
  }, {
23057
23946
  key: "enableMultiline",
23058
23947
  value: function enableMultiline() {
23059
- if (this.props.outputTex !== null && this.props.outputTex !== undefined && this.props.outputTex === true) return false;else if (this.props.enableMultiline !== null && this.props.enableMultiline !== undefined) return this.props.enableMultiline;else return true;
23948
+ // Allow explicit override even in outputTex mode
23949
+ if (this.props.enableMultiline !== null && this.props.enableMultiline !== undefined) return this.props.enableMultiline;
23950
+ // Don't disable multiline in outputTex mode anymore
23951
+ else return true;
23060
23952
  }
23061
23953
  }, {
23062
23954
  key: "enableHtml",
@@ -23086,11 +23978,18 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23086
23978
  var _this$props$outputTex2, _this$props$outputPla;
23087
23979
  var outputTex = (_this$props$outputTex2 = this.props.outputTex) !== null && _this$props$outputTex2 !== void 0 ? _this$props$outputTex2 : false;
23088
23980
  var outputPlainText = (_this$props$outputPla = this.props.outputPlainText) !== null && _this$props$outputPla !== void 0 ? _this$props$outputPla : false;
23981
+
23982
+ // When outputTex is false, prioritize HTML formatting
23983
+ if (!outputTex) {
23984
+ if (hasMath) return MIME_TYPE_ZZISH_HTML_MATH;
23985
+ if (hasHtml || !outputPlainText) return MIME_TYPE_HTML;
23986
+ return MIME_TYPE_PLAIN_TEXT;
23987
+ }
23988
+
23989
+ // Original logic for outputTex = true
23089
23990
  if (outputTex) {
23090
23991
  if (hasMath || !outputPlainText) return MIME_TYPE_TEX;else return MIME_TYPE_PLAIN_TEXT;
23091
23992
  }
23092
- if (hasMath) return MIME_TYPE_ZZISH_HTML_MATH;
23093
- if (hasHtml || !outputPlainText) return MIME_TYPE_HTML;
23094
23993
  return MIME_TYPE_PLAIN_TEXT;
23095
23994
  }
23096
23995
  }, {
@@ -23142,6 +24041,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23142
24041
  this.editableDiv.addEventListener("keydown", this.handleKeyDown);
23143
24042
  this.editableDiv.addEventListener("keyup", this.handleKeyUp);
23144
24043
  this.editableDiv.addEventListener("paste", this.handlePaste);
24044
+ this.editableDiv.addEventListener("copy", this.handleCopy);
23145
24045
  if (this.props.autofocus === true) this.editableDiv.focus();
23146
24046
  } catch (error) {
23147
24047
  console.error(error);
@@ -23154,6 +24054,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23154
24054
  this.editableDiv.removeEventListener("keydown", this.handleKeyDown);
23155
24055
  this.editableDiv.removeEventListener("keyup", this.handleKeyUp);
23156
24056
  this.editableDiv.removeEventListener("paste", this.handlePaste);
24057
+ this.editableDiv.removeEventListener("copy", this.handleCopy);
23157
24058
  } catch (error) {
23158
24059
  console.error(error);
23159
24060
  }
@@ -23162,24 +24063,6 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23162
24063
  key: "componentDidUpdate",
23163
24064
  value: function componentDidUpdate() {
23164
24065
  try {
23165
- if (this._pendingPasteMarkerId) {
23166
- var start = document.getElementById("".concat(this._pendingPasteMarkerId, "_START"));
23167
- var end = document.getElementById("".concat(this._pendingPasteMarkerId, "_END"));
23168
- if (start && end) {
23169
- var range = document.createRange();
23170
- range.setStartAfter(start);
23171
- range.setEndBefore(end);
23172
- var sel = window.getSelection();
23173
- sel.removeAllRanges();
23174
- sel.addRange(range);
23175
- start.parentNode && start.parentNode.removeChild(start);
23176
- end.parentNode && end.parentNode.removeChild(end);
23177
- }
23178
- this._pendingPasteMarkerId = null;
23179
- this.setState({
23180
- pendingHtmlWithMarker: null
23181
- });
23182
- }
23183
24066
  if (this.afterComponentUpdateData !== null && this.afterComponentUpdateData !== undefined && this.afterComponentUpdateData.value === this.props.value) {
23184
24067
  this._setRangeParams(this.afterComponentUpdateData.rangeParams);
23185
24068
  this.afterComponentUpdateData = null;
@@ -23223,6 +24106,16 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23223
24106
  console.log(this.props);
23224
24107
  }
23225
24108
  var html = rawTextToHtml(katex$1, this.props.value || "", this.props.mimeType);
24109
+
24110
+ // Debug render math
24111
+ // if (this.props.value && this.props.value.includes("<math>")) {
24112
+ // console.log("RENDER DEBUG:", {
24113
+ // propsValue: this.props.value,
24114
+ // propsMimeType: this.props.mimeType,
24115
+ // generatedHtml: html,
24116
+ // });
24117
+ // }
24118
+
23226
24119
  var useClassName = "MathRichInput-Container"; // Don't change this name - used by various components to find node
23227
24120
  if (this.props.className) useClassName = this.props.className + " " + useClassName;
23228
24121
  var accentLetter = null;
@@ -23231,7 +24124,8 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23231
24124
  if (this.props.className.includes("answer-edit-input")) {
23232
24125
  toolbarExtraMargin = 20;
23233
24126
  }
23234
- console.log("Toolbar extra margin: " + toolbarExtraMargin);
24127
+ // console.log("Toolbar extra margin: " + toolbarExtraMargin);
24128
+
23235
24129
  return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
23236
24130
  className: useClassName
23237
24131
  }, /*#__PURE__*/React__default["default"].createElement("div", {
@@ -23270,7 +24164,7 @@ var MathRichInput = /*#__PURE__*/function (_React$Component) {
23270
24164
  onCompositionStart: this.handleCompositionStart,
23271
24165
  onCompositionEnd: this.handleCompositionEnd,
23272
24166
  dangerouslySetInnerHTML: {
23273
- __html: this.state.pendingHtmlWithMarker || html
24167
+ __html: html
23274
24168
  },
23275
24169
  "data-placeholder": this.props.placeholder || ""
23276
24170
  }))), this.state.showEquationEditor && /*#__PURE__*/React__default["default"].createElement(EquationEditorModal, {