ed-mathml2tex 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -147,7 +147,9 @@ const NodeTool = {
147
147
  return node.children;
148
148
  },
149
149
  getNodeName: function(node) {
150
- return node.tagName.toLowerCase();
150
+ if (!node) return '';
151
+ const localName = node.localName || node.nodeName;
152
+ return localName ? localName.toLowerCase() : '';
151
153
  },
152
154
  getNodeText: function(node) {
153
155
  return node.textContent;
@@ -704,9 +706,9 @@ function parseLeaf(node) {
704
706
  function parseOperator(node) {
705
707
  let it = NodeTool.getNodeText(node).trim();
706
708
 
707
- // Special case for arrow (→)
709
+ // Special case for arrow (→) with proper spacing
708
710
  if (it === "→") {
709
- return "\\rightarrow";
711
+ return " \\rightarrow "; // Add spaces around arrow
710
712
  }
711
713
 
712
714
  it = MathSymbol.parseOperator(it);
@@ -789,6 +791,7 @@ function renderChildren(children) {
789
791
  if(Brackets.contains(op)){
790
792
  let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
791
793
  stretchy = ['', 'true'].indexOf(stretchy) > -1;
794
+
792
795
  if(Brackets.isRight(op)){
793
796
  const nearLeft = lefts[lefts.length - 1];
794
797
  if(nearLeft){
@@ -796,16 +799,10 @@ function renderChildren(children) {
796
799
  parts.push(Brackets.parseRight(op, stretchy));
797
800
  lefts.pop();
798
801
  } else {
799
- if(Brackets.isLeft(op)) {
800
- parts.push(Brackets.parseLeft(op, stretchy));
801
- lefts.push(op);
802
- }
803
- }
804
- }else {
805
- if(Brackets.isLeft(op)) {
806
- parts.push(Brackets.parseLeft(op, stretchy));
807
- lefts.push(op);
802
+ parts.push(`\\right${op}`);
808
803
  }
804
+ } else {
805
+ parts.push(`\\right${op}`);
809
806
  }
810
807
  } else {
811
808
  parts.push(Brackets.parseLeft(op, stretchy));
@@ -818,10 +815,9 @@ function renderChildren(children) {
818
815
  parts.push(parse(node));
819
816
  }
820
817
  });
821
- if(lefts.length > 0){
822
- for(let i=0; i < lefts.length; i++){
823
- parts.push("\\right.");
824
- }
818
+ // Only add \right. if there are unmatched left brackets
819
+ if(lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
820
+ parts.push("\\right.");
825
821
  }
826
822
  lefts = undefined;
827
823
  return parts;
@@ -833,7 +829,28 @@ function getRender(node) {
833
829
  const nodeName = NodeTool.getNodeName(node);
834
830
  switch(nodeName){
835
831
  case 'msub':
836
- render = getRender_default("@1_{@2}");
832
+ render = function(node, children) {
833
+ if (!children || children.length < 2) return '';
834
+
835
+ const base = parse(children[0]);
836
+ if (!base) return '';
837
+
838
+ const sub = children[1];
839
+ if (!sub) return base;
840
+
841
+ // Handle nested subscript with empty mrow
842
+ if (NodeTool.getNodeName(sub) === 'msub' &&
843
+ sub.firstChild &&
844
+ NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
845
+ (!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
846
+ const lastChild = sub.lastChild;
847
+ if (!lastChild) return base;
848
+ return `${base}_${parse(lastChild)}`;
849
+ }
850
+
851
+ // Regular subscript
852
+ return `${base}_{${parse(sub)}}`;
853
+ };
837
854
  break;
838
855
  case 'msup':
839
856
  render = getRender_default("@1^{@2}");
@@ -895,10 +912,32 @@ function getRender(node) {
895
912
 
896
913
  function renderTable(node, children) {
897
914
  const template = "\\begin{array}{l}@content\\end{array}";
898
- const render = getRender_joinSeparator(template, "\\\\");
915
+ // Remove extra backslash and add proper spacing
916
+ const render = getRender_joinSeparator(template, " \\\\ ");
899
917
  return render(node, children);
900
918
  }
901
919
 
920
+ function renderMover(node, children) {
921
+ // Check if children exists and has enough elements
922
+ if (!children || children.length < 2) return '';
923
+
924
+ const baseNode = children[0];
925
+ const overNode = children[1];
926
+
927
+ // Check if nodes exist before accessing
928
+ if (!baseNode || !overNode) return '';
929
+
930
+ const overText = NodeTool.getNodeText(overNode)?.trim() || '';
931
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
932
+
933
+ // Handle vector notation
934
+ if (overText === "→" && isAccent) {
935
+ return `\\vec{${parse(baseNode)}}`;
936
+ }
937
+
938
+ return parse(baseNode);
939
+ }
940
+
902
941
  function renderMfenced(node, children) {
903
942
  const [open, close, separatorsStr] = [
904
943
  NodeTool.getAttr(node, 'open', '('),
@@ -909,22 +948,25 @@ function renderMfenced(node, children) {
909
948
  const parts = renderChildren(children);
910
949
  const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
911
950
 
912
- // Handle empty open/close brackets
951
+ // Handle nested absolute value cases
952
+ if (open === '|' && close === '') {
953
+ return `\\left|${content}`;
954
+ }
913
955
  if (open === '' && close === '|') {
914
- return `|${content}|`;
956
+ return `${content}\\right|`;
915
957
  }
916
958
 
917
- // Handle absolute value notation
918
- if (open === '|' && (close === '|' || close === '')) {
919
- return `\\left|${content}\\right|`;
959
+ // Handle system of equations with curly brace
960
+ if (open === '{') {
961
+ return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
920
962
  }
921
-
922
- // Regular parentheses
923
- if (open === '(' && close === ')') {
924
- return `\\left(${content}\\right)`;
963
+
964
+ // Handle regular absolute value notation
965
+ if (open === '|' && close === '|') {
966
+ return `\\left|${content}\\right|`;
925
967
  }
926
968
 
927
- // Default case
969
+ // Regular cases with proper closing
928
970
  return `\\left${open}${content}\\right${close || '.'}`;
929
971
  }
930
972
 
@@ -1003,62 +1045,6 @@ function renderMmultiscripts(node, children) {
1003
1045
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
1004
1046
  }
1005
1047
 
1006
- function renderMover(node, children) {
1007
- const nodes = flattenNodeTreeByNodeName(node, 'mover');
1008
- let result = undefined;
1009
-
1010
- // Get the base node and check if it's a subscript or mrow
1011
- const baseNode = children[0];
1012
- const overNode = children[1];
1013
- const nodeName = NodeTool.getNodeName(baseNode);
1014
- const isSubscript = nodeName === 'msub';
1015
- const isMrow = nodeName === 'mrow';
1016
-
1017
- // Handle case where the base is an arrow and mrow is above
1018
- const baseText = NodeTool.getNodeText(baseNode).trim();
1019
- if (baseText === "→" && NodeTool.getNodeName(overNode) === "mrow") {
1020
- return "\\rightarrow";
1021
- }
1022
-
1023
- if (isSubscript) {
1024
- // Handle case like n₂ with arrow
1025
- const base = parse(baseNode);
1026
- return `\\overrightarrow{${base}}`;
1027
- }
1028
-
1029
- if (isMrow) {
1030
- // Handle case like 0 with arrow
1031
- const base = parse(baseNode);
1032
- const overText = NodeTool.getNodeText(overNode).trim();
1033
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1034
-
1035
- if (overText === "→" && isAccent) {
1036
- return `\\overrightarrow{${base}}`;
1037
- }
1038
- }
1039
-
1040
- // Handle case where there is only an arrow with no base
1041
- const overText = NodeTool.getNodeText(overNode).trim();
1042
- if (overText === "→" && NodeTool.getNodeText(baseNode).trim() === "") {
1043
- return `\\rightarrow`; // Return just the arrow
1044
- }
1045
-
1046
- for (let i = 0; i < nodes.length - 1; i++) {
1047
- if (!result) {
1048
- result = parse(nodes[i]);
1049
- }
1050
-
1051
- const _overNode = nodes[i + 1];
1052
- const overText = NodeTool.getNodeText(_overNode).trim();
1053
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1054
-
1055
- if (overText === "→" && isAccent) {
1056
- return `\\overrightarrow{${result}}`;
1057
- }
1058
- }
1059
- return result;
1060
- }
1061
-
1062
1048
  function renderMunder(node, children){
1063
1049
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
1064
1050
  let result = undefined;
@@ -145,7 +145,9 @@ const NodeTool = {
145
145
  return node.children;
146
146
  },
147
147
  getNodeName: function(node) {
148
- return node.tagName.toLowerCase();
148
+ if (!node) return '';
149
+ const localName = node.localName || node.nodeName;
150
+ return localName ? localName.toLowerCase() : '';
149
151
  },
150
152
  getNodeText: function(node) {
151
153
  return node.textContent;
@@ -702,9 +704,9 @@ function parseLeaf(node) {
702
704
  function parseOperator(node) {
703
705
  let it = NodeTool.getNodeText(node).trim();
704
706
 
705
- // Special case for arrow (→)
707
+ // Special case for arrow (→) with proper spacing
706
708
  if (it === "→") {
707
- return "\\rightarrow";
709
+ return " \\rightarrow "; // Add spaces around arrow
708
710
  }
709
711
 
710
712
  it = MathSymbol.parseOperator(it);
@@ -787,6 +789,7 @@ function renderChildren(children) {
787
789
  if(Brackets.contains(op)){
788
790
  let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
789
791
  stretchy = ['', 'true'].indexOf(stretchy) > -1;
792
+
790
793
  if(Brackets.isRight(op)){
791
794
  const nearLeft = lefts[lefts.length - 1];
792
795
  if(nearLeft){
@@ -794,16 +797,10 @@ function renderChildren(children) {
794
797
  parts.push(Brackets.parseRight(op, stretchy));
795
798
  lefts.pop();
796
799
  } else {
797
- if(Brackets.isLeft(op)) {
798
- parts.push(Brackets.parseLeft(op, stretchy));
799
- lefts.push(op);
800
- }
801
- }
802
- }else {
803
- if(Brackets.isLeft(op)) {
804
- parts.push(Brackets.parseLeft(op, stretchy));
805
- lefts.push(op);
800
+ parts.push(`\\right${op}`);
806
801
  }
802
+ } else {
803
+ parts.push(`\\right${op}`);
807
804
  }
808
805
  } else {
809
806
  parts.push(Brackets.parseLeft(op, stretchy));
@@ -816,10 +813,9 @@ function renderChildren(children) {
816
813
  parts.push(parse(node));
817
814
  }
818
815
  });
819
- if(lefts.length > 0){
820
- for(let i=0; i < lefts.length; i++){
821
- parts.push("\\right.");
822
- }
816
+ // Only add \right. if there are unmatched left brackets
817
+ if(lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
818
+ parts.push("\\right.");
823
819
  }
824
820
  lefts = undefined;
825
821
  return parts;
@@ -831,7 +827,28 @@ function getRender(node) {
831
827
  const nodeName = NodeTool.getNodeName(node);
832
828
  switch(nodeName){
833
829
  case 'msub':
834
- render = getRender_default("@1_{@2}");
830
+ render = function(node, children) {
831
+ if (!children || children.length < 2) return '';
832
+
833
+ const base = parse(children[0]);
834
+ if (!base) return '';
835
+
836
+ const sub = children[1];
837
+ if (!sub) return base;
838
+
839
+ // Handle nested subscript with empty mrow
840
+ if (NodeTool.getNodeName(sub) === 'msub' &&
841
+ sub.firstChild &&
842
+ NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
843
+ (!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
844
+ const lastChild = sub.lastChild;
845
+ if (!lastChild) return base;
846
+ return `${base}_${parse(lastChild)}`;
847
+ }
848
+
849
+ // Regular subscript
850
+ return `${base}_{${parse(sub)}}`;
851
+ };
835
852
  break;
836
853
  case 'msup':
837
854
  render = getRender_default("@1^{@2}");
@@ -893,10 +910,32 @@ function getRender(node) {
893
910
 
894
911
  function renderTable(node, children) {
895
912
  const template = "\\begin{array}{l}@content\\end{array}";
896
- const render = getRender_joinSeparator(template, "\\\\");
913
+ // Remove extra backslash and add proper spacing
914
+ const render = getRender_joinSeparator(template, " \\\\ ");
897
915
  return render(node, children);
898
916
  }
899
917
 
918
+ function renderMover(node, children) {
919
+ // Check if children exists and has enough elements
920
+ if (!children || children.length < 2) return '';
921
+
922
+ const baseNode = children[0];
923
+ const overNode = children[1];
924
+
925
+ // Check if nodes exist before accessing
926
+ if (!baseNode || !overNode) return '';
927
+
928
+ const overText = NodeTool.getNodeText(overNode)?.trim() || '';
929
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
930
+
931
+ // Handle vector notation
932
+ if (overText === "→" && isAccent) {
933
+ return `\\vec{${parse(baseNode)}}`;
934
+ }
935
+
936
+ return parse(baseNode);
937
+ }
938
+
900
939
  function renderMfenced(node, children) {
901
940
  const [open, close, separatorsStr] = [
902
941
  NodeTool.getAttr(node, 'open', '('),
@@ -907,22 +946,25 @@ function renderMfenced(node, children) {
907
946
  const parts = renderChildren(children);
908
947
  const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
909
948
 
910
- // Handle empty open/close brackets
949
+ // Handle nested absolute value cases
950
+ if (open === '|' && close === '') {
951
+ return `\\left|${content}`;
952
+ }
911
953
  if (open === '' && close === '|') {
912
- return `|${content}|`;
954
+ return `${content}\\right|`;
913
955
  }
914
956
 
915
- // Handle absolute value notation
916
- if (open === '|' && (close === '|' || close === '')) {
917
- return `\\left|${content}\\right|`;
957
+ // Handle system of equations with curly brace
958
+ if (open === '{') {
959
+ return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
918
960
  }
919
-
920
- // Regular parentheses
921
- if (open === '(' && close === ')') {
922
- return `\\left(${content}\\right)`;
961
+
962
+ // Handle regular absolute value notation
963
+ if (open === '|' && close === '|') {
964
+ return `\\left|${content}\\right|`;
923
965
  }
924
966
 
925
- // Default case
967
+ // Regular cases with proper closing
926
968
  return `\\left${open}${content}\\right${close || '.'}`;
927
969
  }
928
970
 
@@ -1001,62 +1043,6 @@ function renderMmultiscripts(node, children) {
1001
1043
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
1002
1044
  }
1003
1045
 
1004
- function renderMover(node, children) {
1005
- const nodes = flattenNodeTreeByNodeName(node, 'mover');
1006
- let result = undefined;
1007
-
1008
- // Get the base node and check if it's a subscript or mrow
1009
- const baseNode = children[0];
1010
- const overNode = children[1];
1011
- const nodeName = NodeTool.getNodeName(baseNode);
1012
- const isSubscript = nodeName === 'msub';
1013
- const isMrow = nodeName === 'mrow';
1014
-
1015
- // Handle case where the base is an arrow and mrow is above
1016
- const baseText = NodeTool.getNodeText(baseNode).trim();
1017
- if (baseText === "→" && NodeTool.getNodeName(overNode) === "mrow") {
1018
- return "\\rightarrow";
1019
- }
1020
-
1021
- if (isSubscript) {
1022
- // Handle case like n₂ with arrow
1023
- const base = parse(baseNode);
1024
- return `\\overrightarrow{${base}}`;
1025
- }
1026
-
1027
- if (isMrow) {
1028
- // Handle case like 0 with arrow
1029
- const base = parse(baseNode);
1030
- const overText = NodeTool.getNodeText(overNode).trim();
1031
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1032
-
1033
- if (overText === "→" && isAccent) {
1034
- return `\\overrightarrow{${base}}`;
1035
- }
1036
- }
1037
-
1038
- // Handle case where there is only an arrow with no base
1039
- const overText = NodeTool.getNodeText(overNode).trim();
1040
- if (overText === "→" && NodeTool.getNodeText(baseNode).trim() === "") {
1041
- return `\\rightarrow`; // Return just the arrow
1042
- }
1043
-
1044
- for (let i = 0; i < nodes.length - 1; i++) {
1045
- if (!result) {
1046
- result = parse(nodes[i]);
1047
- }
1048
-
1049
- const _overNode = nodes[i + 1];
1050
- const overText = NodeTool.getNodeText(_overNode).trim();
1051
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1052
-
1053
- if (overText === "→" && isAccent) {
1054
- return `\\overrightarrow{${result}}`;
1055
- }
1056
- }
1057
- return result;
1058
- }
1059
-
1060
1046
  function renderMunder(node, children){
1061
1047
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
1062
1048
  let result = undefined;
@@ -151,7 +151,9 @@
151
151
  return node.children;
152
152
  },
153
153
  getNodeName: function(node) {
154
- return node.tagName.toLowerCase();
154
+ if (!node) return '';
155
+ const localName = node.localName || node.nodeName;
156
+ return localName ? localName.toLowerCase() : '';
155
157
  },
156
158
  getNodeText: function(node) {
157
159
  return node.textContent;
@@ -708,9 +710,9 @@
708
710
  function parseOperator(node) {
709
711
  let it = NodeTool.getNodeText(node).trim();
710
712
 
711
- // Special case for arrow (→)
713
+ // Special case for arrow (→) with proper spacing
712
714
  if (it === "→") {
713
- return "\\rightarrow";
715
+ return " \\rightarrow "; // Add spaces around arrow
714
716
  }
715
717
 
716
718
  it = MathSymbol.parseOperator(it);
@@ -793,6 +795,7 @@
793
795
  if(Brackets.contains(op)){
794
796
  let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
795
797
  stretchy = ['', 'true'].indexOf(stretchy) > -1;
798
+
796
799
  if(Brackets.isRight(op)){
797
800
  const nearLeft = lefts[lefts.length - 1];
798
801
  if(nearLeft){
@@ -800,16 +803,10 @@
800
803
  parts.push(Brackets.parseRight(op, stretchy));
801
804
  lefts.pop();
802
805
  } else {
803
- if(Brackets.isLeft(op)) {
804
- parts.push(Brackets.parseLeft(op, stretchy));
805
- lefts.push(op);
806
- }
807
- }
808
- }else {
809
- if(Brackets.isLeft(op)) {
810
- parts.push(Brackets.parseLeft(op, stretchy));
811
- lefts.push(op);
806
+ parts.push(`\\right${op}`);
812
807
  }
808
+ } else {
809
+ parts.push(`\\right${op}`);
813
810
  }
814
811
  } else {
815
812
  parts.push(Brackets.parseLeft(op, stretchy));
@@ -822,10 +819,9 @@
822
819
  parts.push(parse(node));
823
820
  }
824
821
  });
825
- if(lefts.length > 0){
826
- for(let i=0; i < lefts.length; i++){
827
- parts.push("\\right.");
828
- }
822
+ // Only add \right. if there are unmatched left brackets
823
+ if(lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
824
+ parts.push("\\right.");
829
825
  }
830
826
  lefts = undefined;
831
827
  return parts;
@@ -837,7 +833,28 @@
837
833
  const nodeName = NodeTool.getNodeName(node);
838
834
  switch(nodeName){
839
835
  case 'msub':
840
- render = getRender_default("@1_{@2}");
836
+ render = function(node, children) {
837
+ if (!children || children.length < 2) return '';
838
+
839
+ const base = parse(children[0]);
840
+ if (!base) return '';
841
+
842
+ const sub = children[1];
843
+ if (!sub) return base;
844
+
845
+ // Handle nested subscript with empty mrow
846
+ if (NodeTool.getNodeName(sub) === 'msub' &&
847
+ sub.firstChild &&
848
+ NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
849
+ (!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
850
+ const lastChild = sub.lastChild;
851
+ if (!lastChild) return base;
852
+ return `${base}_${parse(lastChild)}`;
853
+ }
854
+
855
+ // Regular subscript
856
+ return `${base}_{${parse(sub)}}`;
857
+ };
841
858
  break;
842
859
  case 'msup':
843
860
  render = getRender_default("@1^{@2}");
@@ -899,10 +916,32 @@
899
916
 
900
917
  function renderTable(node, children) {
901
918
  const template = "\\begin{array}{l}@content\\end{array}";
902
- const render = getRender_joinSeparator(template, "\\\\");
919
+ // Remove extra backslash and add proper spacing
920
+ const render = getRender_joinSeparator(template, " \\\\ ");
903
921
  return render(node, children);
904
922
  }
905
923
 
924
+ function renderMover(node, children) {
925
+ // Check if children exists and has enough elements
926
+ if (!children || children.length < 2) return '';
927
+
928
+ const baseNode = children[0];
929
+ const overNode = children[1];
930
+
931
+ // Check if nodes exist before accessing
932
+ if (!baseNode || !overNode) return '';
933
+
934
+ const overText = NodeTool.getNodeText(overNode)?.trim() || '';
935
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
936
+
937
+ // Handle vector notation
938
+ if (overText === "→" && isAccent) {
939
+ return `\\vec{${parse(baseNode)}}`;
940
+ }
941
+
942
+ return parse(baseNode);
943
+ }
944
+
906
945
  function renderMfenced(node, children) {
907
946
  const [open, close, separatorsStr] = [
908
947
  NodeTool.getAttr(node, 'open', '('),
@@ -913,22 +952,25 @@
913
952
  const parts = renderChildren(children);
914
953
  const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
915
954
 
916
- // Handle empty open/close brackets
955
+ // Handle nested absolute value cases
956
+ if (open === '|' && close === '') {
957
+ return `\\left|${content}`;
958
+ }
917
959
  if (open === '' && close === '|') {
918
- return `|${content}|`;
960
+ return `${content}\\right|`;
919
961
  }
920
962
 
921
- // Handle absolute value notation
922
- if (open === '|' && (close === '|' || close === '')) {
923
- return `\\left|${content}\\right|`;
963
+ // Handle system of equations with curly brace
964
+ if (open === '{') {
965
+ return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
924
966
  }
925
-
926
- // Regular parentheses
927
- if (open === '(' && close === ')') {
928
- return `\\left(${content}\\right)`;
967
+
968
+ // Handle regular absolute value notation
969
+ if (open === '|' && close === '|') {
970
+ return `\\left|${content}\\right|`;
929
971
  }
930
972
 
931
- // Default case
973
+ // Regular cases with proper closing
932
974
  return `\\left${open}${content}\\right${close || '.'}`;
933
975
  }
934
976
 
@@ -1007,62 +1049,6 @@
1007
1049
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
1008
1050
  }
1009
1051
 
1010
- function renderMover(node, children) {
1011
- const nodes = flattenNodeTreeByNodeName(node, 'mover');
1012
- let result = undefined;
1013
-
1014
- // Get the base node and check if it's a subscript or mrow
1015
- const baseNode = children[0];
1016
- const overNode = children[1];
1017
- const nodeName = NodeTool.getNodeName(baseNode);
1018
- const isSubscript = nodeName === 'msub';
1019
- const isMrow = nodeName === 'mrow';
1020
-
1021
- // Handle case where the base is an arrow and mrow is above
1022
- const baseText = NodeTool.getNodeText(baseNode).trim();
1023
- if (baseText === "→" && NodeTool.getNodeName(overNode) === "mrow") {
1024
- return "\\rightarrow";
1025
- }
1026
-
1027
- if (isSubscript) {
1028
- // Handle case like n₂ with arrow
1029
- const base = parse(baseNode);
1030
- return `\\overrightarrow{${base}}`;
1031
- }
1032
-
1033
- if (isMrow) {
1034
- // Handle case like 0 with arrow
1035
- const base = parse(baseNode);
1036
- const overText = NodeTool.getNodeText(overNode).trim();
1037
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1038
-
1039
- if (overText === "→" && isAccent) {
1040
- return `\\overrightarrow{${base}}`;
1041
- }
1042
- }
1043
-
1044
- // Handle case where there is only an arrow with no base
1045
- const overText = NodeTool.getNodeText(overNode).trim();
1046
- if (overText === "→" && NodeTool.getNodeText(baseNode).trim() === "") {
1047
- return `\\rightarrow`; // Return just the arrow
1048
- }
1049
-
1050
- for (let i = 0; i < nodes.length - 1; i++) {
1051
- if (!result) {
1052
- result = parse(nodes[i]);
1053
- }
1054
-
1055
- const _overNode = nodes[i + 1];
1056
- const overText = NodeTool.getNodeText(_overNode).trim();
1057
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1058
-
1059
- if (overText === "→" && isAccent) {
1060
- return `\\overrightarrow{${result}}`;
1061
- }
1062
- }
1063
- return result;
1064
- }
1065
-
1066
1052
  function renderMunder(node, children){
1067
1053
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
1068
1054
  let result = undefined;
@@ -147,7 +147,9 @@ const NodeTool = {
147
147
  return node.children;
148
148
  },
149
149
  getNodeName: function(node) {
150
- return node.tagName.toLowerCase();
150
+ if (!node) return '';
151
+ const localName = node.localName || node.nodeName;
152
+ return localName ? localName.toLowerCase() : '';
151
153
  },
152
154
  getNodeText: function(node) {
153
155
  return node.textContent;
@@ -704,9 +706,9 @@ function parseLeaf(node) {
704
706
  function parseOperator(node) {
705
707
  let it = NodeTool.getNodeText(node).trim();
706
708
 
707
- // Special case for arrow (→)
709
+ // Special case for arrow (→) with proper spacing
708
710
  if (it === "→") {
709
- return "\\rightarrow";
711
+ return " \\rightarrow "; // Add spaces around arrow
710
712
  }
711
713
 
712
714
  it = MathSymbol.parseOperator(it);
@@ -789,6 +791,7 @@ function renderChildren(children) {
789
791
  if(Brackets.contains(op)){
790
792
  let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
791
793
  stretchy = ['', 'true'].indexOf(stretchy) > -1;
794
+
792
795
  if(Brackets.isRight(op)){
793
796
  const nearLeft = lefts[lefts.length - 1];
794
797
  if(nearLeft){
@@ -796,16 +799,10 @@ function renderChildren(children) {
796
799
  parts.push(Brackets.parseRight(op, stretchy));
797
800
  lefts.pop();
798
801
  } else {
799
- if(Brackets.isLeft(op)) {
800
- parts.push(Brackets.parseLeft(op, stretchy));
801
- lefts.push(op);
802
- }
803
- }
804
- }else {
805
- if(Brackets.isLeft(op)) {
806
- parts.push(Brackets.parseLeft(op, stretchy));
807
- lefts.push(op);
802
+ parts.push(`\\right${op}`);
808
803
  }
804
+ } else {
805
+ parts.push(`\\right${op}`);
809
806
  }
810
807
  } else {
811
808
  parts.push(Brackets.parseLeft(op, stretchy));
@@ -818,10 +815,9 @@ function renderChildren(children) {
818
815
  parts.push(parse(node));
819
816
  }
820
817
  });
821
- if(lefts.length > 0){
822
- for(let i=0; i < lefts.length; i++){
823
- parts.push("\\right.");
824
- }
818
+ // Only add \right. if there are unmatched left brackets
819
+ if(lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
820
+ parts.push("\\right.");
825
821
  }
826
822
  lefts = undefined;
827
823
  return parts;
@@ -833,7 +829,28 @@ function getRender(node) {
833
829
  const nodeName = NodeTool.getNodeName(node);
834
830
  switch(nodeName){
835
831
  case 'msub':
836
- render = getRender_default("@1_{@2}");
832
+ render = function(node, children) {
833
+ if (!children || children.length < 2) return '';
834
+
835
+ const base = parse(children[0]);
836
+ if (!base) return '';
837
+
838
+ const sub = children[1];
839
+ if (!sub) return base;
840
+
841
+ // Handle nested subscript with empty mrow
842
+ if (NodeTool.getNodeName(sub) === 'msub' &&
843
+ sub.firstChild &&
844
+ NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
845
+ (!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
846
+ const lastChild = sub.lastChild;
847
+ if (!lastChild) return base;
848
+ return `${base}_${parse(lastChild)}`;
849
+ }
850
+
851
+ // Regular subscript
852
+ return `${base}_{${parse(sub)}}`;
853
+ };
837
854
  break;
838
855
  case 'msup':
839
856
  render = getRender_default("@1^{@2}");
@@ -895,10 +912,32 @@ function getRender(node) {
895
912
 
896
913
  function renderTable(node, children) {
897
914
  const template = "\\begin{array}{l}@content\\end{array}";
898
- const render = getRender_joinSeparator(template, "\\\\");
915
+ // Remove extra backslash and add proper spacing
916
+ const render = getRender_joinSeparator(template, " \\\\ ");
899
917
  return render(node, children);
900
918
  }
901
919
 
920
+ function renderMover(node, children) {
921
+ // Check if children exists and has enough elements
922
+ if (!children || children.length < 2) return '';
923
+
924
+ const baseNode = children[0];
925
+ const overNode = children[1];
926
+
927
+ // Check if nodes exist before accessing
928
+ if (!baseNode || !overNode) return '';
929
+
930
+ const overText = NodeTool.getNodeText(overNode)?.trim() || '';
931
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
932
+
933
+ // Handle vector notation
934
+ if (overText === "→" && isAccent) {
935
+ return `\\vec{${parse(baseNode)}}`;
936
+ }
937
+
938
+ return parse(baseNode);
939
+ }
940
+
902
941
  function renderMfenced(node, children) {
903
942
  const [open, close, separatorsStr] = [
904
943
  NodeTool.getAttr(node, 'open', '('),
@@ -909,22 +948,25 @@ function renderMfenced(node, children) {
909
948
  const parts = renderChildren(children);
910
949
  const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
911
950
 
912
- // Handle empty open/close brackets
951
+ // Handle nested absolute value cases
952
+ if (open === '|' && close === '') {
953
+ return `\\left|${content}`;
954
+ }
913
955
  if (open === '' && close === '|') {
914
- return `|${content}|`;
956
+ return `${content}\\right|`;
915
957
  }
916
958
 
917
- // Handle absolute value notation
918
- if (open === '|' && (close === '|' || close === '')) {
919
- return `\\left|${content}\\right|`;
959
+ // Handle system of equations with curly brace
960
+ if (open === '{') {
961
+ return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
920
962
  }
921
-
922
- // Regular parentheses
923
- if (open === '(' && close === ')') {
924
- return `\\left(${content}\\right)`;
963
+
964
+ // Handle regular absolute value notation
965
+ if (open === '|' && close === '|') {
966
+ return `\\left|${content}\\right|`;
925
967
  }
926
968
 
927
- // Default case
969
+ // Regular cases with proper closing
928
970
  return `\\left${open}${content}\\right${close || '.'}`;
929
971
  }
930
972
 
@@ -1003,62 +1045,6 @@ function renderMmultiscripts(node, children) {
1003
1045
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
1004
1046
  }
1005
1047
 
1006
- function renderMover(node, children) {
1007
- const nodes = flattenNodeTreeByNodeName(node, 'mover');
1008
- let result = undefined;
1009
-
1010
- // Get the base node and check if it's a subscript or mrow
1011
- const baseNode = children[0];
1012
- const overNode = children[1];
1013
- const nodeName = NodeTool.getNodeName(baseNode);
1014
- const isSubscript = nodeName === 'msub';
1015
- const isMrow = nodeName === 'mrow';
1016
-
1017
- // Handle case where the base is an arrow and mrow is above
1018
- const baseText = NodeTool.getNodeText(baseNode).trim();
1019
- if (baseText === "→" && NodeTool.getNodeName(overNode) === "mrow") {
1020
- return "\\rightarrow";
1021
- }
1022
-
1023
- if (isSubscript) {
1024
- // Handle case like n₂ with arrow
1025
- const base = parse(baseNode);
1026
- return `\\overrightarrow{${base}}`;
1027
- }
1028
-
1029
- if (isMrow) {
1030
- // Handle case like 0 with arrow
1031
- const base = parse(baseNode);
1032
- const overText = NodeTool.getNodeText(overNode).trim();
1033
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1034
-
1035
- if (overText === "→" && isAccent) {
1036
- return `\\overrightarrow{${base}}`;
1037
- }
1038
- }
1039
-
1040
- // Handle case where there is only an arrow with no base
1041
- const overText = NodeTool.getNodeText(overNode).trim();
1042
- if (overText === "→" && NodeTool.getNodeText(baseNode).trim() === "") {
1043
- return `\\rightarrow`; // Return just the arrow
1044
- }
1045
-
1046
- for (let i = 0; i < nodes.length - 1; i++) {
1047
- if (!result) {
1048
- result = parse(nodes[i]);
1049
- }
1050
-
1051
- const _overNode = nodes[i + 1];
1052
- const overText = NodeTool.getNodeText(_overNode).trim();
1053
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1054
-
1055
- if (overText === "→" && isAccent) {
1056
- return `\\overrightarrow{${result}}`;
1057
- }
1058
- }
1059
- return result;
1060
- }
1061
-
1062
1048
  function renderMunder(node, children){
1063
1049
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
1064
1050
  let result = undefined;
@@ -145,7 +145,9 @@ const NodeTool = {
145
145
  return node.children;
146
146
  },
147
147
  getNodeName: function(node) {
148
- return node.tagName.toLowerCase();
148
+ if (!node) return '';
149
+ const localName = node.localName || node.nodeName;
150
+ return localName ? localName.toLowerCase() : '';
149
151
  },
150
152
  getNodeText: function(node) {
151
153
  return node.textContent;
@@ -702,9 +704,9 @@ function parseLeaf(node) {
702
704
  function parseOperator(node) {
703
705
  let it = NodeTool.getNodeText(node).trim();
704
706
 
705
- // Special case for arrow (→)
707
+ // Special case for arrow (→) with proper spacing
706
708
  if (it === "→") {
707
- return "\\rightarrow";
709
+ return " \\rightarrow "; // Add spaces around arrow
708
710
  }
709
711
 
710
712
  it = MathSymbol.parseOperator(it);
@@ -787,6 +789,7 @@ function renderChildren(children) {
787
789
  if(Brackets.contains(op)){
788
790
  let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
789
791
  stretchy = ['', 'true'].indexOf(stretchy) > -1;
792
+
790
793
  if(Brackets.isRight(op)){
791
794
  const nearLeft = lefts[lefts.length - 1];
792
795
  if(nearLeft){
@@ -794,16 +797,10 @@ function renderChildren(children) {
794
797
  parts.push(Brackets.parseRight(op, stretchy));
795
798
  lefts.pop();
796
799
  } else {
797
- if(Brackets.isLeft(op)) {
798
- parts.push(Brackets.parseLeft(op, stretchy));
799
- lefts.push(op);
800
- }
801
- }
802
- }else {
803
- if(Brackets.isLeft(op)) {
804
- parts.push(Brackets.parseLeft(op, stretchy));
805
- lefts.push(op);
800
+ parts.push(`\\right${op}`);
806
801
  }
802
+ } else {
803
+ parts.push(`\\right${op}`);
807
804
  }
808
805
  } else {
809
806
  parts.push(Brackets.parseLeft(op, stretchy));
@@ -816,10 +813,9 @@ function renderChildren(children) {
816
813
  parts.push(parse(node));
817
814
  }
818
815
  });
819
- if(lefts.length > 0){
820
- for(let i=0; i < lefts.length; i++){
821
- parts.push("\\right.");
822
- }
816
+ // Only add \right. if there are unmatched left brackets
817
+ if(lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
818
+ parts.push("\\right.");
823
819
  }
824
820
  lefts = undefined;
825
821
  return parts;
@@ -831,7 +827,28 @@ function getRender(node) {
831
827
  const nodeName = NodeTool.getNodeName(node);
832
828
  switch(nodeName){
833
829
  case 'msub':
834
- render = getRender_default("@1_{@2}");
830
+ render = function(node, children) {
831
+ if (!children || children.length < 2) return '';
832
+
833
+ const base = parse(children[0]);
834
+ if (!base) return '';
835
+
836
+ const sub = children[1];
837
+ if (!sub) return base;
838
+
839
+ // Handle nested subscript with empty mrow
840
+ if (NodeTool.getNodeName(sub) === 'msub' &&
841
+ sub.firstChild &&
842
+ NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
843
+ (!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
844
+ const lastChild = sub.lastChild;
845
+ if (!lastChild) return base;
846
+ return `${base}_${parse(lastChild)}`;
847
+ }
848
+
849
+ // Regular subscript
850
+ return `${base}_{${parse(sub)}}`;
851
+ };
835
852
  break;
836
853
  case 'msup':
837
854
  render = getRender_default("@1^{@2}");
@@ -893,10 +910,32 @@ function getRender(node) {
893
910
 
894
911
  function renderTable(node, children) {
895
912
  const template = "\\begin{array}{l}@content\\end{array}";
896
- const render = getRender_joinSeparator(template, "\\\\");
913
+ // Remove extra backslash and add proper spacing
914
+ const render = getRender_joinSeparator(template, " \\\\ ");
897
915
  return render(node, children);
898
916
  }
899
917
 
918
+ function renderMover(node, children) {
919
+ // Check if children exists and has enough elements
920
+ if (!children || children.length < 2) return '';
921
+
922
+ const baseNode = children[0];
923
+ const overNode = children[1];
924
+
925
+ // Check if nodes exist before accessing
926
+ if (!baseNode || !overNode) return '';
927
+
928
+ const overText = NodeTool.getNodeText(overNode)?.trim() || '';
929
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
930
+
931
+ // Handle vector notation
932
+ if (overText === "→" && isAccent) {
933
+ return `\\vec{${parse(baseNode)}}`;
934
+ }
935
+
936
+ return parse(baseNode);
937
+ }
938
+
900
939
  function renderMfenced(node, children) {
901
940
  const [open, close, separatorsStr] = [
902
941
  NodeTool.getAttr(node, 'open', '('),
@@ -907,22 +946,25 @@ function renderMfenced(node, children) {
907
946
  const parts = renderChildren(children);
908
947
  const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
909
948
 
910
- // Handle empty open/close brackets
949
+ // Handle nested absolute value cases
950
+ if (open === '|' && close === '') {
951
+ return `\\left|${content}`;
952
+ }
911
953
  if (open === '' && close === '|') {
912
- return `|${content}|`;
954
+ return `${content}\\right|`;
913
955
  }
914
956
 
915
- // Handle absolute value notation
916
- if (open === '|' && (close === '|' || close === '')) {
917
- return `\\left|${content}\\right|`;
957
+ // Handle system of equations with curly brace
958
+ if (open === '{') {
959
+ return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
918
960
  }
919
-
920
- // Regular parentheses
921
- if (open === '(' && close === ')') {
922
- return `\\left(${content}\\right)`;
961
+
962
+ // Handle regular absolute value notation
963
+ if (open === '|' && close === '|') {
964
+ return `\\left|${content}\\right|`;
923
965
  }
924
966
 
925
- // Default case
967
+ // Regular cases with proper closing
926
968
  return `\\left${open}${content}\\right${close || '.'}`;
927
969
  }
928
970
 
@@ -1001,62 +1043,6 @@ function renderMmultiscripts(node, children) {
1001
1043
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
1002
1044
  }
1003
1045
 
1004
- function renderMover(node, children) {
1005
- const nodes = flattenNodeTreeByNodeName(node, 'mover');
1006
- let result = undefined;
1007
-
1008
- // Get the base node and check if it's a subscript or mrow
1009
- const baseNode = children[0];
1010
- const overNode = children[1];
1011
- const nodeName = NodeTool.getNodeName(baseNode);
1012
- const isSubscript = nodeName === 'msub';
1013
- const isMrow = nodeName === 'mrow';
1014
-
1015
- // Handle case where the base is an arrow and mrow is above
1016
- const baseText = NodeTool.getNodeText(baseNode).trim();
1017
- if (baseText === "→" && NodeTool.getNodeName(overNode) === "mrow") {
1018
- return "\\rightarrow";
1019
- }
1020
-
1021
- if (isSubscript) {
1022
- // Handle case like n₂ with arrow
1023
- const base = parse(baseNode);
1024
- return `\\overrightarrow{${base}}`;
1025
- }
1026
-
1027
- if (isMrow) {
1028
- // Handle case like 0 with arrow
1029
- const base = parse(baseNode);
1030
- const overText = NodeTool.getNodeText(overNode).trim();
1031
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1032
-
1033
- if (overText === "→" && isAccent) {
1034
- return `\\overrightarrow{${base}}`;
1035
- }
1036
- }
1037
-
1038
- // Handle case where there is only an arrow with no base
1039
- const overText = NodeTool.getNodeText(overNode).trim();
1040
- if (overText === "→" && NodeTool.getNodeText(baseNode).trim() === "") {
1041
- return `\\rightarrow`; // Return just the arrow
1042
- }
1043
-
1044
- for (let i = 0; i < nodes.length - 1; i++) {
1045
- if (!result) {
1046
- result = parse(nodes[i]);
1047
- }
1048
-
1049
- const _overNode = nodes[i + 1];
1050
- const overText = NodeTool.getNodeText(_overNode).trim();
1051
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1052
-
1053
- if (overText === "→" && isAccent) {
1054
- return `\\overrightarrow{${result}}`;
1055
- }
1056
- }
1057
- return result;
1058
- }
1059
-
1060
1046
  function renderMunder(node, children){
1061
1047
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
1062
1048
  let result = undefined;
@@ -151,7 +151,9 @@
151
151
  return node.children;
152
152
  },
153
153
  getNodeName: function(node) {
154
- return node.tagName.toLowerCase();
154
+ if (!node) return '';
155
+ const localName = node.localName || node.nodeName;
156
+ return localName ? localName.toLowerCase() : '';
155
157
  },
156
158
  getNodeText: function(node) {
157
159
  return node.textContent;
@@ -708,9 +710,9 @@
708
710
  function parseOperator(node) {
709
711
  let it = NodeTool.getNodeText(node).trim();
710
712
 
711
- // Special case for arrow (→)
713
+ // Special case for arrow (→) with proper spacing
712
714
  if (it === "→") {
713
- return "\\rightarrow";
715
+ return " \\rightarrow "; // Add spaces around arrow
714
716
  }
715
717
 
716
718
  it = MathSymbol.parseOperator(it);
@@ -793,6 +795,7 @@
793
795
  if(Brackets.contains(op)){
794
796
  let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
795
797
  stretchy = ['', 'true'].indexOf(stretchy) > -1;
798
+
796
799
  if(Brackets.isRight(op)){
797
800
  const nearLeft = lefts[lefts.length - 1];
798
801
  if(nearLeft){
@@ -800,16 +803,10 @@
800
803
  parts.push(Brackets.parseRight(op, stretchy));
801
804
  lefts.pop();
802
805
  } else {
803
- if(Brackets.isLeft(op)) {
804
- parts.push(Brackets.parseLeft(op, stretchy));
805
- lefts.push(op);
806
- }
807
- }
808
- }else {
809
- if(Brackets.isLeft(op)) {
810
- parts.push(Brackets.parseLeft(op, stretchy));
811
- lefts.push(op);
806
+ parts.push(`\\right${op}`);
812
807
  }
808
+ } else {
809
+ parts.push(`\\right${op}`);
813
810
  }
814
811
  } else {
815
812
  parts.push(Brackets.parseLeft(op, stretchy));
@@ -822,10 +819,9 @@
822
819
  parts.push(parse(node));
823
820
  }
824
821
  });
825
- if(lefts.length > 0){
826
- for(let i=0; i < lefts.length; i++){
827
- parts.push("\\right.");
828
- }
822
+ // Only add \right. if there are unmatched left brackets
823
+ if(lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
824
+ parts.push("\\right.");
829
825
  }
830
826
  lefts = undefined;
831
827
  return parts;
@@ -837,7 +833,28 @@
837
833
  const nodeName = NodeTool.getNodeName(node);
838
834
  switch(nodeName){
839
835
  case 'msub':
840
- render = getRender_default("@1_{@2}");
836
+ render = function(node, children) {
837
+ if (!children || children.length < 2) return '';
838
+
839
+ const base = parse(children[0]);
840
+ if (!base) return '';
841
+
842
+ const sub = children[1];
843
+ if (!sub) return base;
844
+
845
+ // Handle nested subscript with empty mrow
846
+ if (NodeTool.getNodeName(sub) === 'msub' &&
847
+ sub.firstChild &&
848
+ NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
849
+ (!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
850
+ const lastChild = sub.lastChild;
851
+ if (!lastChild) return base;
852
+ return `${base}_${parse(lastChild)}`;
853
+ }
854
+
855
+ // Regular subscript
856
+ return `${base}_{${parse(sub)}}`;
857
+ };
841
858
  break;
842
859
  case 'msup':
843
860
  render = getRender_default("@1^{@2}");
@@ -899,10 +916,32 @@
899
916
 
900
917
  function renderTable(node, children) {
901
918
  const template = "\\begin{array}{l}@content\\end{array}";
902
- const render = getRender_joinSeparator(template, "\\\\");
919
+ // Remove extra backslash and add proper spacing
920
+ const render = getRender_joinSeparator(template, " \\\\ ");
903
921
  return render(node, children);
904
922
  }
905
923
 
924
+ function renderMover(node, children) {
925
+ // Check if children exists and has enough elements
926
+ if (!children || children.length < 2) return '';
927
+
928
+ const baseNode = children[0];
929
+ const overNode = children[1];
930
+
931
+ // Check if nodes exist before accessing
932
+ if (!baseNode || !overNode) return '';
933
+
934
+ const overText = NodeTool.getNodeText(overNode)?.trim() || '';
935
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
936
+
937
+ // Handle vector notation
938
+ if (overText === "→" && isAccent) {
939
+ return `\\vec{${parse(baseNode)}}`;
940
+ }
941
+
942
+ return parse(baseNode);
943
+ }
944
+
906
945
  function renderMfenced(node, children) {
907
946
  const [open, close, separatorsStr] = [
908
947
  NodeTool.getAttr(node, 'open', '('),
@@ -913,22 +952,25 @@
913
952
  const parts = renderChildren(children);
914
953
  const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
915
954
 
916
- // Handle empty open/close brackets
955
+ // Handle nested absolute value cases
956
+ if (open === '|' && close === '') {
957
+ return `\\left|${content}`;
958
+ }
917
959
  if (open === '' && close === '|') {
918
- return `|${content}|`;
960
+ return `${content}\\right|`;
919
961
  }
920
962
 
921
- // Handle absolute value notation
922
- if (open === '|' && (close === '|' || close === '')) {
923
- return `\\left|${content}\\right|`;
963
+ // Handle system of equations with curly brace
964
+ if (open === '{') {
965
+ return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
924
966
  }
925
-
926
- // Regular parentheses
927
- if (open === '(' && close === ')') {
928
- return `\\left(${content}\\right)`;
967
+
968
+ // Handle regular absolute value notation
969
+ if (open === '|' && close === '|') {
970
+ return `\\left|${content}\\right|`;
929
971
  }
930
972
 
931
- // Default case
973
+ // Regular cases with proper closing
932
974
  return `\\left${open}${content}\\right${close || '.'}`;
933
975
  }
934
976
 
@@ -1007,62 +1049,6 @@
1007
1049
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
1008
1050
  }
1009
1051
 
1010
- function renderMover(node, children) {
1011
- const nodes = flattenNodeTreeByNodeName(node, 'mover');
1012
- let result = undefined;
1013
-
1014
- // Get the base node and check if it's a subscript or mrow
1015
- const baseNode = children[0];
1016
- const overNode = children[1];
1017
- const nodeName = NodeTool.getNodeName(baseNode);
1018
- const isSubscript = nodeName === 'msub';
1019
- const isMrow = nodeName === 'mrow';
1020
-
1021
- // Handle case where the base is an arrow and mrow is above
1022
- const baseText = NodeTool.getNodeText(baseNode).trim();
1023
- if (baseText === "→" && NodeTool.getNodeName(overNode) === "mrow") {
1024
- return "\\rightarrow";
1025
- }
1026
-
1027
- if (isSubscript) {
1028
- // Handle case like n₂ with arrow
1029
- const base = parse(baseNode);
1030
- return `\\overrightarrow{${base}}`;
1031
- }
1032
-
1033
- if (isMrow) {
1034
- // Handle case like 0 with arrow
1035
- const base = parse(baseNode);
1036
- const overText = NodeTool.getNodeText(overNode).trim();
1037
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1038
-
1039
- if (overText === "→" && isAccent) {
1040
- return `\\overrightarrow{${base}}`;
1041
- }
1042
- }
1043
-
1044
- // Handle case where there is only an arrow with no base
1045
- const overText = NodeTool.getNodeText(overNode).trim();
1046
- if (overText === "→" && NodeTool.getNodeText(baseNode).trim() === "") {
1047
- return `\\rightarrow`; // Return just the arrow
1048
- }
1049
-
1050
- for (let i = 0; i < nodes.length - 1; i++) {
1051
- if (!result) {
1052
- result = parse(nodes[i]);
1053
- }
1054
-
1055
- const _overNode = nodes[i + 1];
1056
- const overText = NodeTool.getNodeText(_overNode).trim();
1057
- const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1058
-
1059
- if (overText === "→" && isAccent) {
1060
- return `\\overrightarrow{${result}}`;
1061
- }
1062
- }
1063
- return result;
1064
- }
1065
-
1066
1052
  function renderMunder(node, children){
1067
1053
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
1068
1054
  let result = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ed-mathml2tex",
3
- "version": "0.0.8",
3
+ "version": "0.1.0",
4
4
  "description": "Convert mathml to latex.",
5
5
  "author": "Mika",
6
6
  "license": "MIT",