@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.js CHANGED
@@ -55,6 +55,9 @@ function createEditorNode(kind, expr) {
55
55
  denominator: null,
56
56
  radicand: null,
57
57
  rootIndex: null,
58
+ overExpr: null,
59
+ underExpr: null,
60
+ baseExpr: null,
58
61
  envName: null,
59
62
  matrixStyle: null,
60
63
  matrixRows: null,
@@ -83,6 +86,26 @@ function createSqrtNode() {
83
86
  rootIndex: createEditorChain()
84
87
  };
85
88
  }
89
+ function createOversetNode() {
90
+ return {
91
+ ...createEditorNode("overset", ""),
92
+ overExpr: createEditorChain(),
93
+ baseExpr: createEditorChain()
94
+ };
95
+ }
96
+ function createUndersetNode() {
97
+ return {
98
+ ...createEditorNode("underset", ""),
99
+ underExpr: createEditorChain(),
100
+ baseExpr: createEditorChain()
101
+ };
102
+ }
103
+ function createAccentNode(accentId) {
104
+ return {
105
+ ...createEditorNode("accent", accentId),
106
+ baseExpr: createEditorChain()
107
+ };
108
+ }
86
109
  function createGridEnvNode(envName, rows, columns, columnAlignments) {
87
110
  const safeRows = Math.max(1, Math.min(12, Math.trunc(rows)));
88
111
  const safeColumns = Math.max(1, Math.min(12, Math.trunc(columns)));
@@ -124,6 +147,144 @@ function cloneEditorSession(session) {
124
147
  };
125
148
  }
126
149
 
150
+ // src/editor/accentCommands.ts
151
+ var ACCENT_COMMANDS = {
152
+ vec: {
153
+ id: "vec",
154
+ englishTex: "\\vec",
155
+ arabicTex: "\\arabvec",
156
+ placement: "over",
157
+ label: "\u20D7",
158
+ title: "\u0633\u0647\u0645 \u0641\u0648\u0642"
159
+ },
160
+ hat: {
161
+ id: "hat",
162
+ englishTex: "\\hat",
163
+ arabicTex: "\\hat",
164
+ placement: "over",
165
+ label: "\u02C6",
166
+ title: "\u0642\u0628\u0639\u0629"
167
+ },
168
+ tilde: {
169
+ id: "tilde",
170
+ englishTex: "\\tilde",
171
+ arabicTex: "\\tilde",
172
+ placement: "over",
173
+ label: "\u02DC",
174
+ title: "\u0645\u062F\u0629"
175
+ },
176
+ dot: {
177
+ id: "dot",
178
+ englishTex: "\\dot",
179
+ arabicTex: "\\dot",
180
+ placement: "over",
181
+ label: "\u02D9",
182
+ title: "\u0646\u0642\u0637\u0629 \u0641\u0648\u0642"
183
+ },
184
+ ddot: {
185
+ id: "ddot",
186
+ englishTex: "\\ddot",
187
+ arabicTex: "\\ddot",
188
+ placement: "over",
189
+ label: "\xA8",
190
+ title: "\u0646\u0642\u0637\u062A\u0627\u0646 \u0641\u0648\u0642"
191
+ },
192
+ dddot: {
193
+ id: "dddot",
194
+ englishTex: "\\dddot",
195
+ arabicTex: "\\dddot",
196
+ placement: "over",
197
+ label: "\u20DB",
198
+ title: "\u062B\u0644\u0627\u062B \u0646\u0642\u0627\u0637 \u0641\u0648\u0642"
199
+ },
200
+ bar: {
201
+ id: "bar",
202
+ englishTex: "\\bar",
203
+ arabicTex: "\\bar",
204
+ placement: "over",
205
+ label: "\xAF",
206
+ title: "\u062E\u0637 \u0641\u0648\u0642"
207
+ },
208
+ check: {
209
+ id: "check",
210
+ englishTex: "\\check",
211
+ arabicTex: "\\check",
212
+ placement: "over",
213
+ label: "\u02C7",
214
+ title: "\u0639\u0644\u0627\u0645\u0629 \u062A\u062D\u0642\u0642"
215
+ },
216
+ breve: {
217
+ id: "breve",
218
+ englishTex: "\\breve",
219
+ arabicTex: "\\breve",
220
+ placement: "over",
221
+ label: "\u02D8",
222
+ title: "\u0642\u0648\u0633 \u0642\u0635\u064A\u0631"
223
+ },
224
+ acute: {
225
+ id: "acute",
226
+ englishTex: "\\acute",
227
+ arabicTex: "\\grave",
228
+ placement: "over",
229
+ label: "\xB4",
230
+ title: "\u0646\u0628\u0631\u0629 \u062D\u0627\u062F\u0629"
231
+ },
232
+ grave: {
233
+ id: "grave",
234
+ englishTex: "\\grave",
235
+ arabicTex: "\\acute",
236
+ placement: "over",
237
+ label: "`",
238
+ title: "\u0646\u0628\u0631\u0629 \u062B\u0642\u064A\u0644\u0629"
239
+ },
240
+ overline: {
241
+ id: "overline",
242
+ englishTex: "\\overline",
243
+ arabicTex: "\\overline",
244
+ placement: "over",
245
+ label: "\u203E",
246
+ title: "\u062E\u0637 \u0639\u0644\u0648\u064A \u0645\u0645\u062A\u062F"
247
+ },
248
+ underline: {
249
+ id: "underline",
250
+ englishTex: "\\underline",
251
+ arabicTex: "\\underline",
252
+ placement: "under",
253
+ label: "_",
254
+ title: "\u062E\u0637 \u0633\u0641\u0644\u064A"
255
+ },
256
+ overleftarrow: {
257
+ id: "overleftarrow",
258
+ englishTex: "\\overleftarrow",
259
+ arabicTex: "\\overrightarrow",
260
+ placement: "over",
261
+ label: "\u2190",
262
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631"
263
+ },
264
+ overrightarrow: {
265
+ id: "overrightarrow",
266
+ englishTex: "\\overrightarrow",
267
+ arabicTex: "\\overleftarrow",
268
+ placement: "over",
269
+ label: "\u2192",
270
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646"
271
+ },
272
+ overleftrightarrow: {
273
+ id: "overleftrightarrow",
274
+ englishTex: "\\overleftrightarrow",
275
+ arabicTex: "\\overleftrightarrow",
276
+ placement: "over",
277
+ label: "\u2194",
278
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0645\u0632\u062F\u0648\u062C \u0627\u0644\u0627\u062A\u062C\u0627\u0647"
279
+ }
280
+ };
281
+ function accentCommandSpec(id) {
282
+ return Object.prototype.hasOwnProperty.call(ACCENT_COMMANDS, id) ? ACCENT_COMMANDS[id] : null;
283
+ }
284
+ function accentCommandsList() {
285
+ return Object.values(ACCENT_COMMANDS);
286
+ }
287
+
127
288
  // src/editor/atomicCommands.ts
128
289
  var ATOMIC_COMMANDS = {
129
290
  ///////////////////////// Functions ////////////////////////////////
@@ -544,7 +705,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
544
705
  id: "neq",
545
706
  category: "operators1",
546
707
  Tex: "\\neq",
547
- mirror: false,
708
+ mirror: true,
548
709
  Label: "\u2260",
549
710
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
550
711
  svg_path: null
@@ -554,6 +715,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
554
715
  category: "operators1",
555
716
  Tex: "\\approx",
556
717
  mirror: false,
718
+ displayMirror: true,
557
719
  Label: "\u2248",
558
720
  title: "\u062A\u0642\u0631\u064A\u0628\u0627 \u064A\u0633\u0627\u0648\u064A",
559
721
  svg_path: null
@@ -563,6 +725,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
563
725
  category: "operators1",
564
726
  Tex: "\\sim",
565
727
  mirror: false,
728
+ displayMirror: true,
566
729
  Label: "\u223C",
567
730
  title: "\u0645\u0634\u0627\u0628\u0647",
568
731
  svg_path: null
@@ -581,6 +744,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
581
744
  category: "operators1",
582
745
  Tex: "\\leq",
583
746
  mirror: true,
747
+ displayMirror: false,
584
748
  Label: "\u2264",
585
749
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
586
750
  svg_path: null
@@ -590,6 +754,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
590
754
  category: "operators1",
591
755
  Tex: "\\geq",
592
756
  mirror: true,
757
+ displayMirror: false,
593
758
  Label: "\u2265",
594
759
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
595
760
  svg_path: null
@@ -599,6 +764,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
599
764
  category: "operators1",
600
765
  Tex: "\\coloneqq",
601
766
  mirror: true,
767
+ displayMirror: false,
602
768
  Label: "\u2254",
603
769
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
604
770
  svg_path: null
@@ -608,6 +774,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
608
774
  category: "operators1",
609
775
  Tex: "\\eqqcolon",
610
776
  mirror: true,
777
+ displayMirror: false,
611
778
  Label: "\u2255",
612
779
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
613
780
  svg_path: null
@@ -618,6 +785,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
618
785
  category: "operators2",
619
786
  Tex: "\\propto",
620
787
  mirror: true,
788
+ displayMirror: false,
621
789
  Label: "\u221D",
622
790
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
623
791
  svg_path: null
@@ -627,6 +795,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
627
795
  category: "operators2",
628
796
  Tex: "\\in",
629
797
  mirror: true,
798
+ displayMirror: false,
630
799
  Label: "\u2208",
631
800
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
632
801
  svg_path: null
@@ -636,6 +805,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
636
805
  category: "operators2",
637
806
  Tex: "\\notin",
638
807
  mirror: false,
808
+ displayMirror: true,
639
809
  Label: "\u2209",
640
810
  title: "\u0644\u0627 \u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
641
811
  svg_path: null
@@ -645,6 +815,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
645
815
  category: "operators2",
646
816
  Tex: "\\subset",
647
817
  mirror: true,
818
+ displayMirror: false,
648
819
  Label: "\u2282",
649
820
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
650
821
  svg_path: null
@@ -654,6 +825,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
654
825
  category: "operators2",
655
826
  Tex: "\\supset",
656
827
  mirror: true,
828
+ displayMirror: false,
657
829
  Label: "\u2283",
658
830
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
659
831
  svg_path: null
@@ -663,6 +835,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
663
835
  category: "operators2",
664
836
  Tex: "\\subseteq",
665
837
  mirror: true,
838
+ displayMirror: false,
666
839
  Label: "\u2286",
667
840
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
668
841
  svg_path: null
@@ -672,6 +845,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
672
845
  category: "operators2",
673
846
  Tex: "\\supseteq",
674
847
  mirror: true,
848
+ displayMirror: false,
675
849
  Label: "\u2287",
676
850
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
677
851
  svg_path: null
@@ -681,6 +855,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
681
855
  category: "operators2",
682
856
  Tex: "\\cap",
683
857
  mirror: false,
858
+ displayMirror: true,
684
859
  Label: "\u2229",
685
860
  title: "\u062A\u0642\u0627\u0637\u0639",
686
861
  svg_path: null
@@ -690,6 +865,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
690
865
  category: "operators2",
691
866
  Tex: "\\cup",
692
867
  mirror: false,
868
+ displayMirror: true,
693
869
  Label: "\u222A",
694
870
  title: "\u0627\u062A\u062D\u0627\u062F",
695
871
  svg_path: null
@@ -699,6 +875,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
699
875
  category: "operators2",
700
876
  Tex: "\\emptyset",
701
877
  mirror: false,
878
+ displayMirror: true,
702
879
  Label: "\u2205",
703
880
  title: "\u0627\u0644\u0645\u062C\u0645\u0648\u0639\u0629 \u0627\u0644\u062E\u0627\u0644\u064A\u0629",
704
881
  svg_path: null
@@ -708,6 +885,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
708
885
  category: "operators2",
709
886
  Tex: "\\nsubseteq",
710
887
  mirror: true,
888
+ displayMirror: false,
711
889
  Label: "\u2288",
712
890
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
713
891
  svg_path: null
@@ -717,6 +895,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
717
895
  category: "operators2",
718
896
  Tex: "\\nsupseteq",
719
897
  mirror: true,
898
+ displayMirror: false,
720
899
  Label: "\u2289",
721
900
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
722
901
  svg_path: null
@@ -928,6 +1107,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
928
1107
  category: "integrals",
929
1108
  Tex: "\\iint",
930
1109
  mirror: true,
1110
+ displayMirror: false,
931
1111
  Label: "\u222C",
932
1112
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
933
1113
  svg_path: null
@@ -937,6 +1117,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
937
1117
  category: "integrals",
938
1118
  Tex: "\\iiint",
939
1119
  mirror: true,
1120
+ displayMirror: false,
940
1121
  Label: "\u222D",
941
1122
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
942
1123
  svg_path: null
@@ -946,6 +1127,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
946
1127
  category: "integrals",
947
1128
  Tex: "\\iiiint",
948
1129
  mirror: true,
1130
+ displayMirror: false,
949
1131
  Label: "\u2A0C",
950
1132
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
951
1133
  svg_path: null
@@ -955,6 +1137,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
955
1137
  category: "integrals",
956
1138
  Tex: "\\oint",
957
1139
  mirror: true,
1140
+ displayMirror: false,
958
1141
  Label: "\u222E",
959
1142
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
960
1143
  svg_path: null
@@ -964,6 +1147,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
964
1147
  category: "integrals",
965
1148
  Tex: "\\oiint",
966
1149
  mirror: true,
1150
+ displayMirror: false,
967
1151
  Label: "\u222F",
968
1152
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
969
1153
  svg_path: null
@@ -973,6 +1157,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
973
1157
  category: "integrals",
974
1158
  Tex: "\\oiiint",
975
1159
  mirror: true,
1160
+ displayMirror: false,
976
1161
  Label: "\u2230",
977
1162
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
978
1163
  svg_path: null
@@ -1167,6 +1352,22 @@ function renderMatrixRowsArabic(node, options) {
1167
1352
  }
1168
1353
  return `\\begin{${envName}}${rows.join(" \\\\\n")}\\end{${envName}}`;
1169
1354
  }
1355
+ function renderAccentEnglish(command, accent, base, options) {
1356
+ const baseTex = base ? renderChainEnglish(base, options) : "";
1357
+ const accentTex = accent ? renderChainEnglish(accent, options) : "";
1358
+ if (accentTex === "") {
1359
+ return baseTex;
1360
+ }
1361
+ return `\\${command}{${accentTex}}{${baseTex}}`;
1362
+ }
1363
+ function renderAccentArabic(command, accent, base, options) {
1364
+ const baseTex = base ? renderChainArabic(base, options) : "";
1365
+ const accentTex = accent ? renderChainArabic(accent, options) : "";
1366
+ if (accentTex === "") {
1367
+ return baseTex;
1368
+ }
1369
+ return `\\${command}{${accentTex}}{${baseTex}}`;
1370
+ }
1170
1371
  function escapeText(s) {
1171
1372
  return s.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
1172
1373
  }
@@ -1196,6 +1397,19 @@ function renderNodeEnglish(node, options) {
1196
1397
  const body = `\\sqrt${optionalRootIndexEnglish(node, options)}{${renderChainEnglish(node.radicand, options)}}`;
1197
1398
  return `${body}${latinScripts(node, options)}`;
1198
1399
  }
1400
+ if (node.kind === "overset" && node.baseExpr) {
1401
+ const body = renderAccentEnglish("overset", node.overExpr, node.baseExpr, options);
1402
+ return `${body}${latinScripts(node, options)}`;
1403
+ }
1404
+ if (node.kind === "underset" && node.baseExpr) {
1405
+ const body = renderAccentEnglish("underset", node.underExpr, node.baseExpr, options);
1406
+ return `${body}${latinScripts(node, options)}`;
1407
+ }
1408
+ if (node.kind === "accent" && node.baseExpr) {
1409
+ const cmd = accentCommandSpec(node.expr)?.englishTex ?? `\\${node.expr}`;
1410
+ const body = `${cmd}{${renderChainEnglish(node.baseExpr, options)}}`;
1411
+ return `${body}${latinScripts(node, options)}`;
1412
+ }
1199
1413
  if (node.kind === "env") {
1200
1414
  return `${renderMatrixRowsEnglish(node, options)}${latinScripts(node, options)}`;
1201
1415
  }
@@ -1228,6 +1442,19 @@ function renderNodeArabic(node, options) {
1228
1442
  const content = `\\arabsqrt${optionalRootIndexArabic(node, options)}{${renderChainArabic(node.radicand, options)}}`;
1229
1443
  return arabicScripts(node, content, options);
1230
1444
  }
1445
+ if (node.kind === "overset" && node.baseExpr) {
1446
+ const content = renderAccentArabic("overset", node.overExpr, node.baseExpr, options);
1447
+ return arabicScripts(node, content, options);
1448
+ }
1449
+ if (node.kind === "underset" && node.baseExpr) {
1450
+ const content = renderAccentArabic("underset", node.underExpr, node.baseExpr, options);
1451
+ return arabicScripts(node, content, options);
1452
+ }
1453
+ if (node.kind === "accent" && node.baseExpr) {
1454
+ const cmd = accentCommandSpec(node.expr)?.arabicTex ?? `\\${node.expr}`;
1455
+ const content = `${cmd}{${renderChainArabic(node.baseExpr, options)}}`;
1456
+ return arabicScripts(node, content, options);
1457
+ }
1231
1458
  if (node.kind === "env") {
1232
1459
  return arabicScripts(node, renderMatrixRowsArabic(node, options), options);
1233
1460
  }
@@ -1284,6 +1511,35 @@ function collectCaretPositions(chain) {
1284
1511
  if (node.radicand) {
1285
1512
  walkChain(node.radicand);
1286
1513
  }
1514
+ if (node.kind === "overset") {
1515
+ if (node.overExpr) {
1516
+ walkChain(node.overExpr);
1517
+ }
1518
+ if (node.baseExpr) {
1519
+ walkChain(node.baseExpr);
1520
+ }
1521
+ } else if (node.kind === "underset") {
1522
+ if (node.baseExpr) {
1523
+ walkChain(node.baseExpr);
1524
+ }
1525
+ if (node.underExpr) {
1526
+ walkChain(node.underExpr);
1527
+ }
1528
+ } else if (node.kind === "accent") {
1529
+ if (node.baseExpr) {
1530
+ walkChain(node.baseExpr);
1531
+ }
1532
+ } else {
1533
+ if (node.overExpr) {
1534
+ walkChain(node.overExpr);
1535
+ }
1536
+ if (node.underExpr) {
1537
+ walkChain(node.underExpr);
1538
+ }
1539
+ if (node.baseExpr) {
1540
+ walkChain(node.baseExpr);
1541
+ }
1542
+ }
1287
1543
  if (node.matrixRows) {
1288
1544
  for (const row of node.matrixRows) {
1289
1545
  for (const cell of row) {
@@ -1417,6 +1673,42 @@ function findChainById(root, chainId) {
1417
1673
  return nested;
1418
1674
  }
1419
1675
  }
1676
+ if (node.overExpr) {
1677
+ if (node.overExpr.id === chainId) {
1678
+ return {
1679
+ chain: node.overExpr,
1680
+ relation: { kind: "overExpr", ownerNodeId: node.id, ownerChainId: root.id }
1681
+ };
1682
+ }
1683
+ const nested = findChainById(node.overExpr, chainId);
1684
+ if (nested) {
1685
+ return nested;
1686
+ }
1687
+ }
1688
+ if (node.underExpr) {
1689
+ if (node.underExpr.id === chainId) {
1690
+ return {
1691
+ chain: node.underExpr,
1692
+ relation: { kind: "underExpr", ownerNodeId: node.id, ownerChainId: root.id }
1693
+ };
1694
+ }
1695
+ const nested = findChainById(node.underExpr, chainId);
1696
+ if (nested) {
1697
+ return nested;
1698
+ }
1699
+ }
1700
+ if (node.baseExpr) {
1701
+ if (node.baseExpr.id === chainId) {
1702
+ return {
1703
+ chain: node.baseExpr,
1704
+ relation: { kind: "baseExpr", ownerNodeId: node.id, ownerChainId: root.id }
1705
+ };
1706
+ }
1707
+ const nested = findChainById(node.baseExpr, chainId);
1708
+ if (nested) {
1709
+ return nested;
1710
+ }
1711
+ }
1420
1712
  if (node.matrixRows) {
1421
1713
  for (let rowIndex = 0; rowIndex < node.matrixRows.length; rowIndex += 1) {
1422
1714
  const row = node.matrixRows[rowIndex];
@@ -1490,6 +1782,24 @@ function findNodeById(root, nodeId) {
1490
1782
  return nested;
1491
1783
  }
1492
1784
  }
1785
+ if (node.overExpr) {
1786
+ const nested = findNodeById(node.overExpr, nodeId);
1787
+ if (nested) {
1788
+ return nested;
1789
+ }
1790
+ }
1791
+ if (node.underExpr) {
1792
+ const nested = findNodeById(node.underExpr, nodeId);
1793
+ if (nested) {
1794
+ return nested;
1795
+ }
1796
+ }
1797
+ if (node.baseExpr) {
1798
+ const nested = findNodeById(node.baseExpr, nodeId);
1799
+ if (nested) {
1800
+ return nested;
1801
+ }
1802
+ }
1493
1803
  if (node.matrixRows) {
1494
1804
  for (const row of node.matrixRows) {
1495
1805
  for (const cell of row) {
@@ -1525,6 +1835,15 @@ function nodeChildChains(node) {
1525
1835
  if (node.radicand) {
1526
1836
  chains.push(node.radicand);
1527
1837
  }
1838
+ if (node.overExpr) {
1839
+ chains.push(node.overExpr);
1840
+ }
1841
+ if (node.underExpr) {
1842
+ chains.push(node.underExpr);
1843
+ }
1844
+ if (node.baseExpr) {
1845
+ chains.push(node.baseExpr);
1846
+ }
1528
1847
  if (node.matrixRows) {
1529
1848
  for (const row of node.matrixRows) {
1530
1849
  chains.push(...row);
@@ -1895,6 +2214,91 @@ function derivativeTemplateInitialCaret(englishFrac) {
1895
2214
  };
1896
2215
  }
1897
2216
 
2217
+ // src/editor/limitsTemplates.ts
2218
+ var LIMITS_SERIES_TEMPLATE_IDS = /* @__PURE__ */ new Set([
2219
+ "sum",
2220
+ "prod",
2221
+ "lim",
2222
+ "max",
2223
+ "min",
2224
+ "sup",
2225
+ "inf"
2226
+ ]);
2227
+ function isLimitsSeriesTemplateId(commandId) {
2228
+ return LIMITS_SERIES_TEMPLATE_IDS.has(commandId);
2229
+ }
2230
+ function syncChainIds2(english, arabic) {
2231
+ arabic.id = english.id;
2232
+ const limit = Math.min(english.nodes.length, arabic.nodes.length);
2233
+ for (let index = 0; index < limit; index += 1) {
2234
+ arabic.nodes[index].id = english.nodes[index].id;
2235
+ }
2236
+ }
2237
+ function syncOversetIds(english, arabic) {
2238
+ arabic.id = english.id;
2239
+ if (english.overExpr && arabic.overExpr) {
2240
+ syncChainIds2(english.overExpr, arabic.overExpr);
2241
+ }
2242
+ if (english.baseExpr && arabic.baseExpr) {
2243
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
2244
+ }
2245
+ }
2246
+ function syncUndersetIds(english, arabic) {
2247
+ arabic.id = english.id;
2248
+ if (english.underExpr && arabic.underExpr) {
2249
+ syncChainIds2(english.underExpr, arabic.underExpr);
2250
+ }
2251
+ if (english.baseExpr && arabic.baseExpr) {
2252
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
2253
+ }
2254
+ }
2255
+ function buildLimitsSeriesTemplate(commandId) {
2256
+ if (!isLimitsSeriesTemplateId(commandId)) {
2257
+ throw new Error(`BuTeX: unsupported limits-series template id: ${commandId}`);
2258
+ }
2259
+ const englishOp = createAtomicCommandNode(commandId);
2260
+ const arabicOp = createAtomicCommandNode(commandId);
2261
+ arabicOp.id = englishOp.id;
2262
+ const englishOverset = createOversetNode();
2263
+ const arabicOverset = createOversetNode();
2264
+ syncOversetIds(englishOverset, arabicOverset);
2265
+ englishOverset.baseExpr = createEditorChain([englishOp]);
2266
+ arabicOverset.baseExpr = createEditorChain([arabicOp]);
2267
+ syncChainIds2(englishOverset.baseExpr, arabicOverset.baseExpr);
2268
+ const englishUnderset = createUndersetNode();
2269
+ const arabicUnderset = createUndersetNode();
2270
+ syncUndersetIds(englishUnderset, arabicUnderset);
2271
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
2272
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
2273
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
2274
+ return { english: englishUnderset, arabic: arabicUnderset };
2275
+ }
2276
+ function buildAccentPairTemplate() {
2277
+ const englishOverset = createOversetNode();
2278
+ const arabicOverset = createOversetNode();
2279
+ syncOversetIds(englishOverset, arabicOverset);
2280
+ const englishUnderset = createUndersetNode();
2281
+ const arabicUnderset = createUndersetNode();
2282
+ syncUndersetIds(englishUnderset, arabicUnderset);
2283
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
2284
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
2285
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
2286
+ return { english: englishUnderset, arabic: arabicUnderset };
2287
+ }
2288
+ function accentPairTemplateInitialCaret(englishUnderset) {
2289
+ const overset = englishUnderset.baseExpr?.nodes[0];
2290
+ return {
2291
+ chainId: overset?.baseExpr?.id ?? englishUnderset.underExpr?.id ?? "",
2292
+ index: 0
2293
+ };
2294
+ }
2295
+ function limitsSeriesTemplateInitialCaret(englishUnderset) {
2296
+ return {
2297
+ chainId: englishUnderset.underExpr?.id ?? englishUnderset.baseExpr?.id ?? "",
2298
+ index: 0
2299
+ };
2300
+ }
2301
+
1898
2302
  // src/editor/uiLocale.ts
1899
2303
  var ENGLISH_COMMAND_TITLES = {
1900
2304
  cos: "Cosine",
@@ -1938,6 +2342,24 @@ var ENGLISH_COMMAND_TITLES = {
1938
2342
  quadSpace: "Large space",
1939
2343
  qquadSpace: "Extra-large space"
1940
2344
  };
2345
+ var ENGLISH_ACCENT_TITLES = {
2346
+ vec: "Vector arrow",
2347
+ hat: "Hat",
2348
+ tilde: "Tilde",
2349
+ dot: "Dot",
2350
+ ddot: "Double dot",
2351
+ dddot: "Triple dot",
2352
+ bar: "Bar",
2353
+ check: "Check",
2354
+ breve: "Breve",
2355
+ acute: "Acute",
2356
+ grave: "Grave",
2357
+ overline: "Overline",
2358
+ underline: "Underline",
2359
+ overleftarrow: "Over left arrow",
2360
+ overrightarrow: "Over right arrow",
2361
+ overleftrightarrow: "Over left-right arrow"
2362
+ };
1941
2363
  var ENGLISH_OPERATOR_TITLES = {
1942
2364
  inf: "Infinity",
1943
2365
  times: "Multiplication",
@@ -2011,6 +2433,10 @@ var EQUATION_EDITOR_MESSAGES = {
2011
2433
  insertBraces: "\u0625\u062F\u0631\u0627\u062C \u062D\u0627\u0635\u0631\u062A\u064A\u0646",
2012
2434
  insertFraction: "\u0625\u062F\u0631\u0627\u062C \u0643\u0633\u0631",
2013
2435
  insertRoot: "\u0625\u062F\u0631\u0627\u062C \u062C\u0630\u0631",
2436
+ insertOverset: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642",
2437
+ insertUnderset: "\u0625\u062F\u0631\u0627\u062C \u062A\u062D\u062A",
2438
+ insertAccentPair: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642 \u0648\u062A\u062D\u062A",
2439
+ insertAccents: "\u0625\u062F\u0631\u0627\u062C \u0644\u0643\u0646\u0629",
2014
2440
  environments: "\u0627\u0644\u0628\u064A\u0626\u0627\u062A",
2015
2441
  insertEnvironment: "\u0625\u062F\u0631\u0627\u062C \u0628\u064A\u0626\u0629 \u0634\u0628\u0643\u064A\u0629",
2016
2442
  environmentType: "\u0646\u0648\u0639 \u0627\u0644\u0628\u064A\u0626\u0629",
@@ -2118,6 +2544,10 @@ var EQUATION_EDITOR_MESSAGES = {
2118
2544
  insertBraces: "Insert braces",
2119
2545
  insertFraction: "Insert fraction",
2120
2546
  insertRoot: "Insert root",
2547
+ insertOverset: "Insert overset",
2548
+ insertUnderset: "Insert underset",
2549
+ insertAccentPair: "Insert overset and underset",
2550
+ insertAccents: "Insert accent",
2121
2551
  environments: "Environments",
2122
2552
  insertEnvironment: "Insert grid environment",
2123
2553
  environmentType: "Environment type",
@@ -2218,6 +2648,9 @@ function atomicCommandTitle(spec, locale) {
2218
2648
  function atomicOperatorCommandTitle(spec, locale) {
2219
2649
  return locale === "en" ? ENGLISH_OPERATOR_TITLES[spec.id] ?? spec.title : spec.title;
2220
2650
  }
2651
+ function accentCommandTitle(spec, locale) {
2652
+ return locale === "en" ? ENGLISH_ACCENT_TITLES[spec.id] ?? spec.title : spec.title;
2653
+ }
2221
2654
 
2222
2655
  // src/editor/state.ts
2223
2656
  function ensureSyncEntry(session, nodeId) {
@@ -2252,7 +2685,7 @@ function syncSelectionToCaret(session) {
2252
2685
  session.selectedNodeId = located.relation.ownerNodeId;
2253
2686
  return;
2254
2687
  }
2255
- 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") {
2688
+ 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") {
2256
2689
  session.selectedNodeId = located.relation.ownerNodeId;
2257
2690
  return;
2258
2691
  }
@@ -2609,6 +3042,106 @@ function moveCaretVertical(session, direction) {
2609
3042
  appendDebug(next, "moveCaretVertical", "sqrt-radicand-down-exit");
2610
3043
  return next;
2611
3044
  }
3045
+ if (located.relation.kind === "overExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3046
+ if (direction === "down") {
3047
+ next.caret = {
3048
+ chainId: ownerNode.baseExpr.id,
3049
+ index: ownerNode.baseExpr.nodes.length
3050
+ };
3051
+ next.selectedStructureNodeId = null;
3052
+ syncSelectionToCaret(next);
3053
+ appendDebug(next, "moveCaretVertical", "overset-over->base");
3054
+ return next;
3055
+ }
3056
+ next.caret = {
3057
+ chainId: ownerLocated.chain.id,
3058
+ index: ownerLocated.index
3059
+ };
3060
+ next.selectedStructureNodeId = null;
3061
+ syncSelectionToCaret(next);
3062
+ appendDebug(next, "moveCaretVertical", "overset-over-up-exit");
3063
+ return next;
3064
+ }
3065
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "overset" && ownerNode.overExpr) {
3066
+ if (direction === "up") {
3067
+ next.caret = {
3068
+ chainId: ownerNode.overExpr.id,
3069
+ index: ownerNode.overExpr.nodes.length
3070
+ };
3071
+ next.selectedStructureNodeId = null;
3072
+ syncSelectionToCaret(next);
3073
+ appendDebug(next, "moveCaretVertical", "overset-base->over");
3074
+ return next;
3075
+ }
3076
+ next.caret = {
3077
+ chainId: ownerLocated.chain.id,
3078
+ index: ownerLocated.index + 1
3079
+ };
3080
+ next.selectedStructureNodeId = null;
3081
+ syncSelectionToCaret(next);
3082
+ appendDebug(next, "moveCaretVertical", "overset-base-down-exit");
3083
+ return next;
3084
+ }
3085
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "underset" && ownerNode.underExpr) {
3086
+ if (direction === "down") {
3087
+ next.caret = {
3088
+ chainId: ownerNode.underExpr.id,
3089
+ index: ownerNode.underExpr.nodes.length
3090
+ };
3091
+ next.selectedStructureNodeId = null;
3092
+ syncSelectionToCaret(next);
3093
+ appendDebug(next, "moveCaretVertical", "underset-base->under");
3094
+ return next;
3095
+ }
3096
+ next.caret = {
3097
+ chainId: ownerLocated.chain.id,
3098
+ index: ownerLocated.index
3099
+ };
3100
+ next.selectedStructureNodeId = null;
3101
+ syncSelectionToCaret(next);
3102
+ appendDebug(next, "moveCaretVertical", "underset-base-up-exit");
3103
+ return next;
3104
+ }
3105
+ if (located.relation.kind === "underExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3106
+ if (direction === "up") {
3107
+ next.caret = {
3108
+ chainId: ownerNode.baseExpr.id,
3109
+ index: ownerNode.baseExpr.nodes.length
3110
+ };
3111
+ next.selectedStructureNodeId = null;
3112
+ syncSelectionToCaret(next);
3113
+ appendDebug(next, "moveCaretVertical", "underset-under->base");
3114
+ return next;
3115
+ }
3116
+ next.caret = {
3117
+ chainId: ownerLocated.chain.id,
3118
+ index: ownerLocated.index + 1
3119
+ };
3120
+ next.selectedStructureNodeId = null;
3121
+ syncSelectionToCaret(next);
3122
+ appendDebug(next, "moveCaretVertical", "underset-under-down-exit");
3123
+ return next;
3124
+ }
3125
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "accent") {
3126
+ if (direction === "up") {
3127
+ next.caret = {
3128
+ chainId: ownerLocated.chain.id,
3129
+ index: ownerLocated.index
3130
+ };
3131
+ next.selectedStructureNodeId = null;
3132
+ syncSelectionToCaret(next);
3133
+ appendDebug(next, "moveCaretVertical", "accent-base-up-exit");
3134
+ return next;
3135
+ }
3136
+ next.caret = {
3137
+ chainId: ownerLocated.chain.id,
3138
+ index: ownerLocated.index + 1
3139
+ };
3140
+ next.selectedStructureNodeId = null;
3141
+ syncSelectionToCaret(next);
3142
+ appendDebug(next, "moveCaretVertical", "accent-base-down-exit");
3143
+ return next;
3144
+ }
2612
3145
  if (located.relation.kind === "matrixCell" && ownerLocated && ownerNode?.matrixRows) {
2613
3146
  const rowIndex = located.relation.rowIndex ?? 0;
2614
3147
  const columnIndex = located.relation.columnIndex ?? 0;
@@ -2986,6 +3519,119 @@ function insertSqrt(session) {
2986
3519
  appendDebug(next, "insertSqrt", "");
2987
3520
  return next;
2988
3521
  }
3522
+ function insertOverset(session) {
3523
+ const base = replaceSelectionFirst(session);
3524
+ const next = cloneEditorSession(base);
3525
+ const englishNode = createOversetNode();
3526
+ const arabicNode = createOversetNode();
3527
+ arabicNode.id = englishNode.id;
3528
+ if (arabicNode.overExpr && englishNode.overExpr) {
3529
+ arabicNode.overExpr.id = englishNode.overExpr.id;
3530
+ }
3531
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
3532
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
3533
+ }
3534
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3535
+ next.caret = {
3536
+ chainId: englishNode.overExpr ? englishNode.overExpr.id : next.caret.chainId,
3537
+ index: 0
3538
+ };
3539
+ next.selectedStructureNodeId = null;
3540
+ syncSelectionToCaret(next);
3541
+ appendDebug(next, "insertOverset", "");
3542
+ return next;
3543
+ }
3544
+ function insertUnderset(session) {
3545
+ const base = replaceSelectionFirst(session);
3546
+ const next = cloneEditorSession(base);
3547
+ const englishNode = createUndersetNode();
3548
+ const arabicNode = createUndersetNode();
3549
+ arabicNode.id = englishNode.id;
3550
+ if (arabicNode.underExpr && englishNode.underExpr) {
3551
+ arabicNode.underExpr.id = englishNode.underExpr.id;
3552
+ }
3553
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
3554
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
3555
+ }
3556
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3557
+ next.caret = {
3558
+ chainId: englishNode.underExpr ? englishNode.underExpr.id : next.caret.chainId,
3559
+ index: 0
3560
+ };
3561
+ next.selectedStructureNodeId = null;
3562
+ syncSelectionToCaret(next);
3563
+ appendDebug(next, "insertUnderset", "");
3564
+ return next;
3565
+ }
3566
+ function insertLimitsSeriesTemplate(session, commandId) {
3567
+ const base = replaceSelectionFirst(session);
3568
+ const next = cloneEditorSession(base);
3569
+ const { english, arabic } = buildLimitsSeriesTemplate(commandId);
3570
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
3571
+ syncSubtreeRecursive(next, english, arabic);
3572
+ next.caret = limitsSeriesTemplateInitialCaret(english);
3573
+ next.selectedStructureNodeId = null;
3574
+ syncSelectionToCaret(next);
3575
+ appendDebug(next, "insertLimitsSeriesTemplate", commandId);
3576
+ return next;
3577
+ }
3578
+ function insertAccentPair(session) {
3579
+ const base = replaceSelectionFirst(session);
3580
+ const next = cloneEditorSession(base);
3581
+ const { english, arabic } = buildAccentPairTemplate();
3582
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
3583
+ syncSubtreeRecursive(next, english, arabic);
3584
+ next.caret = accentPairTemplateInitialCaret(english);
3585
+ next.selectedStructureNodeId = null;
3586
+ syncSelectionToCaret(next);
3587
+ appendDebug(next, "insertAccentPair", "");
3588
+ return next;
3589
+ }
3590
+ function insertAccent(session, accentId) {
3591
+ const next = cloneEditorSession(session);
3592
+ const englishNode = createAccentNode(accentId);
3593
+ const arabicNode = createAccentNode(accentId);
3594
+ arabicNode.id = englishNode.id;
3595
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
3596
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
3597
+ }
3598
+ if (isSelectionActive(next.selection) && next.selection) {
3599
+ const sel = next.selection;
3600
+ const { start, end } = normalizeSelection(sel);
3601
+ const englishLocated = findChainById(next.englishTree, sel.chainId);
3602
+ const arabicLocated = findChainById(next.arabicTree, sel.chainId);
3603
+ if (englishLocated && arabicLocated && end > start) {
3604
+ const count = end - start;
3605
+ const englishWrapped = englishLocated.chain.nodes.splice(start, count);
3606
+ const arabicWrapped = arabicLocated.chain.nodes.splice(start, count);
3607
+ englishNode.baseExpr.nodes = englishWrapped;
3608
+ arabicNode.baseExpr.nodes = arabicWrapped;
3609
+ englishLocated.chain.nodes.splice(start, 0, englishNode);
3610
+ arabicLocated.chain.nodes.splice(start, 0, arabicNode);
3611
+ syncSubtreeRecursive(next, englishNode, arabicNode);
3612
+ next.caret = {
3613
+ chainId: englishNode.baseExpr.id,
3614
+ index: englishWrapped.length
3615
+ };
3616
+ next.selection = null;
3617
+ next.selectedStructureNodeId = null;
3618
+ syncSelectionToCaret(next);
3619
+ appendDebug(next, "insertAccent", `${accentId}:wrap`);
3620
+ return next;
3621
+ }
3622
+ }
3623
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3624
+ syncSubtreeRecursive(next, englishNode, arabicNode);
3625
+ next.caret = {
3626
+ chainId: englishNode.baseExpr ? englishNode.baseExpr.id : next.caret.chainId,
3627
+ index: 0
3628
+ };
3629
+ next.selection = null;
3630
+ next.selectedStructureNodeId = null;
3631
+ syncSelectionToCaret(next);
3632
+ appendDebug(next, "insertAccent", `${accentId}:empty`);
3633
+ return next;
3634
+ }
2989
3635
  function insertMatrixEnv(session, style, rows, columns) {
2990
3636
  return insertGridEnv(session, style, rows, columns);
2991
3637
  }
@@ -3233,7 +3879,7 @@ function deleteBackward(session) {
3233
3879
  if (!previousNode) {
3234
3880
  return session;
3235
3881
  }
3236
- if ((previousNode.kind === "delimiter" || previousNode.kind === "frac" || previousNode.kind === "sqrt" || previousNode.kind === "env") && next.selectedStructureNodeId !== previousNode.id) {
3882
+ 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) {
3237
3883
  next.selectedStructureNodeId = previousNode.id;
3238
3884
  next.selectedNodeId = previousNode.id;
3239
3885
  appendDebug(next, "deleteBackward", "selected structure");
@@ -3723,6 +4369,168 @@ function buildSqrtNodeBody(node, buildChain, side) {
3723
4369
  return wrap;
3724
4370
  }
3725
4371
 
4372
+ // src/editor/display/overset.ts
4373
+ var OVERSET_CSS = `
4374
+ .overset {
4375
+ display: inline-flex;
4376
+ flex-direction: column;
4377
+ align-items: center;
4378
+ justify-content: center;
4379
+ vertical-align: middle;
4380
+ margin: 0 2px;
4381
+ min-width: 1.2em;
4382
+ }
4383
+
4384
+ .overset-over {
4385
+ display: inline-flex;
4386
+ justify-content: center;
4387
+ align-items: center;
4388
+ min-height: 0.85em;
4389
+ padding: 0 2px;
4390
+ font-size: 0.72em;
4391
+ line-height: 1.1;
4392
+ }
4393
+
4394
+ .overset-base {
4395
+ display: inline-flex;
4396
+ justify-content: center;
4397
+ align-items: center;
4398
+ min-height: 1.05em;
4399
+ padding: 0 2px;
4400
+ }
4401
+ `.trim();
4402
+ function buildOversetNodeBody(node, buildChain) {
4403
+ const wrap = document.createElement("span");
4404
+ wrap.className = "overset";
4405
+ const over = document.createElement("span");
4406
+ over.className = "overset-over";
4407
+ if (node.overExpr) {
4408
+ over.appendChild(buildChain(node.overExpr));
4409
+ }
4410
+ const base = document.createElement("span");
4411
+ base.className = "overset-base";
4412
+ if (node.baseExpr) {
4413
+ base.appendChild(buildChain(node.baseExpr));
4414
+ }
4415
+ wrap.appendChild(over);
4416
+ wrap.appendChild(base);
4417
+ return wrap;
4418
+ }
4419
+
4420
+ // src/editor/display/underset.ts
4421
+ var UNDERSET_CSS = `
4422
+ .underset {
4423
+ display: inline-flex;
4424
+ flex-direction: column;
4425
+ align-items: center;
4426
+ justify-content: center;
4427
+ vertical-align: middle;
4428
+ margin: 0 2px;
4429
+ min-width: 1.2em;
4430
+ }
4431
+
4432
+ .underset-base {
4433
+ display: inline-flex;
4434
+ justify-content: center;
4435
+ align-items: center;
4436
+ min-height: 1.05em;
4437
+ padding: 0 2px;
4438
+ }
4439
+
4440
+ .underset-under {
4441
+ display: inline-flex;
4442
+ justify-content: center;
4443
+ align-items: center;
4444
+ min-height: 0.85em;
4445
+ padding: 0 2px;
4446
+ font-size: 0.72em;
4447
+ line-height: 1.1;
4448
+ }
4449
+ `.trim();
4450
+ function buildUndersetNodeBody(node, buildChain) {
4451
+ const wrap = document.createElement("span");
4452
+ wrap.className = "underset";
4453
+ const base = document.createElement("span");
4454
+ base.className = "underset-base";
4455
+ if (node.baseExpr) {
4456
+ base.appendChild(buildChain(node.baseExpr));
4457
+ }
4458
+ const under = document.createElement("span");
4459
+ under.className = "underset-under";
4460
+ if (node.underExpr) {
4461
+ under.appendChild(buildChain(node.underExpr));
4462
+ }
4463
+ wrap.appendChild(base);
4464
+ wrap.appendChild(under);
4465
+ return wrap;
4466
+ }
4467
+
4468
+ // src/editor/display/accent.ts
4469
+ var ACCENT_CSS = `
4470
+ .math-accent {
4471
+ display: inline-flex;
4472
+ flex-direction: column;
4473
+ align-items: center;
4474
+ justify-content: center;
4475
+ vertical-align: middle;
4476
+ margin: 0 2px;
4477
+ min-width: 1.1em;
4478
+ }
4479
+
4480
+ .math-accent-chrome {
4481
+ display: inline-flex;
4482
+ justify-content: center;
4483
+ align-items: center;
4484
+ min-height: 0.7em;
4485
+ padding: 0 2px;
4486
+ font-size: 0.85em;
4487
+ line-height: 1;
4488
+ color: var(--butex-script-color, #475569);
4489
+ user-select: none;
4490
+ }
4491
+
4492
+ .math-accent-base {
4493
+ display: inline-flex;
4494
+ justify-content: center;
4495
+ align-items: center;
4496
+ min-height: 1.05em;
4497
+ padding: 0 2px;
4498
+ }
4499
+
4500
+ .math-accent--under .math-accent-chrome {
4501
+ min-height: 0.45em;
4502
+ border-top: 1.5px solid currentColor;
4503
+ width: 100%;
4504
+ font-size: 0;
4505
+ color: var(--butex-surface-fg, #0f172a);
4506
+ }
4507
+ `.trim();
4508
+ function buildAccentNodeBody(node, buildChain) {
4509
+ const wrap = document.createElement("span");
4510
+ const spec = accentCommandSpec(node.expr);
4511
+ const placement = spec?.placement ?? "over";
4512
+ wrap.className = placement === "under" ? "math-accent math-accent--under" : "math-accent math-accent--over";
4513
+ const chrome = document.createElement("span");
4514
+ chrome.className = "math-accent-chrome";
4515
+ chrome.setAttribute("aria-hidden", "true");
4516
+ if (placement === "over") {
4517
+ chrome.textContent = spec?.label ?? "\u02C6";
4518
+ }
4519
+ const base = document.createElement("span");
4520
+ base.className = "math-accent-base";
4521
+ if (node.baseExpr) {
4522
+ base.appendChild(buildChain(node.baseExpr));
4523
+ }
4524
+ if (placement === "under") {
4525
+ wrap.appendChild(base);
4526
+ wrap.appendChild(chrome);
4527
+ } else {
4528
+ wrap.appendChild(chrome);
4529
+ wrap.appendChild(base);
4530
+ }
4531
+ return wrap;
4532
+ }
4533
+
3726
4534
  // src/editor/display/env.ts
3727
4535
  var WRAPPERS = {
3728
4536
  matrix: { left: "", right: "" },
@@ -4095,6 +4903,15 @@ function buildNodeBody(node, buildChain, side, digitForm = "western", uiLocale =
4095
4903
  if (node.kind === "sqrt") {
4096
4904
  return buildSqrtNodeBody(node, buildChain, side);
4097
4905
  }
4906
+ if (node.kind === "overset") {
4907
+ return buildOversetNodeBody(node, buildChain);
4908
+ }
4909
+ if (node.kind === "underset") {
4910
+ return buildUndersetNodeBody(node, buildChain);
4911
+ }
4912
+ if (node.kind === "accent") {
4913
+ return buildAccentNodeBody(node, buildChain);
4914
+ }
4098
4915
  if (node.kind === "env") {
4099
4916
  return buildMatrixEnvNodeBody(node, buildChain, side);
4100
4917
  }
@@ -4294,6 +5111,12 @@ ${FRAC_CSS}
4294
5111
 
4295
5112
  ${SQRT_CSS}
4296
5113
 
5114
+ ${OVERSET_CSS}
5115
+
5116
+ ${UNDERSET_CSS}
5117
+
5118
+ ${ACCENT_CSS}
5119
+
4297
5120
  ${MATRIX_ENV_CSS}
4298
5121
 
4299
5122
  ${ATOMIC_COMMAND_CSS}
@@ -5013,6 +5836,26 @@ function createEditorRuntime(options) {
5013
5836
  applyStructural((prev) => insertSqrt(prev));
5014
5837
  surfaceEl.focus();
5015
5838
  },
5839
+ insertOverset: () => {
5840
+ applyStructural((prev) => insertOverset(prev));
5841
+ surfaceEl.focus();
5842
+ },
5843
+ insertUnderset: () => {
5844
+ applyStructural((prev) => insertUnderset(prev));
5845
+ surfaceEl.focus();
5846
+ },
5847
+ insertLimitsSeriesTemplate: (commandId) => {
5848
+ applyStructural((prev) => insertLimitsSeriesTemplate(prev, commandId));
5849
+ surfaceEl.focus();
5850
+ },
5851
+ insertAccentPair: () => {
5852
+ applyStructural((prev) => insertAccentPair(prev));
5853
+ surfaceEl.focus();
5854
+ },
5855
+ insertAccent: (accentId) => {
5856
+ applyStructural((prev) => insertAccent(prev, accentId));
5857
+ surfaceEl.focus();
5858
+ },
5016
5859
  insertAtomicCommand: (commandId) => {
5017
5860
  applyStructural((prev) => insertAtomicCommand(prev, commandId));
5018
5861
  surfaceEl.focus();
@@ -5067,7 +5910,10 @@ function createEditorRuntime(options) {
5067
5910
  // src/mathjax/svgPatcher.ts
5068
5911
  var SVG_ARABSQRT_SELECTOR = 'mjx-container[jax="SVG"] svg .mjx-rtl-mirror[data-mjx-rtl-root="true"]';
5069
5912
  var ARABSQRT_SELECTOR = '.mjx-rtl-mirror[data-mjx-rtl-root="true"]';
5913
+ var SVG_PREVIEW_MIRROR_SELECTOR = 'mjx-container[jax="SVG"] svg .butex-preview-mirror';
5914
+ var PREVIEW_MIRROR_SELECTOR = ".butex-preview-mirror";
5070
5915
  var PATCHED_ATTR = "data-butex-svg-arabsqrt";
5916
+ var MIRROR_PATCHED_ATTR = "data-butex-svg-mirror";
5071
5917
  var ROOT_INDEX_SCALE = 0.72;
5072
5918
  var ROOT_INDEX_GAP_FACTOR = 0.08;
5073
5919
  var ROOT_INDEX_MIN_GAP = 2;
@@ -5092,11 +5938,12 @@ function directSvgElementChildren(element) {
5092
5938
  return Array.from(element.children).filter((child) => child instanceof SVGElement);
5093
5939
  }
5094
5940
  function boxFor(element) {
5095
- if (!(element instanceof SVGGraphicsElement) || typeof element.getBBox !== "function") {
5941
+ const candidate = element;
5942
+ if (typeof candidate.getBBox !== "function") {
5096
5943
  return null;
5097
5944
  }
5098
5945
  try {
5099
- const box = element.getBBox();
5946
+ const box = candidate.getBBox();
5100
5947
  return {
5101
5948
  x: box.x,
5102
5949
  y: box.y,
@@ -5340,6 +6187,31 @@ function patchArabSqrtRoot(root) {
5340
6187
  root.setAttribute(PATCHED_ATTR, "patched");
5341
6188
  rootNode.setAttribute(PATCHED_ATTR, "patched");
5342
6189
  }
6190
+ function patchPreviewMirror(element) {
6191
+ if (element.getAttribute(MIRROR_PATCHED_ATTR) === "patched") {
6192
+ return;
6193
+ }
6194
+ const box = boxFor(element);
6195
+ if (!box || box.width <= 0) {
6196
+ return;
6197
+ }
6198
+ const [tx, ty] = readTranslate(element);
6199
+ const mirrorX = tx + 2 * box.x + box.width;
6200
+ element.setAttribute(
6201
+ "transform",
6202
+ `translate(${roundSvg(mirrorX)},${roundSvg(ty)}) scale(-1,1)`
6203
+ );
6204
+ element.setAttribute(MIRROR_PATCHED_ATTR, "patched");
6205
+ }
6206
+ function collectPreviewMirrors(root) {
6207
+ let mirrors = Array.from(root.querySelectorAll(SVG_PREVIEW_MIRROR_SELECTOR));
6208
+ if (!mirrors.length) {
6209
+ mirrors = Array.from(root.querySelectorAll(PREVIEW_MIRROR_SELECTOR)).filter((candidate) => {
6210
+ return candidate.closest('mjx-container[jax="SVG"]') != null;
6211
+ });
6212
+ }
6213
+ return mirrors;
6214
+ }
5343
6215
  function patchBuTeXSvgMath(root) {
5344
6216
  if (typeof root.querySelectorAll !== "function") {
5345
6217
  return;
@@ -5351,6 +6223,7 @@ function patchBuTeXSvgMath(root) {
5351
6223
  });
5352
6224
  }
5353
6225
  roots.sort((a, b) => rootDepth(b) - rootDepth(a)).forEach((arabSqrtRoot) => patchArabSqrtRoot(arabSqrtRoot));
6226
+ collectPreviewMirrors(root).forEach((mirror) => patchPreviewMirror(mirror));
5354
6227
  }
5355
6228
 
5356
6229
  // src/mathjax/renderIsland.ts
@@ -5624,6 +6497,29 @@ var WIDGET_CHROME_CSS = `
5624
6497
  padding-top: 1px;
5625
6498
  }
5626
6499
 
6500
+ .butex-widget .toolbar-accent-icon {
6501
+ display: inline-flex;
6502
+ flex-direction: column;
6503
+ align-items: center;
6504
+ justify-content: center;
6505
+ min-width: 14px;
6506
+ font-size: 10px;
6507
+ line-height: 1;
6508
+ gap: 1px;
6509
+ }
6510
+
6511
+ .butex-widget .toolbar-accent-icon--over span:first-child,
6512
+ .butex-widget .toolbar-accent-icon--under span:last-child {
6513
+ font-size: 8px;
6514
+ opacity: 0.85;
6515
+ }
6516
+
6517
+ .butex-widget .toolbar-accent-icon--pair span:first-child,
6518
+ .butex-widget .toolbar-accent-icon--pair span:last-child {
6519
+ font-size: 8px;
6520
+ opacity: 0.85;
6521
+ }
6522
+
5627
6523
  .butex-widget .matrix-command-menu {
5628
6524
  position: relative;
5629
6525
  display: inline-flex;
@@ -6365,6 +7261,7 @@ var CHARACTER_FONT_OPTIONS = [
6365
7261
  { id: "diwani", label: "\u062F\u064A\u0648" },
6366
7262
  { id: "diwaniOutline", label: "\u0638\u0644" }
6367
7263
  ];
7264
+ var ACCENT_COMMANDS_LIST = accentCommandsList();
6368
7265
  var FUNCTION_COMMANDS = atomicCommandsByCategory("function");
6369
7266
  var LIMITS_SERIES_COMMANDS = atomicCommandsByCategory("limits_series");
6370
7267
  var FUNCTION2_COMMANDS = atomicCommandsByCategory("function2");
@@ -6512,6 +7409,7 @@ var ButexEditor = (0, import_react.forwardRef)(
6512
7409
  const [operator2MenuOpen, setOperator2MenuOpen] = (0, import_react.useState)(false);
6513
7410
  const [operator3MenuOpen, setOperator3MenuOpen] = (0, import_react.useState)(false);
6514
7411
  const [dotMenuOpen, setDotMenuOpen] = (0, import_react.useState)(false);
7412
+ const [accentMenuOpen, setAccentMenuOpen] = (0, import_react.useState)(false);
6515
7413
  const [integralMenuOpen, setIntegralMenuOpen] = (0, import_react.useState)(false);
6516
7414
  const [selectedDigitForm2, setSelectedDigitForm] = (0, import_react.useState)("western");
6517
7415
  const [selectedCharacterFont, setSelectedCharacterFont] = (0, import_react.useState)("default");
@@ -6781,7 +7679,78 @@ ${arabic || currentMessages.empty}`;
6781
7679
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-sqrt", className: "icon-btn", type: "button", title: messages.insertRoot, "aria-label": messages.insertRoot, onClick: () => {
6782
7680
  runtimeRef.current?.insertSqrt();
6783
7681
  focusSurface();
6784
- }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) })
7682
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) }),
7683
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-overset", className: "icon-btn", type: "button", title: messages.insertOverset, "aria-label": messages.insertOverset, onClick: () => {
7684
+ runtimeRef.current?.insertOverset();
7685
+ focusSurface();
7686
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
7687
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" }),
7688
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" })
7689
+ ] }) }),
7690
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-underset", className: "icon-btn", type: "button", title: messages.insertUnderset, "aria-label": messages.insertUnderset, onClick: () => {
7691
+ runtimeRef.current?.insertUnderset();
7692
+ focusSurface();
7693
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--under", "aria-hidden": "true", children: [
7694
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" }),
7695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" })
7696
+ ] }) }),
7697
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-accent-pair", className: "icon-btn", type: "button", title: messages.insertAccentPair, "aria-label": messages.insertAccentPair, onClick: () => {
7698
+ runtimeRef.current?.insertAccentPair();
7699
+ focusSurface();
7700
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--pair", "aria-hidden": "true", children: [
7701
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" }),
7702
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" }),
7703
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" })
7704
+ ] }) }),
7705
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
7706
+ "div",
7707
+ {
7708
+ className: "function-command-menu operator-command-menu",
7709
+ onBlur: (event) => {
7710
+ const nextFocus = event.relatedTarget;
7711
+ if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) {
7712
+ setAccentMenuOpen(false);
7713
+ }
7714
+ },
7715
+ children: [
7716
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
7717
+ "button",
7718
+ {
7719
+ id: "insert-accents",
7720
+ className: "icon-btn function-command-btn operator-command-btn",
7721
+ type: "button",
7722
+ title: messages.insertAccents,
7723
+ "aria-label": messages.insertAccents,
7724
+ "aria-haspopup": "listbox",
7725
+ "aria-expanded": accentMenuOpen,
7726
+ onClick: () => setAccentMenuOpen((open) => !open),
7727
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
7728
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u02C6" }),
7729
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" })
7730
+ ] })
7731
+ }
7732
+ ),
7733
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "function-command-options operator-command-options", role: "listbox", "aria-label": messages.insertAccents, hidden: !accentMenuOpen, children: ACCENT_COMMANDS_LIST.map((command) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
7734
+ "button",
7735
+ {
7736
+ className: "function-command-option operator-command-option",
7737
+ type: "button",
7738
+ role: "option",
7739
+ title: accentCommandTitle(command, uiLocale),
7740
+ "aria-label": accentCommandTitle(command, uiLocale),
7741
+ onMouseDown: (event) => event.preventDefault(),
7742
+ onClick: () => {
7743
+ runtimeRef.current?.insertAccent(command.id);
7744
+ setAccentMenuOpen(false);
7745
+ focusSurface();
7746
+ },
7747
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "operator-command-label", children: command.label })
7748
+ },
7749
+ command.id
7750
+ )) })
7751
+ ]
7752
+ }
7753
+ )
6785
7754
  ] }),
6786
7755
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "toolbar-group toolbar-environments", role: "group", "aria-label": messages.environments, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
6787
7756
  "div",
@@ -7109,7 +8078,7 @@ ${arabic || currentMessages.empty}`;
7109
8078
  "aria-label": atomicCommandTitle(command, uiLocale),
7110
8079
  onMouseDown: (event) => event.preventDefault(),
7111
8080
  onClick: () => {
7112
- runtimeRef.current?.insertAtomicCommand(command.id);
8081
+ runtimeRef.current?.insertLimitsSeriesTemplate(command.id);
7113
8082
  setLimitsSeriesMenuOpen(false);
7114
8083
  focusSurface();
7115
8084
  },