@drghaliasri/butex 5.3.2 → 5.4.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.
package/dist/react.mjs CHANGED
@@ -36,6 +36,9 @@ function createEditorNode(kind, expr) {
36
36
  denominator: null,
37
37
  radicand: null,
38
38
  rootIndex: null,
39
+ overExpr: null,
40
+ underExpr: null,
41
+ baseExpr: null,
39
42
  envName: null,
40
43
  matrixStyle: null,
41
44
  matrixRows: null,
@@ -64,6 +67,26 @@ function createSqrtNode() {
64
67
  rootIndex: createEditorChain()
65
68
  };
66
69
  }
70
+ function createOversetNode() {
71
+ return {
72
+ ...createEditorNode("overset", ""),
73
+ overExpr: createEditorChain(),
74
+ baseExpr: createEditorChain()
75
+ };
76
+ }
77
+ function createUndersetNode() {
78
+ return {
79
+ ...createEditorNode("underset", ""),
80
+ underExpr: createEditorChain(),
81
+ baseExpr: createEditorChain()
82
+ };
83
+ }
84
+ function createAccentNode(accentId) {
85
+ return {
86
+ ...createEditorNode("accent", accentId),
87
+ baseExpr: createEditorChain()
88
+ };
89
+ }
67
90
  function createGridEnvNode(envName, rows, columns, columnAlignments) {
68
91
  const safeRows = Math.max(1, Math.min(12, Math.trunc(rows)));
69
92
  const safeColumns = Math.max(1, Math.min(12, Math.trunc(columns)));
@@ -105,6 +128,144 @@ function cloneEditorSession(session) {
105
128
  };
106
129
  }
107
130
 
131
+ // src/editor/accentCommands.ts
132
+ var ACCENT_COMMANDS = {
133
+ vec: {
134
+ id: "vec",
135
+ englishTex: "\\vec",
136
+ arabicTex: "\\arabvec",
137
+ placement: "over",
138
+ label: "\u20D7",
139
+ title: "\u0633\u0647\u0645 \u0641\u0648\u0642"
140
+ },
141
+ hat: {
142
+ id: "hat",
143
+ englishTex: "\\hat",
144
+ arabicTex: "\\hat",
145
+ placement: "over",
146
+ label: "\u02C6",
147
+ title: "\u0642\u0628\u0639\u0629"
148
+ },
149
+ tilde: {
150
+ id: "tilde",
151
+ englishTex: "\\tilde",
152
+ arabicTex: "\\tilde",
153
+ placement: "over",
154
+ label: "\u02DC",
155
+ title: "\u0645\u062F\u0629"
156
+ },
157
+ dot: {
158
+ id: "dot",
159
+ englishTex: "\\dot",
160
+ arabicTex: "\\dot",
161
+ placement: "over",
162
+ label: "\u02D9",
163
+ title: "\u0646\u0642\u0637\u0629 \u0641\u0648\u0642"
164
+ },
165
+ ddot: {
166
+ id: "ddot",
167
+ englishTex: "\\ddot",
168
+ arabicTex: "\\ddot",
169
+ placement: "over",
170
+ label: "\xA8",
171
+ title: "\u0646\u0642\u0637\u062A\u0627\u0646 \u0641\u0648\u0642"
172
+ },
173
+ dddot: {
174
+ id: "dddot",
175
+ englishTex: "\\dddot",
176
+ arabicTex: "\\dddot",
177
+ placement: "over",
178
+ label: "\u20DB",
179
+ title: "\u062B\u0644\u0627\u062B \u0646\u0642\u0627\u0637 \u0641\u0648\u0642"
180
+ },
181
+ bar: {
182
+ id: "bar",
183
+ englishTex: "\\bar",
184
+ arabicTex: "\\bar",
185
+ placement: "over",
186
+ label: "\xAF",
187
+ title: "\u062E\u0637 \u0641\u0648\u0642"
188
+ },
189
+ check: {
190
+ id: "check",
191
+ englishTex: "\\check",
192
+ arabicTex: "\\check",
193
+ placement: "over",
194
+ label: "\u02C7",
195
+ title: "\u0639\u0644\u0627\u0645\u0629 \u062A\u062D\u0642\u0642"
196
+ },
197
+ breve: {
198
+ id: "breve",
199
+ englishTex: "\\breve",
200
+ arabicTex: "\\breve",
201
+ placement: "over",
202
+ label: "\u02D8",
203
+ title: "\u0642\u0648\u0633 \u0642\u0635\u064A\u0631"
204
+ },
205
+ acute: {
206
+ id: "acute",
207
+ englishTex: "\\acute",
208
+ arabicTex: "\\grave",
209
+ placement: "over",
210
+ label: "\xB4",
211
+ title: "\u0646\u0628\u0631\u0629 \u062D\u0627\u062F\u0629"
212
+ },
213
+ grave: {
214
+ id: "grave",
215
+ englishTex: "\\grave",
216
+ arabicTex: "\\acute",
217
+ placement: "over",
218
+ label: "`",
219
+ title: "\u0646\u0628\u0631\u0629 \u062B\u0642\u064A\u0644\u0629"
220
+ },
221
+ overline: {
222
+ id: "overline",
223
+ englishTex: "\\overline",
224
+ arabicTex: "\\overline",
225
+ placement: "over",
226
+ label: "\u203E",
227
+ title: "\u062E\u0637 \u0639\u0644\u0648\u064A \u0645\u0645\u062A\u062F"
228
+ },
229
+ underline: {
230
+ id: "underline",
231
+ englishTex: "\\underline",
232
+ arabicTex: "\\underline",
233
+ placement: "under",
234
+ label: "_",
235
+ title: "\u062E\u0637 \u0633\u0641\u0644\u064A"
236
+ },
237
+ overleftarrow: {
238
+ id: "overleftarrow",
239
+ englishTex: "\\overleftarrow",
240
+ arabicTex: "\\overrightarrow",
241
+ placement: "over",
242
+ label: "\u2190",
243
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631"
244
+ },
245
+ overrightarrow: {
246
+ id: "overrightarrow",
247
+ englishTex: "\\overrightarrow",
248
+ arabicTex: "\\overleftarrow",
249
+ placement: "over",
250
+ label: "\u2192",
251
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646"
252
+ },
253
+ overleftrightarrow: {
254
+ id: "overleftrightarrow",
255
+ englishTex: "\\overleftrightarrow",
256
+ arabicTex: "\\overleftrightarrow",
257
+ placement: "over",
258
+ label: "\u2194",
259
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0645\u0632\u062F\u0648\u062C \u0627\u0644\u0627\u062A\u062C\u0627\u0647"
260
+ }
261
+ };
262
+ function accentCommandSpec(id) {
263
+ return Object.prototype.hasOwnProperty.call(ACCENT_COMMANDS, id) ? ACCENT_COMMANDS[id] : null;
264
+ }
265
+ function accentCommandsList() {
266
+ return Object.values(ACCENT_COMMANDS);
267
+ }
268
+
108
269
  // src/editor/atomicCommands.ts
109
270
  var ATOMIC_COMMANDS = {
110
271
  ///////////////////////// Functions ////////////////////////////////
@@ -525,7 +686,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
525
686
  id: "neq",
526
687
  category: "operators1",
527
688
  Tex: "\\neq",
528
- mirror: false,
689
+ mirror: true,
529
690
  Label: "\u2260",
530
691
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
531
692
  svg_path: null
@@ -535,6 +696,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
535
696
  category: "operators1",
536
697
  Tex: "\\approx",
537
698
  mirror: false,
699
+ displayMirror: true,
538
700
  Label: "\u2248",
539
701
  title: "\u062A\u0642\u0631\u064A\u0628\u0627 \u064A\u0633\u0627\u0648\u064A",
540
702
  svg_path: null
@@ -544,6 +706,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
544
706
  category: "operators1",
545
707
  Tex: "\\sim",
546
708
  mirror: false,
709
+ displayMirror: true,
547
710
  Label: "\u223C",
548
711
  title: "\u0645\u0634\u0627\u0628\u0647",
549
712
  svg_path: null
@@ -562,6 +725,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
562
725
  category: "operators1",
563
726
  Tex: "\\leq",
564
727
  mirror: true,
728
+ displayMirror: false,
565
729
  Label: "\u2264",
566
730
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
567
731
  svg_path: null
@@ -571,6 +735,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
571
735
  category: "operators1",
572
736
  Tex: "\\geq",
573
737
  mirror: true,
738
+ displayMirror: false,
574
739
  Label: "\u2265",
575
740
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
576
741
  svg_path: null
@@ -580,6 +745,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
580
745
  category: "operators1",
581
746
  Tex: "\\coloneqq",
582
747
  mirror: true,
748
+ displayMirror: false,
583
749
  Label: "\u2254",
584
750
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
585
751
  svg_path: null
@@ -589,6 +755,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
589
755
  category: "operators1",
590
756
  Tex: "\\eqqcolon",
591
757
  mirror: true,
758
+ displayMirror: false,
592
759
  Label: "\u2255",
593
760
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
594
761
  svg_path: null
@@ -599,6 +766,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
599
766
  category: "operators2",
600
767
  Tex: "\\propto",
601
768
  mirror: true,
769
+ displayMirror: false,
602
770
  Label: "\u221D",
603
771
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
604
772
  svg_path: null
@@ -608,6 +776,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
608
776
  category: "operators2",
609
777
  Tex: "\\in",
610
778
  mirror: true,
779
+ displayMirror: false,
611
780
  Label: "\u2208",
612
781
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
613
782
  svg_path: null
@@ -617,6 +786,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
617
786
  category: "operators2",
618
787
  Tex: "\\notin",
619
788
  mirror: false,
789
+ displayMirror: true,
620
790
  Label: "\u2209",
621
791
  title: "\u0644\u0627 \u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
622
792
  svg_path: null
@@ -626,6 +796,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
626
796
  category: "operators2",
627
797
  Tex: "\\subset",
628
798
  mirror: true,
799
+ displayMirror: false,
629
800
  Label: "\u2282",
630
801
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
631
802
  svg_path: null
@@ -635,6 +806,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
635
806
  category: "operators2",
636
807
  Tex: "\\supset",
637
808
  mirror: true,
809
+ displayMirror: false,
638
810
  Label: "\u2283",
639
811
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
640
812
  svg_path: null
@@ -644,6 +816,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
644
816
  category: "operators2",
645
817
  Tex: "\\subseteq",
646
818
  mirror: true,
819
+ displayMirror: false,
647
820
  Label: "\u2286",
648
821
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
649
822
  svg_path: null
@@ -653,6 +826,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
653
826
  category: "operators2",
654
827
  Tex: "\\supseteq",
655
828
  mirror: true,
829
+ displayMirror: false,
656
830
  Label: "\u2287",
657
831
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
658
832
  svg_path: null
@@ -662,6 +836,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
662
836
  category: "operators2",
663
837
  Tex: "\\cap",
664
838
  mirror: false,
839
+ displayMirror: true,
665
840
  Label: "\u2229",
666
841
  title: "\u062A\u0642\u0627\u0637\u0639",
667
842
  svg_path: null
@@ -671,6 +846,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
671
846
  category: "operators2",
672
847
  Tex: "\\cup",
673
848
  mirror: false,
849
+ displayMirror: true,
674
850
  Label: "\u222A",
675
851
  title: "\u0627\u062A\u062D\u0627\u062F",
676
852
  svg_path: null
@@ -680,6 +856,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
680
856
  category: "operators2",
681
857
  Tex: "\\emptyset",
682
858
  mirror: false,
859
+ displayMirror: true,
683
860
  Label: "\u2205",
684
861
  title: "\u0627\u0644\u0645\u062C\u0645\u0648\u0639\u0629 \u0627\u0644\u062E\u0627\u0644\u064A\u0629",
685
862
  svg_path: null
@@ -689,6 +866,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
689
866
  category: "operators2",
690
867
  Tex: "\\nsubseteq",
691
868
  mirror: true,
869
+ displayMirror: false,
692
870
  Label: "\u2288",
693
871
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
694
872
  svg_path: null
@@ -698,6 +876,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
698
876
  category: "operators2",
699
877
  Tex: "\\nsupseteq",
700
878
  mirror: true,
879
+ displayMirror: false,
701
880
  Label: "\u2289",
702
881
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
703
882
  svg_path: null
@@ -909,6 +1088,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
909
1088
  category: "integrals",
910
1089
  Tex: "\\iint",
911
1090
  mirror: true,
1091
+ displayMirror: false,
912
1092
  Label: "\u222C",
913
1093
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
914
1094
  svg_path: null
@@ -918,6 +1098,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
918
1098
  category: "integrals",
919
1099
  Tex: "\\iiint",
920
1100
  mirror: true,
1101
+ displayMirror: false,
921
1102
  Label: "\u222D",
922
1103
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
923
1104
  svg_path: null
@@ -927,6 +1108,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
927
1108
  category: "integrals",
928
1109
  Tex: "\\iiiint",
929
1110
  mirror: true,
1111
+ displayMirror: false,
930
1112
  Label: "\u2A0C",
931
1113
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
932
1114
  svg_path: null
@@ -936,6 +1118,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
936
1118
  category: "integrals",
937
1119
  Tex: "\\oint",
938
1120
  mirror: true,
1121
+ displayMirror: false,
939
1122
  Label: "\u222E",
940
1123
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
941
1124
  svg_path: null
@@ -945,6 +1128,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
945
1128
  category: "integrals",
946
1129
  Tex: "\\oiint",
947
1130
  mirror: true,
1131
+ displayMirror: false,
948
1132
  Label: "\u222F",
949
1133
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
950
1134
  svg_path: null
@@ -954,6 +1138,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
954
1138
  category: "integrals",
955
1139
  Tex: "\\oiiint",
956
1140
  mirror: true,
1141
+ displayMirror: false,
957
1142
  Label: "\u2230",
958
1143
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
959
1144
  svg_path: null
@@ -1148,6 +1333,22 @@ function renderMatrixRowsArabic(node, options) {
1148
1333
  }
1149
1334
  return `\\begin{${envName}}${rows.join(" \\\\\n")}\\end{${envName}}`;
1150
1335
  }
1336
+ function renderAccentEnglish(command, accent, base, options) {
1337
+ const baseTex = base ? renderChainEnglish(base, options) : "";
1338
+ const accentTex = accent ? renderChainEnglish(accent, options) : "";
1339
+ if (accentTex === "") {
1340
+ return baseTex;
1341
+ }
1342
+ return `\\${command}{${accentTex}}{${baseTex}}`;
1343
+ }
1344
+ function renderAccentArabic(command, accent, base, options) {
1345
+ const baseTex = base ? renderChainArabic(base, options) : "";
1346
+ const accentTex = accent ? renderChainArabic(accent, options) : "";
1347
+ if (accentTex === "") {
1348
+ return baseTex;
1349
+ }
1350
+ return `\\${command}{${accentTex}}{${baseTex}}`;
1351
+ }
1151
1352
  function escapeText(s) {
1152
1353
  return s.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
1153
1354
  }
@@ -1177,6 +1378,19 @@ function renderNodeEnglish(node, options) {
1177
1378
  const body = `\\sqrt${optionalRootIndexEnglish(node, options)}{${renderChainEnglish(node.radicand, options)}}`;
1178
1379
  return `${body}${latinScripts(node, options)}`;
1179
1380
  }
1381
+ if (node.kind === "overset" && node.baseExpr) {
1382
+ const body = renderAccentEnglish("overset", node.overExpr, node.baseExpr, options);
1383
+ return `${body}${latinScripts(node, options)}`;
1384
+ }
1385
+ if (node.kind === "underset" && node.baseExpr) {
1386
+ const body = renderAccentEnglish("underset", node.underExpr, node.baseExpr, options);
1387
+ return `${body}${latinScripts(node, options)}`;
1388
+ }
1389
+ if (node.kind === "accent" && node.baseExpr) {
1390
+ const cmd = accentCommandSpec(node.expr)?.englishTex ?? `\\${node.expr}`;
1391
+ const body = `${cmd}{${renderChainEnglish(node.baseExpr, options)}}`;
1392
+ return `${body}${latinScripts(node, options)}`;
1393
+ }
1180
1394
  if (node.kind === "env") {
1181
1395
  return `${renderMatrixRowsEnglish(node, options)}${latinScripts(node, options)}`;
1182
1396
  }
@@ -1209,6 +1423,19 @@ function renderNodeArabic(node, options) {
1209
1423
  const content = `\\arabsqrt${optionalRootIndexArabic(node, options)}{${renderChainArabic(node.radicand, options)}}`;
1210
1424
  return arabicScripts(node, content, options);
1211
1425
  }
1426
+ if (node.kind === "overset" && node.baseExpr) {
1427
+ const content = renderAccentArabic("overset", node.overExpr, node.baseExpr, options);
1428
+ return arabicScripts(node, content, options);
1429
+ }
1430
+ if (node.kind === "underset" && node.baseExpr) {
1431
+ const content = renderAccentArabic("underset", node.underExpr, node.baseExpr, options);
1432
+ return arabicScripts(node, content, options);
1433
+ }
1434
+ if (node.kind === "accent" && node.baseExpr) {
1435
+ const cmd = accentCommandSpec(node.expr)?.arabicTex ?? `\\${node.expr}`;
1436
+ const content = `${cmd}{${renderChainArabic(node.baseExpr, options)}}`;
1437
+ return arabicScripts(node, content, options);
1438
+ }
1212
1439
  if (node.kind === "env") {
1213
1440
  return arabicScripts(node, renderMatrixRowsArabic(node, options), options);
1214
1441
  }
@@ -1265,6 +1492,35 @@ function collectCaretPositions(chain) {
1265
1492
  if (node.radicand) {
1266
1493
  walkChain(node.radicand);
1267
1494
  }
1495
+ if (node.kind === "overset") {
1496
+ if (node.overExpr) {
1497
+ walkChain(node.overExpr);
1498
+ }
1499
+ if (node.baseExpr) {
1500
+ walkChain(node.baseExpr);
1501
+ }
1502
+ } else if (node.kind === "underset") {
1503
+ if (node.baseExpr) {
1504
+ walkChain(node.baseExpr);
1505
+ }
1506
+ if (node.underExpr) {
1507
+ walkChain(node.underExpr);
1508
+ }
1509
+ } else if (node.kind === "accent") {
1510
+ if (node.baseExpr) {
1511
+ walkChain(node.baseExpr);
1512
+ }
1513
+ } else {
1514
+ if (node.overExpr) {
1515
+ walkChain(node.overExpr);
1516
+ }
1517
+ if (node.underExpr) {
1518
+ walkChain(node.underExpr);
1519
+ }
1520
+ if (node.baseExpr) {
1521
+ walkChain(node.baseExpr);
1522
+ }
1523
+ }
1268
1524
  if (node.matrixRows) {
1269
1525
  for (const row of node.matrixRows) {
1270
1526
  for (const cell of row) {
@@ -1398,6 +1654,42 @@ function findChainById(root, chainId) {
1398
1654
  return nested;
1399
1655
  }
1400
1656
  }
1657
+ if (node.overExpr) {
1658
+ if (node.overExpr.id === chainId) {
1659
+ return {
1660
+ chain: node.overExpr,
1661
+ relation: { kind: "overExpr", ownerNodeId: node.id, ownerChainId: root.id }
1662
+ };
1663
+ }
1664
+ const nested = findChainById(node.overExpr, chainId);
1665
+ if (nested) {
1666
+ return nested;
1667
+ }
1668
+ }
1669
+ if (node.underExpr) {
1670
+ if (node.underExpr.id === chainId) {
1671
+ return {
1672
+ chain: node.underExpr,
1673
+ relation: { kind: "underExpr", ownerNodeId: node.id, ownerChainId: root.id }
1674
+ };
1675
+ }
1676
+ const nested = findChainById(node.underExpr, chainId);
1677
+ if (nested) {
1678
+ return nested;
1679
+ }
1680
+ }
1681
+ if (node.baseExpr) {
1682
+ if (node.baseExpr.id === chainId) {
1683
+ return {
1684
+ chain: node.baseExpr,
1685
+ relation: { kind: "baseExpr", ownerNodeId: node.id, ownerChainId: root.id }
1686
+ };
1687
+ }
1688
+ const nested = findChainById(node.baseExpr, chainId);
1689
+ if (nested) {
1690
+ return nested;
1691
+ }
1692
+ }
1401
1693
  if (node.matrixRows) {
1402
1694
  for (let rowIndex = 0; rowIndex < node.matrixRows.length; rowIndex += 1) {
1403
1695
  const row = node.matrixRows[rowIndex];
@@ -1471,6 +1763,24 @@ function findNodeById(root, nodeId) {
1471
1763
  return nested;
1472
1764
  }
1473
1765
  }
1766
+ if (node.overExpr) {
1767
+ const nested = findNodeById(node.overExpr, nodeId);
1768
+ if (nested) {
1769
+ return nested;
1770
+ }
1771
+ }
1772
+ if (node.underExpr) {
1773
+ const nested = findNodeById(node.underExpr, nodeId);
1774
+ if (nested) {
1775
+ return nested;
1776
+ }
1777
+ }
1778
+ if (node.baseExpr) {
1779
+ const nested = findNodeById(node.baseExpr, nodeId);
1780
+ if (nested) {
1781
+ return nested;
1782
+ }
1783
+ }
1474
1784
  if (node.matrixRows) {
1475
1785
  for (const row of node.matrixRows) {
1476
1786
  for (const cell of row) {
@@ -1506,6 +1816,15 @@ function nodeChildChains(node) {
1506
1816
  if (node.radicand) {
1507
1817
  chains.push(node.radicand);
1508
1818
  }
1819
+ if (node.overExpr) {
1820
+ chains.push(node.overExpr);
1821
+ }
1822
+ if (node.underExpr) {
1823
+ chains.push(node.underExpr);
1824
+ }
1825
+ if (node.baseExpr) {
1826
+ chains.push(node.baseExpr);
1827
+ }
1509
1828
  if (node.matrixRows) {
1510
1829
  for (const row of node.matrixRows) {
1511
1830
  chains.push(...row);
@@ -1876,6 +2195,91 @@ function derivativeTemplateInitialCaret(englishFrac) {
1876
2195
  };
1877
2196
  }
1878
2197
 
2198
+ // src/editor/limitsTemplates.ts
2199
+ var LIMITS_SERIES_TEMPLATE_IDS = /* @__PURE__ */ new Set([
2200
+ "sum",
2201
+ "prod",
2202
+ "lim",
2203
+ "max",
2204
+ "min",
2205
+ "sup",
2206
+ "inf"
2207
+ ]);
2208
+ function isLimitsSeriesTemplateId(commandId) {
2209
+ return LIMITS_SERIES_TEMPLATE_IDS.has(commandId);
2210
+ }
2211
+ function syncChainIds2(english, arabic) {
2212
+ arabic.id = english.id;
2213
+ const limit = Math.min(english.nodes.length, arabic.nodes.length);
2214
+ for (let index = 0; index < limit; index += 1) {
2215
+ arabic.nodes[index].id = english.nodes[index].id;
2216
+ }
2217
+ }
2218
+ function syncOversetIds(english, arabic) {
2219
+ arabic.id = english.id;
2220
+ if (english.overExpr && arabic.overExpr) {
2221
+ syncChainIds2(english.overExpr, arabic.overExpr);
2222
+ }
2223
+ if (english.baseExpr && arabic.baseExpr) {
2224
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
2225
+ }
2226
+ }
2227
+ function syncUndersetIds(english, arabic) {
2228
+ arabic.id = english.id;
2229
+ if (english.underExpr && arabic.underExpr) {
2230
+ syncChainIds2(english.underExpr, arabic.underExpr);
2231
+ }
2232
+ if (english.baseExpr && arabic.baseExpr) {
2233
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
2234
+ }
2235
+ }
2236
+ function buildLimitsSeriesTemplate(commandId) {
2237
+ if (!isLimitsSeriesTemplateId(commandId)) {
2238
+ throw new Error(`BuTeX: unsupported limits-series template id: ${commandId}`);
2239
+ }
2240
+ const englishOp = createAtomicCommandNode(commandId);
2241
+ const arabicOp = createAtomicCommandNode(commandId);
2242
+ arabicOp.id = englishOp.id;
2243
+ const englishOverset = createOversetNode();
2244
+ const arabicOverset = createOversetNode();
2245
+ syncOversetIds(englishOverset, arabicOverset);
2246
+ englishOverset.baseExpr = createEditorChain([englishOp]);
2247
+ arabicOverset.baseExpr = createEditorChain([arabicOp]);
2248
+ syncChainIds2(englishOverset.baseExpr, arabicOverset.baseExpr);
2249
+ const englishUnderset = createUndersetNode();
2250
+ const arabicUnderset = createUndersetNode();
2251
+ syncUndersetIds(englishUnderset, arabicUnderset);
2252
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
2253
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
2254
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
2255
+ return { english: englishUnderset, arabic: arabicUnderset };
2256
+ }
2257
+ function buildAccentPairTemplate() {
2258
+ const englishOverset = createOversetNode();
2259
+ const arabicOverset = createOversetNode();
2260
+ syncOversetIds(englishOverset, arabicOverset);
2261
+ const englishUnderset = createUndersetNode();
2262
+ const arabicUnderset = createUndersetNode();
2263
+ syncUndersetIds(englishUnderset, arabicUnderset);
2264
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
2265
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
2266
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
2267
+ return { english: englishUnderset, arabic: arabicUnderset };
2268
+ }
2269
+ function accentPairTemplateInitialCaret(englishUnderset) {
2270
+ const overset = englishUnderset.baseExpr?.nodes[0];
2271
+ return {
2272
+ chainId: overset?.baseExpr?.id ?? englishUnderset.underExpr?.id ?? "",
2273
+ index: 0
2274
+ };
2275
+ }
2276
+ function limitsSeriesTemplateInitialCaret(englishUnderset) {
2277
+ return {
2278
+ chainId: englishUnderset.underExpr?.id ?? englishUnderset.baseExpr?.id ?? "",
2279
+ index: 0
2280
+ };
2281
+ }
2282
+
1879
2283
  // src/editor/uiLocale.ts
1880
2284
  var ENGLISH_COMMAND_TITLES = {
1881
2285
  cos: "Cosine",
@@ -1919,6 +2323,24 @@ var ENGLISH_COMMAND_TITLES = {
1919
2323
  quadSpace: "Large space",
1920
2324
  qquadSpace: "Extra-large space"
1921
2325
  };
2326
+ var ENGLISH_ACCENT_TITLES = {
2327
+ vec: "Vector arrow",
2328
+ hat: "Hat",
2329
+ tilde: "Tilde",
2330
+ dot: "Dot",
2331
+ ddot: "Double dot",
2332
+ dddot: "Triple dot",
2333
+ bar: "Bar",
2334
+ check: "Check",
2335
+ breve: "Breve",
2336
+ acute: "Acute",
2337
+ grave: "Grave",
2338
+ overline: "Overline",
2339
+ underline: "Underline",
2340
+ overleftarrow: "Over left arrow",
2341
+ overrightarrow: "Over right arrow",
2342
+ overleftrightarrow: "Over left-right arrow"
2343
+ };
1922
2344
  var ENGLISH_OPERATOR_TITLES = {
1923
2345
  inf: "Infinity",
1924
2346
  times: "Multiplication",
@@ -1992,6 +2414,10 @@ var EQUATION_EDITOR_MESSAGES = {
1992
2414
  insertBraces: "\u0625\u062F\u0631\u0627\u062C \u062D\u0627\u0635\u0631\u062A\u064A\u0646",
1993
2415
  insertFraction: "\u0625\u062F\u0631\u0627\u062C \u0643\u0633\u0631",
1994
2416
  insertRoot: "\u0625\u062F\u0631\u0627\u062C \u062C\u0630\u0631",
2417
+ insertOverset: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642",
2418
+ insertUnderset: "\u0625\u062F\u0631\u0627\u062C \u062A\u062D\u062A",
2419
+ insertAccentPair: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642 \u0648\u062A\u062D\u062A",
2420
+ insertAccents: "\u0625\u062F\u0631\u0627\u062C \u0644\u0643\u0646\u0629",
1995
2421
  environments: "\u0627\u0644\u0628\u064A\u0626\u0627\u062A",
1996
2422
  insertEnvironment: "\u0625\u062F\u0631\u0627\u062C \u0628\u064A\u0626\u0629 \u0634\u0628\u0643\u064A\u0629",
1997
2423
  environmentType: "\u0646\u0648\u0639 \u0627\u0644\u0628\u064A\u0626\u0629",
@@ -2099,6 +2525,10 @@ var EQUATION_EDITOR_MESSAGES = {
2099
2525
  insertBraces: "Insert braces",
2100
2526
  insertFraction: "Insert fraction",
2101
2527
  insertRoot: "Insert root",
2528
+ insertOverset: "Insert overset",
2529
+ insertUnderset: "Insert underset",
2530
+ insertAccentPair: "Insert overset and underset",
2531
+ insertAccents: "Insert accent",
2102
2532
  environments: "Environments",
2103
2533
  insertEnvironment: "Insert grid environment",
2104
2534
  environmentType: "Environment type",
@@ -2199,6 +2629,9 @@ function atomicCommandTitle(spec, locale) {
2199
2629
  function atomicOperatorCommandTitle(spec, locale) {
2200
2630
  return locale === "en" ? ENGLISH_OPERATOR_TITLES[spec.id] ?? spec.title : spec.title;
2201
2631
  }
2632
+ function accentCommandTitle(spec, locale) {
2633
+ return locale === "en" ? ENGLISH_ACCENT_TITLES[spec.id] ?? spec.title : spec.title;
2634
+ }
2202
2635
 
2203
2636
  // src/editor/state.ts
2204
2637
  function ensureSyncEntry(session, nodeId) {
@@ -2233,7 +2666,7 @@ function syncSelectionToCaret(session) {
2233
2666
  session.selectedNodeId = located.relation.ownerNodeId;
2234
2667
  return;
2235
2668
  }
2236
- if (located.relation.kind === "inner" || located.relation.kind === "numerator" || located.relation.kind === "denominator" || located.relation.kind === "radicand" || located.relation.kind === "rootIndex" || located.relation.kind === "matrixCell") {
2669
+ if (located.relation.kind === "inner" || located.relation.kind === "numerator" || located.relation.kind === "denominator" || located.relation.kind === "radicand" || located.relation.kind === "rootIndex" || located.relation.kind === "overExpr" || located.relation.kind === "underExpr" || located.relation.kind === "baseExpr" || located.relation.kind === "matrixCell") {
2237
2670
  session.selectedNodeId = located.relation.ownerNodeId;
2238
2671
  return;
2239
2672
  }
@@ -2590,6 +3023,106 @@ function moveCaretVertical(session, direction) {
2590
3023
  appendDebug(next, "moveCaretVertical", "sqrt-radicand-down-exit");
2591
3024
  return next;
2592
3025
  }
3026
+ if (located.relation.kind === "overExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3027
+ if (direction === "down") {
3028
+ next.caret = {
3029
+ chainId: ownerNode.baseExpr.id,
3030
+ index: ownerNode.baseExpr.nodes.length
3031
+ };
3032
+ next.selectedStructureNodeId = null;
3033
+ syncSelectionToCaret(next);
3034
+ appendDebug(next, "moveCaretVertical", "overset-over->base");
3035
+ return next;
3036
+ }
3037
+ next.caret = {
3038
+ chainId: ownerLocated.chain.id,
3039
+ index: ownerLocated.index
3040
+ };
3041
+ next.selectedStructureNodeId = null;
3042
+ syncSelectionToCaret(next);
3043
+ appendDebug(next, "moveCaretVertical", "overset-over-up-exit");
3044
+ return next;
3045
+ }
3046
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "overset" && ownerNode.overExpr) {
3047
+ if (direction === "up") {
3048
+ next.caret = {
3049
+ chainId: ownerNode.overExpr.id,
3050
+ index: ownerNode.overExpr.nodes.length
3051
+ };
3052
+ next.selectedStructureNodeId = null;
3053
+ syncSelectionToCaret(next);
3054
+ appendDebug(next, "moveCaretVertical", "overset-base->over");
3055
+ return next;
3056
+ }
3057
+ next.caret = {
3058
+ chainId: ownerLocated.chain.id,
3059
+ index: ownerLocated.index + 1
3060
+ };
3061
+ next.selectedStructureNodeId = null;
3062
+ syncSelectionToCaret(next);
3063
+ appendDebug(next, "moveCaretVertical", "overset-base-down-exit");
3064
+ return next;
3065
+ }
3066
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "underset" && ownerNode.underExpr) {
3067
+ if (direction === "down") {
3068
+ next.caret = {
3069
+ chainId: ownerNode.underExpr.id,
3070
+ index: ownerNode.underExpr.nodes.length
3071
+ };
3072
+ next.selectedStructureNodeId = null;
3073
+ syncSelectionToCaret(next);
3074
+ appendDebug(next, "moveCaretVertical", "underset-base->under");
3075
+ return next;
3076
+ }
3077
+ next.caret = {
3078
+ chainId: ownerLocated.chain.id,
3079
+ index: ownerLocated.index
3080
+ };
3081
+ next.selectedStructureNodeId = null;
3082
+ syncSelectionToCaret(next);
3083
+ appendDebug(next, "moveCaretVertical", "underset-base-up-exit");
3084
+ return next;
3085
+ }
3086
+ if (located.relation.kind === "underExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3087
+ if (direction === "up") {
3088
+ next.caret = {
3089
+ chainId: ownerNode.baseExpr.id,
3090
+ index: ownerNode.baseExpr.nodes.length
3091
+ };
3092
+ next.selectedStructureNodeId = null;
3093
+ syncSelectionToCaret(next);
3094
+ appendDebug(next, "moveCaretVertical", "underset-under->base");
3095
+ return next;
3096
+ }
3097
+ next.caret = {
3098
+ chainId: ownerLocated.chain.id,
3099
+ index: ownerLocated.index + 1
3100
+ };
3101
+ next.selectedStructureNodeId = null;
3102
+ syncSelectionToCaret(next);
3103
+ appendDebug(next, "moveCaretVertical", "underset-under-down-exit");
3104
+ return next;
3105
+ }
3106
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "accent") {
3107
+ if (direction === "up") {
3108
+ next.caret = {
3109
+ chainId: ownerLocated.chain.id,
3110
+ index: ownerLocated.index
3111
+ };
3112
+ next.selectedStructureNodeId = null;
3113
+ syncSelectionToCaret(next);
3114
+ appendDebug(next, "moveCaretVertical", "accent-base-up-exit");
3115
+ return next;
3116
+ }
3117
+ next.caret = {
3118
+ chainId: ownerLocated.chain.id,
3119
+ index: ownerLocated.index + 1
3120
+ };
3121
+ next.selectedStructureNodeId = null;
3122
+ syncSelectionToCaret(next);
3123
+ appendDebug(next, "moveCaretVertical", "accent-base-down-exit");
3124
+ return next;
3125
+ }
2593
3126
  if (located.relation.kind === "matrixCell" && ownerLocated && ownerNode?.matrixRows) {
2594
3127
  const rowIndex = located.relation.rowIndex ?? 0;
2595
3128
  const columnIndex = located.relation.columnIndex ?? 0;
@@ -2967,6 +3500,119 @@ function insertSqrt(session) {
2967
3500
  appendDebug(next, "insertSqrt", "");
2968
3501
  return next;
2969
3502
  }
3503
+ function insertOverset(session) {
3504
+ const base = replaceSelectionFirst(session);
3505
+ const next = cloneEditorSession(base);
3506
+ const englishNode = createOversetNode();
3507
+ const arabicNode = createOversetNode();
3508
+ arabicNode.id = englishNode.id;
3509
+ if (arabicNode.overExpr && englishNode.overExpr) {
3510
+ arabicNode.overExpr.id = englishNode.overExpr.id;
3511
+ }
3512
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
3513
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
3514
+ }
3515
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3516
+ next.caret = {
3517
+ chainId: englishNode.overExpr ? englishNode.overExpr.id : next.caret.chainId,
3518
+ index: 0
3519
+ };
3520
+ next.selectedStructureNodeId = null;
3521
+ syncSelectionToCaret(next);
3522
+ appendDebug(next, "insertOverset", "");
3523
+ return next;
3524
+ }
3525
+ function insertUnderset(session) {
3526
+ const base = replaceSelectionFirst(session);
3527
+ const next = cloneEditorSession(base);
3528
+ const englishNode = createUndersetNode();
3529
+ const arabicNode = createUndersetNode();
3530
+ arabicNode.id = englishNode.id;
3531
+ if (arabicNode.underExpr && englishNode.underExpr) {
3532
+ arabicNode.underExpr.id = englishNode.underExpr.id;
3533
+ }
3534
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
3535
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
3536
+ }
3537
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3538
+ next.caret = {
3539
+ chainId: englishNode.underExpr ? englishNode.underExpr.id : next.caret.chainId,
3540
+ index: 0
3541
+ };
3542
+ next.selectedStructureNodeId = null;
3543
+ syncSelectionToCaret(next);
3544
+ appendDebug(next, "insertUnderset", "");
3545
+ return next;
3546
+ }
3547
+ function insertLimitsSeriesTemplate(session, commandId) {
3548
+ const base = replaceSelectionFirst(session);
3549
+ const next = cloneEditorSession(base);
3550
+ const { english, arabic } = buildLimitsSeriesTemplate(commandId);
3551
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
3552
+ syncSubtreeRecursive(next, english, arabic);
3553
+ next.caret = limitsSeriesTemplateInitialCaret(english);
3554
+ next.selectedStructureNodeId = null;
3555
+ syncSelectionToCaret(next);
3556
+ appendDebug(next, "insertLimitsSeriesTemplate", commandId);
3557
+ return next;
3558
+ }
3559
+ function insertAccentPair(session) {
3560
+ const base = replaceSelectionFirst(session);
3561
+ const next = cloneEditorSession(base);
3562
+ const { english, arabic } = buildAccentPairTemplate();
3563
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
3564
+ syncSubtreeRecursive(next, english, arabic);
3565
+ next.caret = accentPairTemplateInitialCaret(english);
3566
+ next.selectedStructureNodeId = null;
3567
+ syncSelectionToCaret(next);
3568
+ appendDebug(next, "insertAccentPair", "");
3569
+ return next;
3570
+ }
3571
+ function insertAccent(session, accentId) {
3572
+ const next = cloneEditorSession(session);
3573
+ const englishNode = createAccentNode(accentId);
3574
+ const arabicNode = createAccentNode(accentId);
3575
+ arabicNode.id = englishNode.id;
3576
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
3577
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
3578
+ }
3579
+ if (isSelectionActive(next.selection) && next.selection) {
3580
+ const sel = next.selection;
3581
+ const { start, end } = normalizeSelection(sel);
3582
+ const englishLocated = findChainById(next.englishTree, sel.chainId);
3583
+ const arabicLocated = findChainById(next.arabicTree, sel.chainId);
3584
+ if (englishLocated && arabicLocated && end > start) {
3585
+ const count = end - start;
3586
+ const englishWrapped = englishLocated.chain.nodes.splice(start, count);
3587
+ const arabicWrapped = arabicLocated.chain.nodes.splice(start, count);
3588
+ englishNode.baseExpr.nodes = englishWrapped;
3589
+ arabicNode.baseExpr.nodes = arabicWrapped;
3590
+ englishLocated.chain.nodes.splice(start, 0, englishNode);
3591
+ arabicLocated.chain.nodes.splice(start, 0, arabicNode);
3592
+ syncSubtreeRecursive(next, englishNode, arabicNode);
3593
+ next.caret = {
3594
+ chainId: englishNode.baseExpr.id,
3595
+ index: englishWrapped.length
3596
+ };
3597
+ next.selection = null;
3598
+ next.selectedStructureNodeId = null;
3599
+ syncSelectionToCaret(next);
3600
+ appendDebug(next, "insertAccent", `${accentId}:wrap`);
3601
+ return next;
3602
+ }
3603
+ }
3604
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3605
+ syncSubtreeRecursive(next, englishNode, arabicNode);
3606
+ next.caret = {
3607
+ chainId: englishNode.baseExpr ? englishNode.baseExpr.id : next.caret.chainId,
3608
+ index: 0
3609
+ };
3610
+ next.selection = null;
3611
+ next.selectedStructureNodeId = null;
3612
+ syncSelectionToCaret(next);
3613
+ appendDebug(next, "insertAccent", `${accentId}:empty`);
3614
+ return next;
3615
+ }
2970
3616
  function insertMatrixEnv(session, style, rows, columns) {
2971
3617
  return insertGridEnv(session, style, rows, columns);
2972
3618
  }
@@ -3214,7 +3860,7 @@ function deleteBackward(session) {
3214
3860
  if (!previousNode) {
3215
3861
  return session;
3216
3862
  }
3217
- if ((previousNode.kind === "delimiter" || previousNode.kind === "frac" || previousNode.kind === "sqrt" || previousNode.kind === "env") && next.selectedStructureNodeId !== previousNode.id) {
3863
+ if ((previousNode.kind === "delimiter" || previousNode.kind === "frac" || previousNode.kind === "sqrt" || previousNode.kind === "overset" || previousNode.kind === "underset" || previousNode.kind === "accent" || previousNode.kind === "env") && next.selectedStructureNodeId !== previousNode.id) {
3218
3864
  next.selectedStructureNodeId = previousNode.id;
3219
3865
  next.selectedNodeId = previousNode.id;
3220
3866
  appendDebug(next, "deleteBackward", "selected structure");
@@ -3704,6 +4350,168 @@ function buildSqrtNodeBody(node, buildChain, side) {
3704
4350
  return wrap;
3705
4351
  }
3706
4352
 
4353
+ // src/editor/display/overset.ts
4354
+ var OVERSET_CSS = `
4355
+ .overset {
4356
+ display: inline-flex;
4357
+ flex-direction: column;
4358
+ align-items: center;
4359
+ justify-content: center;
4360
+ vertical-align: middle;
4361
+ margin: 0 2px;
4362
+ min-width: 1.2em;
4363
+ }
4364
+
4365
+ .overset-over {
4366
+ display: inline-flex;
4367
+ justify-content: center;
4368
+ align-items: center;
4369
+ min-height: 0.85em;
4370
+ padding: 0 2px;
4371
+ font-size: 0.72em;
4372
+ line-height: 1.1;
4373
+ }
4374
+
4375
+ .overset-base {
4376
+ display: inline-flex;
4377
+ justify-content: center;
4378
+ align-items: center;
4379
+ min-height: 1.05em;
4380
+ padding: 0 2px;
4381
+ }
4382
+ `.trim();
4383
+ function buildOversetNodeBody(node, buildChain) {
4384
+ const wrap = document.createElement("span");
4385
+ wrap.className = "overset";
4386
+ const over = document.createElement("span");
4387
+ over.className = "overset-over";
4388
+ if (node.overExpr) {
4389
+ over.appendChild(buildChain(node.overExpr));
4390
+ }
4391
+ const base = document.createElement("span");
4392
+ base.className = "overset-base";
4393
+ if (node.baseExpr) {
4394
+ base.appendChild(buildChain(node.baseExpr));
4395
+ }
4396
+ wrap.appendChild(over);
4397
+ wrap.appendChild(base);
4398
+ return wrap;
4399
+ }
4400
+
4401
+ // src/editor/display/underset.ts
4402
+ var UNDERSET_CSS = `
4403
+ .underset {
4404
+ display: inline-flex;
4405
+ flex-direction: column;
4406
+ align-items: center;
4407
+ justify-content: center;
4408
+ vertical-align: middle;
4409
+ margin: 0 2px;
4410
+ min-width: 1.2em;
4411
+ }
4412
+
4413
+ .underset-base {
4414
+ display: inline-flex;
4415
+ justify-content: center;
4416
+ align-items: center;
4417
+ min-height: 1.05em;
4418
+ padding: 0 2px;
4419
+ }
4420
+
4421
+ .underset-under {
4422
+ display: inline-flex;
4423
+ justify-content: center;
4424
+ align-items: center;
4425
+ min-height: 0.85em;
4426
+ padding: 0 2px;
4427
+ font-size: 0.72em;
4428
+ line-height: 1.1;
4429
+ }
4430
+ `.trim();
4431
+ function buildUndersetNodeBody(node, buildChain) {
4432
+ const wrap = document.createElement("span");
4433
+ wrap.className = "underset";
4434
+ const base = document.createElement("span");
4435
+ base.className = "underset-base";
4436
+ if (node.baseExpr) {
4437
+ base.appendChild(buildChain(node.baseExpr));
4438
+ }
4439
+ const under = document.createElement("span");
4440
+ under.className = "underset-under";
4441
+ if (node.underExpr) {
4442
+ under.appendChild(buildChain(node.underExpr));
4443
+ }
4444
+ wrap.appendChild(base);
4445
+ wrap.appendChild(under);
4446
+ return wrap;
4447
+ }
4448
+
4449
+ // src/editor/display/accent.ts
4450
+ var ACCENT_CSS = `
4451
+ .math-accent {
4452
+ display: inline-flex;
4453
+ flex-direction: column;
4454
+ align-items: center;
4455
+ justify-content: center;
4456
+ vertical-align: middle;
4457
+ margin: 0 2px;
4458
+ min-width: 1.1em;
4459
+ }
4460
+
4461
+ .math-accent-chrome {
4462
+ display: inline-flex;
4463
+ justify-content: center;
4464
+ align-items: center;
4465
+ min-height: 0.7em;
4466
+ padding: 0 2px;
4467
+ font-size: 0.85em;
4468
+ line-height: 1;
4469
+ color: var(--butex-script-color, #475569);
4470
+ user-select: none;
4471
+ }
4472
+
4473
+ .math-accent-base {
4474
+ display: inline-flex;
4475
+ justify-content: center;
4476
+ align-items: center;
4477
+ min-height: 1.05em;
4478
+ padding: 0 2px;
4479
+ }
4480
+
4481
+ .math-accent--under .math-accent-chrome {
4482
+ min-height: 0.45em;
4483
+ border-top: 1.5px solid currentColor;
4484
+ width: 100%;
4485
+ font-size: 0;
4486
+ color: var(--butex-surface-fg, #0f172a);
4487
+ }
4488
+ `.trim();
4489
+ function buildAccentNodeBody(node, buildChain) {
4490
+ const wrap = document.createElement("span");
4491
+ const spec = accentCommandSpec(node.expr);
4492
+ const placement = spec?.placement ?? "over";
4493
+ wrap.className = placement === "under" ? "math-accent math-accent--under" : "math-accent math-accent--over";
4494
+ const chrome = document.createElement("span");
4495
+ chrome.className = "math-accent-chrome";
4496
+ chrome.setAttribute("aria-hidden", "true");
4497
+ if (placement === "over") {
4498
+ chrome.textContent = spec?.label ?? "\u02C6";
4499
+ }
4500
+ const base = document.createElement("span");
4501
+ base.className = "math-accent-base";
4502
+ if (node.baseExpr) {
4503
+ base.appendChild(buildChain(node.baseExpr));
4504
+ }
4505
+ if (placement === "under") {
4506
+ wrap.appendChild(base);
4507
+ wrap.appendChild(chrome);
4508
+ } else {
4509
+ wrap.appendChild(chrome);
4510
+ wrap.appendChild(base);
4511
+ }
4512
+ return wrap;
4513
+ }
4514
+
3707
4515
  // src/editor/display/env.ts
3708
4516
  var WRAPPERS = {
3709
4517
  matrix: { left: "", right: "" },
@@ -4076,6 +4884,15 @@ function buildNodeBody(node, buildChain, side, digitForm = "western", uiLocale =
4076
4884
  if (node.kind === "sqrt") {
4077
4885
  return buildSqrtNodeBody(node, buildChain, side);
4078
4886
  }
4887
+ if (node.kind === "overset") {
4888
+ return buildOversetNodeBody(node, buildChain);
4889
+ }
4890
+ if (node.kind === "underset") {
4891
+ return buildUndersetNodeBody(node, buildChain);
4892
+ }
4893
+ if (node.kind === "accent") {
4894
+ return buildAccentNodeBody(node, buildChain);
4895
+ }
4079
4896
  if (node.kind === "env") {
4080
4897
  return buildMatrixEnvNodeBody(node, buildChain, side);
4081
4898
  }
@@ -4275,6 +5092,12 @@ ${FRAC_CSS}
4275
5092
 
4276
5093
  ${SQRT_CSS}
4277
5094
 
5095
+ ${OVERSET_CSS}
5096
+
5097
+ ${UNDERSET_CSS}
5098
+
5099
+ ${ACCENT_CSS}
5100
+
4278
5101
  ${MATRIX_ENV_CSS}
4279
5102
 
4280
5103
  ${ATOMIC_COMMAND_CSS}
@@ -4994,6 +5817,26 @@ function createEditorRuntime(options) {
4994
5817
  applyStructural((prev) => insertSqrt(prev));
4995
5818
  surfaceEl.focus();
4996
5819
  },
5820
+ insertOverset: () => {
5821
+ applyStructural((prev) => insertOverset(prev));
5822
+ surfaceEl.focus();
5823
+ },
5824
+ insertUnderset: () => {
5825
+ applyStructural((prev) => insertUnderset(prev));
5826
+ surfaceEl.focus();
5827
+ },
5828
+ insertLimitsSeriesTemplate: (commandId) => {
5829
+ applyStructural((prev) => insertLimitsSeriesTemplate(prev, commandId));
5830
+ surfaceEl.focus();
5831
+ },
5832
+ insertAccentPair: () => {
5833
+ applyStructural((prev) => insertAccentPair(prev));
5834
+ surfaceEl.focus();
5835
+ },
5836
+ insertAccent: (accentId) => {
5837
+ applyStructural((prev) => insertAccent(prev, accentId));
5838
+ surfaceEl.focus();
5839
+ },
4997
5840
  insertAtomicCommand: (commandId) => {
4998
5841
  applyStructural((prev) => insertAtomicCommand(prev, commandId));
4999
5842
  surfaceEl.focus();
@@ -5048,7 +5891,10 @@ function createEditorRuntime(options) {
5048
5891
  // src/mathjax/svgPatcher.ts
5049
5892
  var SVG_ARABSQRT_SELECTOR = 'mjx-container[jax="SVG"] svg .mjx-rtl-mirror[data-mjx-rtl-root="true"]';
5050
5893
  var ARABSQRT_SELECTOR = '.mjx-rtl-mirror[data-mjx-rtl-root="true"]';
5894
+ var SVG_PREVIEW_MIRROR_SELECTOR = 'mjx-container[jax="SVG"] svg .butex-preview-mirror';
5895
+ var PREVIEW_MIRROR_SELECTOR = ".butex-preview-mirror";
5051
5896
  var PATCHED_ATTR = "data-butex-svg-arabsqrt";
5897
+ var MIRROR_PATCHED_ATTR = "data-butex-svg-mirror";
5052
5898
  var ROOT_INDEX_SCALE = 0.72;
5053
5899
  var ROOT_INDEX_GAP_FACTOR = 0.08;
5054
5900
  var ROOT_INDEX_MIN_GAP = 2;
@@ -5073,11 +5919,12 @@ function directSvgElementChildren(element) {
5073
5919
  return Array.from(element.children).filter((child) => child instanceof SVGElement);
5074
5920
  }
5075
5921
  function boxFor(element) {
5076
- if (!(element instanceof SVGGraphicsElement) || typeof element.getBBox !== "function") {
5922
+ const candidate = element;
5923
+ if (typeof candidate.getBBox !== "function") {
5077
5924
  return null;
5078
5925
  }
5079
5926
  try {
5080
- const box = element.getBBox();
5927
+ const box = candidate.getBBox();
5081
5928
  return {
5082
5929
  x: box.x,
5083
5930
  y: box.y,
@@ -5321,6 +6168,31 @@ function patchArabSqrtRoot(root) {
5321
6168
  root.setAttribute(PATCHED_ATTR, "patched");
5322
6169
  rootNode.setAttribute(PATCHED_ATTR, "patched");
5323
6170
  }
6171
+ function patchPreviewMirror(element) {
6172
+ if (element.getAttribute(MIRROR_PATCHED_ATTR) === "patched") {
6173
+ return;
6174
+ }
6175
+ const box = boxFor(element);
6176
+ if (!box || box.width <= 0) {
6177
+ return;
6178
+ }
6179
+ const [tx, ty] = readTranslate(element);
6180
+ const mirrorX = tx + 2 * box.x + box.width;
6181
+ element.setAttribute(
6182
+ "transform",
6183
+ `translate(${roundSvg(mirrorX)},${roundSvg(ty)}) scale(-1,1)`
6184
+ );
6185
+ element.setAttribute(MIRROR_PATCHED_ATTR, "patched");
6186
+ }
6187
+ function collectPreviewMirrors(root) {
6188
+ let mirrors = Array.from(root.querySelectorAll(SVG_PREVIEW_MIRROR_SELECTOR));
6189
+ if (!mirrors.length) {
6190
+ mirrors = Array.from(root.querySelectorAll(PREVIEW_MIRROR_SELECTOR)).filter((candidate) => {
6191
+ return candidate.closest('mjx-container[jax="SVG"]') != null;
6192
+ });
6193
+ }
6194
+ return mirrors;
6195
+ }
5324
6196
  function patchBuTeXSvgMath(root) {
5325
6197
  if (typeof root.querySelectorAll !== "function") {
5326
6198
  return;
@@ -5332,6 +6204,7 @@ function patchBuTeXSvgMath(root) {
5332
6204
  });
5333
6205
  }
5334
6206
  roots.sort((a, b) => rootDepth(b) - rootDepth(a)).forEach((arabSqrtRoot) => patchArabSqrtRoot(arabSqrtRoot));
6207
+ collectPreviewMirrors(root).forEach((mirror) => patchPreviewMirror(mirror));
5335
6208
  }
5336
6209
 
5337
6210
  // src/mathjax/renderIsland.ts
@@ -5605,6 +6478,29 @@ var WIDGET_CHROME_CSS = `
5605
6478
  padding-top: 1px;
5606
6479
  }
5607
6480
 
6481
+ .butex-widget .toolbar-accent-icon {
6482
+ display: inline-flex;
6483
+ flex-direction: column;
6484
+ align-items: center;
6485
+ justify-content: center;
6486
+ min-width: 14px;
6487
+ font-size: 10px;
6488
+ line-height: 1;
6489
+ gap: 1px;
6490
+ }
6491
+
6492
+ .butex-widget .toolbar-accent-icon--over span:first-child,
6493
+ .butex-widget .toolbar-accent-icon--under span:last-child {
6494
+ font-size: 8px;
6495
+ opacity: 0.85;
6496
+ }
6497
+
6498
+ .butex-widget .toolbar-accent-icon--pair span:first-child,
6499
+ .butex-widget .toolbar-accent-icon--pair span:last-child {
6500
+ font-size: 8px;
6501
+ opacity: 0.85;
6502
+ }
6503
+
5608
6504
  .butex-widget .matrix-command-menu {
5609
6505
  position: relative;
5610
6506
  display: inline-flex;
@@ -6346,6 +7242,7 @@ var CHARACTER_FONT_OPTIONS = [
6346
7242
  { id: "diwani", label: "\u062F\u064A\u0648" },
6347
7243
  { id: "diwaniOutline", label: "\u0638\u0644" }
6348
7244
  ];
7245
+ var ACCENT_COMMANDS_LIST = accentCommandsList();
6349
7246
  var FUNCTION_COMMANDS = atomicCommandsByCategory("function");
6350
7247
  var LIMITS_SERIES_COMMANDS = atomicCommandsByCategory("limits_series");
6351
7248
  var FUNCTION2_COMMANDS = atomicCommandsByCategory("function2");
@@ -6493,6 +7390,7 @@ var ButexEditor = forwardRef(
6493
7390
  const [operator2MenuOpen, setOperator2MenuOpen] = useState(false);
6494
7391
  const [operator3MenuOpen, setOperator3MenuOpen] = useState(false);
6495
7392
  const [dotMenuOpen, setDotMenuOpen] = useState(false);
7393
+ const [accentMenuOpen, setAccentMenuOpen] = useState(false);
6496
7394
  const [integralMenuOpen, setIntegralMenuOpen] = useState(false);
6497
7395
  const [selectedDigitForm2, setSelectedDigitForm] = useState("western");
6498
7396
  const [selectedCharacterFont, setSelectedCharacterFont] = useState("default");
@@ -6762,7 +7660,78 @@ ${arabic || currentMessages.empty}`;
6762
7660
  /* @__PURE__ */ jsx("button", { id: "insert-sqrt", className: "icon-btn", type: "button", title: messages.insertRoot, "aria-label": messages.insertRoot, onClick: () => {
6763
7661
  runtimeRef.current?.insertSqrt();
6764
7662
  focusSurface();
6765
- }, children: /* @__PURE__ */ jsx("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) })
7663
+ }, children: /* @__PURE__ */ jsx("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) }),
7664
+ /* @__PURE__ */ jsx("button", { id: "insert-overset", className: "icon-btn", type: "button", title: messages.insertOverset, "aria-label": messages.insertOverset, onClick: () => {
7665
+ runtimeRef.current?.insertOverset();
7666
+ focusSurface();
7667
+ }, children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
7668
+ /* @__PURE__ */ jsx("span", { children: "\u2217" }),
7669
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" })
7670
+ ] }) }),
7671
+ /* @__PURE__ */ jsx("button", { id: "insert-underset", className: "icon-btn", type: "button", title: messages.insertUnderset, "aria-label": messages.insertUnderset, onClick: () => {
7672
+ runtimeRef.current?.insertUnderset();
7673
+ focusSurface();
7674
+ }, children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--under", "aria-hidden": "true", children: [
7675
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" }),
7676
+ /* @__PURE__ */ jsx("span", { children: "\u2217" })
7677
+ ] }) }),
7678
+ /* @__PURE__ */ jsx("button", { id: "insert-accent-pair", className: "icon-btn", type: "button", title: messages.insertAccentPair, "aria-label": messages.insertAccentPair, onClick: () => {
7679
+ runtimeRef.current?.insertAccentPair();
7680
+ focusSurface();
7681
+ }, children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--pair", "aria-hidden": "true", children: [
7682
+ /* @__PURE__ */ jsx("span", { children: "\u2217" }),
7683
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" }),
7684
+ /* @__PURE__ */ jsx("span", { children: "\u2217" })
7685
+ ] }) }),
7686
+ /* @__PURE__ */ jsxs(
7687
+ "div",
7688
+ {
7689
+ className: "function-command-menu operator-command-menu",
7690
+ onBlur: (event) => {
7691
+ const nextFocus = event.relatedTarget;
7692
+ if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) {
7693
+ setAccentMenuOpen(false);
7694
+ }
7695
+ },
7696
+ children: [
7697
+ /* @__PURE__ */ jsx(
7698
+ "button",
7699
+ {
7700
+ id: "insert-accents",
7701
+ className: "icon-btn function-command-btn operator-command-btn",
7702
+ type: "button",
7703
+ title: messages.insertAccents,
7704
+ "aria-label": messages.insertAccents,
7705
+ "aria-haspopup": "listbox",
7706
+ "aria-expanded": accentMenuOpen,
7707
+ onClick: () => setAccentMenuOpen((open) => !open),
7708
+ children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
7709
+ /* @__PURE__ */ jsx("span", { children: "\u02C6" }),
7710
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" })
7711
+ ] })
7712
+ }
7713
+ ),
7714
+ /* @__PURE__ */ jsx("div", { className: "function-command-options operator-command-options", role: "listbox", "aria-label": messages.insertAccents, hidden: !accentMenuOpen, children: ACCENT_COMMANDS_LIST.map((command) => /* @__PURE__ */ jsx(
7715
+ "button",
7716
+ {
7717
+ className: "function-command-option operator-command-option",
7718
+ type: "button",
7719
+ role: "option",
7720
+ title: accentCommandTitle(command, uiLocale),
7721
+ "aria-label": accentCommandTitle(command, uiLocale),
7722
+ onMouseDown: (event) => event.preventDefault(),
7723
+ onClick: () => {
7724
+ runtimeRef.current?.insertAccent(command.id);
7725
+ setAccentMenuOpen(false);
7726
+ focusSurface();
7727
+ },
7728
+ children: /* @__PURE__ */ jsx("span", { className: "operator-command-label", children: command.label })
7729
+ },
7730
+ command.id
7731
+ )) })
7732
+ ]
7733
+ }
7734
+ )
6766
7735
  ] }),
6767
7736
  /* @__PURE__ */ jsx("div", { className: "toolbar-group toolbar-environments", role: "group", "aria-label": messages.environments, children: /* @__PURE__ */ jsxs(
6768
7737
  "div",
@@ -7090,7 +8059,7 @@ ${arabic || currentMessages.empty}`;
7090
8059
  "aria-label": atomicCommandTitle(command, uiLocale),
7091
8060
  onMouseDown: (event) => event.preventDefault(),
7092
8061
  onClick: () => {
7093
- runtimeRef.current?.insertAtomicCommand(command.id);
8062
+ runtimeRef.current?.insertLimitsSeriesTemplate(command.id);
7094
8063
  setLimitsSeriesMenuOpen(false);
7095
8064
  focusSurface();
7096
8065
  },