@drghaliasri/butex 5.3.3 → 5.4.1

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.
@@ -665,6 +665,158 @@ function createEmptyDocument() {
665
665
  };
666
666
  }
667
667
 
668
+ // src/editor/accentCommands.ts
669
+ var ACCENT_COMMANDS = {
670
+ vec: {
671
+ id: "vec",
672
+ englishTex: "\\vec",
673
+ arabicTex: "\\arabvec",
674
+ placement: "over",
675
+ label: "\u20D7",
676
+ title: "\u0633\u0647\u0645 \u0641\u0648\u0642"
677
+ },
678
+ hat: {
679
+ id: "hat",
680
+ englishTex: "\\hat",
681
+ arabicTex: "\\hat",
682
+ placement: "over",
683
+ label: "\u02C6",
684
+ title: "\u0642\u0628\u0639\u0629"
685
+ },
686
+ tilde: {
687
+ id: "tilde",
688
+ englishTex: "\\tilde",
689
+ arabicTex: "\\tilde",
690
+ placement: "over",
691
+ label: "\u02DC",
692
+ title: "\u0645\u062F\u0629"
693
+ },
694
+ dot: {
695
+ id: "dot",
696
+ englishTex: "\\dot",
697
+ arabicTex: "\\dot",
698
+ placement: "over",
699
+ label: "\u02D9",
700
+ title: "\u0646\u0642\u0637\u0629 \u0641\u0648\u0642"
701
+ },
702
+ ddot: {
703
+ id: "ddot",
704
+ englishTex: "\\ddot",
705
+ arabicTex: "\\ddot",
706
+ placement: "over",
707
+ label: "\xA8",
708
+ title: "\u0646\u0642\u0637\u062A\u0627\u0646 \u0641\u0648\u0642"
709
+ },
710
+ dddot: {
711
+ id: "dddot",
712
+ englishTex: "\\dddot",
713
+ arabicTex: "\\dddot",
714
+ placement: "over",
715
+ label: "\u20DB",
716
+ title: "\u062B\u0644\u0627\u062B \u0646\u0642\u0627\u0637 \u0641\u0648\u0642"
717
+ },
718
+ bar: {
719
+ id: "bar",
720
+ englishTex: "\\bar",
721
+ arabicTex: "\\bar",
722
+ placement: "over",
723
+ label: "\xAF",
724
+ title: "\u062E\u0637 \u0641\u0648\u0642"
725
+ },
726
+ check: {
727
+ id: "check",
728
+ englishTex: "\\check",
729
+ arabicTex: "\\check",
730
+ placement: "over",
731
+ label: "\u02C7",
732
+ title: "\u0639\u0644\u0627\u0645\u0629 \u062A\u062D\u0642\u0642"
733
+ },
734
+ breve: {
735
+ id: "breve",
736
+ englishTex: "\\breve",
737
+ arabicTex: "\\breve",
738
+ placement: "over",
739
+ label: "\u02D8",
740
+ title: "\u0642\u0648\u0633 \u0642\u0635\u064A\u0631"
741
+ },
742
+ acute: {
743
+ id: "acute",
744
+ englishTex: "\\acute",
745
+ arabicTex: "\\grave",
746
+ placement: "over",
747
+ label: "\xB4",
748
+ title: "\u0646\u0628\u0631\u0629 \u062D\u0627\u062F\u0629"
749
+ },
750
+ grave: {
751
+ id: "grave",
752
+ englishTex: "\\grave",
753
+ arabicTex: "\\acute",
754
+ placement: "over",
755
+ label: "`",
756
+ title: "\u0646\u0628\u0631\u0629 \u062B\u0642\u064A\u0644\u0629"
757
+ },
758
+ overline: {
759
+ id: "overline",
760
+ englishTex: "\\overline",
761
+ arabicTex: "\\overline",
762
+ placement: "over",
763
+ label: "\u203E",
764
+ title: "\u062E\u0637 \u0639\u0644\u0648\u064A \u0645\u0645\u062A\u062F"
765
+ },
766
+ underline: {
767
+ id: "underline",
768
+ englishTex: "\\underline",
769
+ arabicTex: "\\underline",
770
+ placement: "under",
771
+ label: "_",
772
+ title: "\u062E\u0637 \u0633\u0641\u0644\u064A"
773
+ },
774
+ overleftarrow: {
775
+ id: "overleftarrow",
776
+ englishTex: "\\overleftarrow",
777
+ arabicTex: "\\overrightarrow",
778
+ placement: "over",
779
+ label: "\u2190",
780
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631"
781
+ },
782
+ overrightarrow: {
783
+ id: "overrightarrow",
784
+ englishTex: "\\overrightarrow",
785
+ arabicTex: "\\overleftarrow",
786
+ placement: "over",
787
+ label: "\u2192",
788
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646"
789
+ },
790
+ overleftrightarrow: {
791
+ id: "overleftrightarrow",
792
+ englishTex: "\\overleftrightarrow",
793
+ arabicTex: "\\overleftrightarrow",
794
+ placement: "over",
795
+ label: "\u2194",
796
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0645\u0632\u062F\u0648\u062C \u0627\u0644\u0627\u062A\u062C\u0627\u0647"
797
+ }
798
+ };
799
+ function accentCommandSpec(id) {
800
+ return Object.prototype.hasOwnProperty.call(ACCENT_COMMANDS, id) ? ACCENT_COMMANDS[id] : null;
801
+ }
802
+ function accentCommandsList() {
803
+ return Object.values(ACCENT_COMMANDS);
804
+ }
805
+ function accentCommandIdFromTex(tex) {
806
+ const normalized = tex.startsWith("\\") ? tex : `\\${tex}`;
807
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
808
+ if (spec.englishTex === normalized) {
809
+ return spec.id;
810
+ }
811
+ }
812
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
813
+ if (spec.arabicTex === normalized) {
814
+ return spec.id;
815
+ }
816
+ }
817
+ return null;
818
+ }
819
+
668
820
  // src/editor/atomicCommands.ts
669
821
  var ATOMIC_COMMANDS = {
670
822
  ///////////////////////// Functions ////////////////////////////////
@@ -1639,6 +1791,9 @@ function createEditorNode(kind, expr) {
1639
1791
  denominator: null,
1640
1792
  radicand: null,
1641
1793
  rootIndex: null,
1794
+ overExpr: null,
1795
+ underExpr: null,
1796
+ baseExpr: null,
1642
1797
  envName: null,
1643
1798
  matrixStyle: null,
1644
1799
  matrixRows: null,
@@ -1667,6 +1822,26 @@ function createSqrtNode() {
1667
1822
  rootIndex: createEditorChain()
1668
1823
  };
1669
1824
  }
1825
+ function createOversetNode() {
1826
+ return {
1827
+ ...createEditorNode("overset", ""),
1828
+ overExpr: createEditorChain(),
1829
+ baseExpr: createEditorChain()
1830
+ };
1831
+ }
1832
+ function createUndersetNode() {
1833
+ return {
1834
+ ...createEditorNode("underset", ""),
1835
+ underExpr: createEditorChain(),
1836
+ baseExpr: createEditorChain()
1837
+ };
1838
+ }
1839
+ function createAccentNode(accentId) {
1840
+ return {
1841
+ ...createEditorNode("accent", accentId),
1842
+ baseExpr: createEditorChain()
1843
+ };
1844
+ }
1670
1845
  function createGridEnvNode(envName, rows, columns, columnAlignments) {
1671
1846
  const safeRows = Math.max(1, Math.min(12, Math.trunc(rows)));
1672
1847
  const safeColumns = Math.max(1, Math.min(12, Math.trunc(columns)));
@@ -1731,6 +1906,15 @@ function collectNodeIds(chain, ids) {
1731
1906
  if (node.radicand) {
1732
1907
  collectNodeIds(node.radicand, ids);
1733
1908
  }
1909
+ if (node.overExpr) {
1910
+ collectNodeIds(node.overExpr, ids);
1911
+ }
1912
+ if (node.underExpr) {
1913
+ collectNodeIds(node.underExpr, ids);
1914
+ }
1915
+ if (node.baseExpr) {
1916
+ collectNodeIds(node.baseExpr, ids);
1917
+ }
1734
1918
  if (node.matrixRows) {
1735
1919
  for (const row of node.matrixRows) {
1736
1920
  for (const cell of row) {
@@ -1895,6 +2079,22 @@ function renderMatrixRowsArabic(node, options) {
1895
2079
  }
1896
2080
  return `\\begin{${envName}}${rows.join(" \\\\\n")}\\end{${envName}}`;
1897
2081
  }
2082
+ function renderAccentEnglish(command, accent, base, options) {
2083
+ const baseTex = base ? renderChainEnglish(base, options) : "";
2084
+ const accentTex = accent ? renderChainEnglish(accent, options) : "";
2085
+ if (accentTex === "") {
2086
+ return baseTex;
2087
+ }
2088
+ return `\\${command}{${accentTex}}{${baseTex}}`;
2089
+ }
2090
+ function renderAccentArabic(command, accent, base, options) {
2091
+ const baseTex = base ? renderChainArabic(base, options) : "";
2092
+ const accentTex = accent ? renderChainArabic(accent, options) : "";
2093
+ if (accentTex === "") {
2094
+ return baseTex;
2095
+ }
2096
+ return `\\${command}{${accentTex}}{${baseTex}}`;
2097
+ }
1898
2098
  function escapeText(s) {
1899
2099
  return s.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
1900
2100
  }
@@ -1924,6 +2124,19 @@ function renderNodeEnglish(node, options) {
1924
2124
  const body = `\\sqrt${optionalRootIndexEnglish(node, options)}{${renderChainEnglish(node.radicand, options)}}`;
1925
2125
  return `${body}${latinScripts(node, options)}`;
1926
2126
  }
2127
+ if (node.kind === "overset" && node.baseExpr) {
2128
+ const body = renderAccentEnglish("overset", node.overExpr, node.baseExpr, options);
2129
+ return `${body}${latinScripts(node, options)}`;
2130
+ }
2131
+ if (node.kind === "underset" && node.baseExpr) {
2132
+ const body = renderAccentEnglish("underset", node.underExpr, node.baseExpr, options);
2133
+ return `${body}${latinScripts(node, options)}`;
2134
+ }
2135
+ if (node.kind === "accent" && node.baseExpr) {
2136
+ const cmd = accentCommandSpec(node.expr)?.englishTex ?? `\\${node.expr}`;
2137
+ const body = `${cmd}{${renderChainEnglish(node.baseExpr, options)}}`;
2138
+ return `${body}${latinScripts(node, options)}`;
2139
+ }
1927
2140
  if (node.kind === "env") {
1928
2141
  return `${renderMatrixRowsEnglish(node, options)}${latinScripts(node, options)}`;
1929
2142
  }
@@ -1956,6 +2169,19 @@ function renderNodeArabic(node, options) {
1956
2169
  const content = `\\arabsqrt${optionalRootIndexArabic(node, options)}{${renderChainArabic(node.radicand, options)}}`;
1957
2170
  return arabicScripts(node, content, options);
1958
2171
  }
2172
+ if (node.kind === "overset" && node.baseExpr) {
2173
+ const content = renderAccentArabic("overset", node.overExpr, node.baseExpr, options);
2174
+ return arabicScripts(node, content, options);
2175
+ }
2176
+ if (node.kind === "underset" && node.baseExpr) {
2177
+ const content = renderAccentArabic("underset", node.underExpr, node.baseExpr, options);
2178
+ return arabicScripts(node, content, options);
2179
+ }
2180
+ if (node.kind === "accent" && node.baseExpr) {
2181
+ const cmd = accentCommandSpec(node.expr)?.arabicTex ?? `\\${node.expr}`;
2182
+ const content = `${cmd}{${renderChainArabic(node.baseExpr, options)}}`;
2183
+ return arabicScripts(node, content, options);
2184
+ }
1959
2185
  if (node.kind === "env") {
1960
2186
  return arabicScripts(node, renderMatrixRowsArabic(node, options), options);
1961
2187
  }
@@ -2012,6 +2238,35 @@ function collectCaretPositions(chain) {
2012
2238
  if (node.radicand) {
2013
2239
  walkChain(node.radicand);
2014
2240
  }
2241
+ if (node.kind === "overset") {
2242
+ if (node.overExpr) {
2243
+ walkChain(node.overExpr);
2244
+ }
2245
+ if (node.baseExpr) {
2246
+ walkChain(node.baseExpr);
2247
+ }
2248
+ } else if (node.kind === "underset") {
2249
+ if (node.baseExpr) {
2250
+ walkChain(node.baseExpr);
2251
+ }
2252
+ if (node.underExpr) {
2253
+ walkChain(node.underExpr);
2254
+ }
2255
+ } else if (node.kind === "accent") {
2256
+ if (node.baseExpr) {
2257
+ walkChain(node.baseExpr);
2258
+ }
2259
+ } else {
2260
+ if (node.overExpr) {
2261
+ walkChain(node.overExpr);
2262
+ }
2263
+ if (node.underExpr) {
2264
+ walkChain(node.underExpr);
2265
+ }
2266
+ if (node.baseExpr) {
2267
+ walkChain(node.baseExpr);
2268
+ }
2269
+ }
2015
2270
  if (node.matrixRows) {
2016
2271
  for (const row of node.matrixRows) {
2017
2272
  for (const cell of row) {
@@ -2145,6 +2400,42 @@ function findChainById(root, chainId) {
2145
2400
  return nested;
2146
2401
  }
2147
2402
  }
2403
+ if (node.overExpr) {
2404
+ if (node.overExpr.id === chainId) {
2405
+ return {
2406
+ chain: node.overExpr,
2407
+ relation: { kind: "overExpr", ownerNodeId: node.id, ownerChainId: root.id }
2408
+ };
2409
+ }
2410
+ const nested = findChainById(node.overExpr, chainId);
2411
+ if (nested) {
2412
+ return nested;
2413
+ }
2414
+ }
2415
+ if (node.underExpr) {
2416
+ if (node.underExpr.id === chainId) {
2417
+ return {
2418
+ chain: node.underExpr,
2419
+ relation: { kind: "underExpr", ownerNodeId: node.id, ownerChainId: root.id }
2420
+ };
2421
+ }
2422
+ const nested = findChainById(node.underExpr, chainId);
2423
+ if (nested) {
2424
+ return nested;
2425
+ }
2426
+ }
2427
+ if (node.baseExpr) {
2428
+ if (node.baseExpr.id === chainId) {
2429
+ return {
2430
+ chain: node.baseExpr,
2431
+ relation: { kind: "baseExpr", ownerNodeId: node.id, ownerChainId: root.id }
2432
+ };
2433
+ }
2434
+ const nested = findChainById(node.baseExpr, chainId);
2435
+ if (nested) {
2436
+ return nested;
2437
+ }
2438
+ }
2148
2439
  if (node.matrixRows) {
2149
2440
  for (let rowIndex = 0; rowIndex < node.matrixRows.length; rowIndex += 1) {
2150
2441
  const row = node.matrixRows[rowIndex];
@@ -2218,6 +2509,24 @@ function findNodeById(root, nodeId) {
2218
2509
  return nested;
2219
2510
  }
2220
2511
  }
2512
+ if (node.overExpr) {
2513
+ const nested = findNodeById(node.overExpr, nodeId);
2514
+ if (nested) {
2515
+ return nested;
2516
+ }
2517
+ }
2518
+ if (node.underExpr) {
2519
+ const nested = findNodeById(node.underExpr, nodeId);
2520
+ if (nested) {
2521
+ return nested;
2522
+ }
2523
+ }
2524
+ if (node.baseExpr) {
2525
+ const nested = findNodeById(node.baseExpr, nodeId);
2526
+ if (nested) {
2527
+ return nested;
2528
+ }
2529
+ }
2221
2530
  if (node.matrixRows) {
2222
2531
  for (const row of node.matrixRows) {
2223
2532
  for (const cell of row) {
@@ -2253,6 +2562,15 @@ function nodeChildChains(node) {
2253
2562
  if (node.radicand) {
2254
2563
  chains.push(node.radicand);
2255
2564
  }
2565
+ if (node.overExpr) {
2566
+ chains.push(node.overExpr);
2567
+ }
2568
+ if (node.underExpr) {
2569
+ chains.push(node.underExpr);
2570
+ }
2571
+ if (node.baseExpr) {
2572
+ chains.push(node.baseExpr);
2573
+ }
2256
2574
  if (node.matrixRows) {
2257
2575
  for (const row of node.matrixRows) {
2258
2576
  chains.push(...row);
@@ -2623,6 +2941,91 @@ function derivativeTemplateInitialCaret(englishFrac) {
2623
2941
  };
2624
2942
  }
2625
2943
 
2944
+ // src/editor/limitsTemplates.ts
2945
+ var LIMITS_SERIES_TEMPLATE_IDS = /* @__PURE__ */ new Set([
2946
+ "sum",
2947
+ "prod",
2948
+ "lim",
2949
+ "max",
2950
+ "min",
2951
+ "sup",
2952
+ "inf"
2953
+ ]);
2954
+ function isLimitsSeriesTemplateId(commandId) {
2955
+ return LIMITS_SERIES_TEMPLATE_IDS.has(commandId);
2956
+ }
2957
+ function syncChainIds2(english, arabic) {
2958
+ arabic.id = english.id;
2959
+ const limit = Math.min(english.nodes.length, arabic.nodes.length);
2960
+ for (let index = 0; index < limit; index += 1) {
2961
+ arabic.nodes[index].id = english.nodes[index].id;
2962
+ }
2963
+ }
2964
+ function syncOversetIds(english, arabic) {
2965
+ arabic.id = english.id;
2966
+ if (english.overExpr && arabic.overExpr) {
2967
+ syncChainIds2(english.overExpr, arabic.overExpr);
2968
+ }
2969
+ if (english.baseExpr && arabic.baseExpr) {
2970
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
2971
+ }
2972
+ }
2973
+ function syncUndersetIds(english, arabic) {
2974
+ arabic.id = english.id;
2975
+ if (english.underExpr && arabic.underExpr) {
2976
+ syncChainIds2(english.underExpr, arabic.underExpr);
2977
+ }
2978
+ if (english.baseExpr && arabic.baseExpr) {
2979
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
2980
+ }
2981
+ }
2982
+ function buildLimitsSeriesTemplate(commandId) {
2983
+ if (!isLimitsSeriesTemplateId(commandId)) {
2984
+ throw new Error(`BuTeX: unsupported limits-series template id: ${commandId}`);
2985
+ }
2986
+ const englishOp = createAtomicCommandNode(commandId);
2987
+ const arabicOp = createAtomicCommandNode(commandId);
2988
+ arabicOp.id = englishOp.id;
2989
+ const englishOverset = createOversetNode();
2990
+ const arabicOverset = createOversetNode();
2991
+ syncOversetIds(englishOverset, arabicOverset);
2992
+ englishOverset.baseExpr = createEditorChain([englishOp]);
2993
+ arabicOverset.baseExpr = createEditorChain([arabicOp]);
2994
+ syncChainIds2(englishOverset.baseExpr, arabicOverset.baseExpr);
2995
+ const englishUnderset = createUndersetNode();
2996
+ const arabicUnderset = createUndersetNode();
2997
+ syncUndersetIds(englishUnderset, arabicUnderset);
2998
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
2999
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
3000
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
3001
+ return { english: englishUnderset, arabic: arabicUnderset };
3002
+ }
3003
+ function buildAccentPairTemplate() {
3004
+ const englishOverset = createOversetNode();
3005
+ const arabicOverset = createOversetNode();
3006
+ syncOversetIds(englishOverset, arabicOverset);
3007
+ const englishUnderset = createUndersetNode();
3008
+ const arabicUnderset = createUndersetNode();
3009
+ syncUndersetIds(englishUnderset, arabicUnderset);
3010
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
3011
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
3012
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
3013
+ return { english: englishUnderset, arabic: arabicUnderset };
3014
+ }
3015
+ function accentPairTemplateInitialCaret(englishUnderset) {
3016
+ const overset = englishUnderset.baseExpr?.nodes[0];
3017
+ return {
3018
+ chainId: overset?.baseExpr?.id ?? englishUnderset.underExpr?.id ?? "",
3019
+ index: 0
3020
+ };
3021
+ }
3022
+ function limitsSeriesTemplateInitialCaret(englishUnderset) {
3023
+ return {
3024
+ chainId: englishUnderset.underExpr?.id ?? englishUnderset.baseExpr?.id ?? "",
3025
+ index: 0
3026
+ };
3027
+ }
3028
+
2626
3029
  // src/editor/uiLocale.ts
2627
3030
  var ENGLISH_COMMAND_TITLES = {
2628
3031
  cos: "Cosine",
@@ -2666,6 +3069,24 @@ var ENGLISH_COMMAND_TITLES = {
2666
3069
  quadSpace: "Large space",
2667
3070
  qquadSpace: "Extra-large space"
2668
3071
  };
3072
+ var ENGLISH_ACCENT_TITLES = {
3073
+ vec: "Vector arrow",
3074
+ hat: "Hat",
3075
+ tilde: "Tilde",
3076
+ dot: "Dot",
3077
+ ddot: "Double dot",
3078
+ dddot: "Triple dot",
3079
+ bar: "Bar",
3080
+ check: "Check",
3081
+ breve: "Breve",
3082
+ acute: "Acute",
3083
+ grave: "Grave",
3084
+ overline: "Overline",
3085
+ underline: "Underline",
3086
+ overleftarrow: "Over left arrow",
3087
+ overrightarrow: "Over right arrow",
3088
+ overleftrightarrow: "Over left-right arrow"
3089
+ };
2669
3090
  var ENGLISH_OPERATOR_TITLES = {
2670
3091
  inf: "Infinity",
2671
3092
  times: "Multiplication",
@@ -2739,6 +3160,10 @@ var EQUATION_EDITOR_MESSAGES = {
2739
3160
  insertBraces: "\u0625\u062F\u0631\u0627\u062C \u062D\u0627\u0635\u0631\u062A\u064A\u0646",
2740
3161
  insertFraction: "\u0625\u062F\u0631\u0627\u062C \u0643\u0633\u0631",
2741
3162
  insertRoot: "\u0625\u062F\u0631\u0627\u062C \u062C\u0630\u0631",
3163
+ insertOverset: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642",
3164
+ insertUnderset: "\u0625\u062F\u0631\u0627\u062C \u062A\u062D\u062A",
3165
+ insertAccentPair: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642 \u0648\u062A\u062D\u062A",
3166
+ insertAccents: "\u0625\u062F\u0631\u0627\u062C \u0644\u0643\u0646\u0629",
2742
3167
  environments: "\u0627\u0644\u0628\u064A\u0626\u0627\u062A",
2743
3168
  insertEnvironment: "\u0625\u062F\u0631\u0627\u062C \u0628\u064A\u0626\u0629 \u0634\u0628\u0643\u064A\u0629",
2744
3169
  environmentType: "\u0646\u0648\u0639 \u0627\u0644\u0628\u064A\u0626\u0629",
@@ -2846,6 +3271,10 @@ var EQUATION_EDITOR_MESSAGES = {
2846
3271
  insertBraces: "Insert braces",
2847
3272
  insertFraction: "Insert fraction",
2848
3273
  insertRoot: "Insert root",
3274
+ insertOverset: "Insert overset",
3275
+ insertUnderset: "Insert underset",
3276
+ insertAccentPair: "Insert overset and underset",
3277
+ insertAccents: "Insert accent",
2849
3278
  environments: "Environments",
2850
3279
  insertEnvironment: "Insert grid environment",
2851
3280
  environmentType: "Environment type",
@@ -2946,6 +3375,9 @@ function atomicCommandTitle(spec, locale) {
2946
3375
  function atomicOperatorCommandTitle(spec, locale) {
2947
3376
  return locale === "en" ? ENGLISH_OPERATOR_TITLES[spec.id] ?? spec.title : spec.title;
2948
3377
  }
3378
+ function accentCommandTitle(spec, locale) {
3379
+ return locale === "en" ? ENGLISH_ACCENT_TITLES[spec.id] ?? spec.title : spec.title;
3380
+ }
2949
3381
 
2950
3382
  // src/editor/state.ts
2951
3383
  function ensureSyncEntry(session, nodeId) {
@@ -2980,7 +3412,7 @@ function syncSelectionToCaret(session) {
2980
3412
  session.selectedNodeId = located.relation.ownerNodeId;
2981
3413
  return;
2982
3414
  }
2983
- 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") {
3415
+ 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") {
2984
3416
  session.selectedNodeId = located.relation.ownerNodeId;
2985
3417
  return;
2986
3418
  }
@@ -3337,6 +3769,106 @@ function moveCaretVertical(session, direction) {
3337
3769
  appendDebug(next, "moveCaretVertical", "sqrt-radicand-down-exit");
3338
3770
  return next;
3339
3771
  }
3772
+ if (located.relation.kind === "overExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3773
+ if (direction === "down") {
3774
+ next.caret = {
3775
+ chainId: ownerNode.baseExpr.id,
3776
+ index: ownerNode.baseExpr.nodes.length
3777
+ };
3778
+ next.selectedStructureNodeId = null;
3779
+ syncSelectionToCaret(next);
3780
+ appendDebug(next, "moveCaretVertical", "overset-over->base");
3781
+ return next;
3782
+ }
3783
+ next.caret = {
3784
+ chainId: ownerLocated.chain.id,
3785
+ index: ownerLocated.index
3786
+ };
3787
+ next.selectedStructureNodeId = null;
3788
+ syncSelectionToCaret(next);
3789
+ appendDebug(next, "moveCaretVertical", "overset-over-up-exit");
3790
+ return next;
3791
+ }
3792
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "overset" && ownerNode.overExpr) {
3793
+ if (direction === "up") {
3794
+ next.caret = {
3795
+ chainId: ownerNode.overExpr.id,
3796
+ index: ownerNode.overExpr.nodes.length
3797
+ };
3798
+ next.selectedStructureNodeId = null;
3799
+ syncSelectionToCaret(next);
3800
+ appendDebug(next, "moveCaretVertical", "overset-base->over");
3801
+ return next;
3802
+ }
3803
+ next.caret = {
3804
+ chainId: ownerLocated.chain.id,
3805
+ index: ownerLocated.index + 1
3806
+ };
3807
+ next.selectedStructureNodeId = null;
3808
+ syncSelectionToCaret(next);
3809
+ appendDebug(next, "moveCaretVertical", "overset-base-down-exit");
3810
+ return next;
3811
+ }
3812
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "underset" && ownerNode.underExpr) {
3813
+ if (direction === "down") {
3814
+ next.caret = {
3815
+ chainId: ownerNode.underExpr.id,
3816
+ index: ownerNode.underExpr.nodes.length
3817
+ };
3818
+ next.selectedStructureNodeId = null;
3819
+ syncSelectionToCaret(next);
3820
+ appendDebug(next, "moveCaretVertical", "underset-base->under");
3821
+ return next;
3822
+ }
3823
+ next.caret = {
3824
+ chainId: ownerLocated.chain.id,
3825
+ index: ownerLocated.index
3826
+ };
3827
+ next.selectedStructureNodeId = null;
3828
+ syncSelectionToCaret(next);
3829
+ appendDebug(next, "moveCaretVertical", "underset-base-up-exit");
3830
+ return next;
3831
+ }
3832
+ if (located.relation.kind === "underExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3833
+ if (direction === "up") {
3834
+ next.caret = {
3835
+ chainId: ownerNode.baseExpr.id,
3836
+ index: ownerNode.baseExpr.nodes.length
3837
+ };
3838
+ next.selectedStructureNodeId = null;
3839
+ syncSelectionToCaret(next);
3840
+ appendDebug(next, "moveCaretVertical", "underset-under->base");
3841
+ return next;
3842
+ }
3843
+ next.caret = {
3844
+ chainId: ownerLocated.chain.id,
3845
+ index: ownerLocated.index + 1
3846
+ };
3847
+ next.selectedStructureNodeId = null;
3848
+ syncSelectionToCaret(next);
3849
+ appendDebug(next, "moveCaretVertical", "underset-under-down-exit");
3850
+ return next;
3851
+ }
3852
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "accent") {
3853
+ if (direction === "up") {
3854
+ next.caret = {
3855
+ chainId: ownerLocated.chain.id,
3856
+ index: ownerLocated.index
3857
+ };
3858
+ next.selectedStructureNodeId = null;
3859
+ syncSelectionToCaret(next);
3860
+ appendDebug(next, "moveCaretVertical", "accent-base-up-exit");
3861
+ return next;
3862
+ }
3863
+ next.caret = {
3864
+ chainId: ownerLocated.chain.id,
3865
+ index: ownerLocated.index + 1
3866
+ };
3867
+ next.selectedStructureNodeId = null;
3868
+ syncSelectionToCaret(next);
3869
+ appendDebug(next, "moveCaretVertical", "accent-base-down-exit");
3870
+ return next;
3871
+ }
3340
3872
  if (located.relation.kind === "matrixCell" && ownerLocated && ownerNode?.matrixRows) {
3341
3873
  const rowIndex = located.relation.rowIndex ?? 0;
3342
3874
  const columnIndex = located.relation.columnIndex ?? 0;
@@ -3714,6 +4246,119 @@ function insertSqrt(session) {
3714
4246
  appendDebug(next, "insertSqrt", "");
3715
4247
  return next;
3716
4248
  }
4249
+ function insertOverset(session) {
4250
+ const base = replaceSelectionFirst(session);
4251
+ const next = cloneEditorSession(base);
4252
+ const englishNode = createOversetNode();
4253
+ const arabicNode = createOversetNode();
4254
+ arabicNode.id = englishNode.id;
4255
+ if (arabicNode.overExpr && englishNode.overExpr) {
4256
+ arabicNode.overExpr.id = englishNode.overExpr.id;
4257
+ }
4258
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4259
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4260
+ }
4261
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4262
+ next.caret = {
4263
+ chainId: englishNode.overExpr ? englishNode.overExpr.id : next.caret.chainId,
4264
+ index: 0
4265
+ };
4266
+ next.selectedStructureNodeId = null;
4267
+ syncSelectionToCaret(next);
4268
+ appendDebug(next, "insertOverset", "");
4269
+ return next;
4270
+ }
4271
+ function insertUnderset(session) {
4272
+ const base = replaceSelectionFirst(session);
4273
+ const next = cloneEditorSession(base);
4274
+ const englishNode = createUndersetNode();
4275
+ const arabicNode = createUndersetNode();
4276
+ arabicNode.id = englishNode.id;
4277
+ if (arabicNode.underExpr && englishNode.underExpr) {
4278
+ arabicNode.underExpr.id = englishNode.underExpr.id;
4279
+ }
4280
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4281
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4282
+ }
4283
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4284
+ next.caret = {
4285
+ chainId: englishNode.underExpr ? englishNode.underExpr.id : next.caret.chainId,
4286
+ index: 0
4287
+ };
4288
+ next.selectedStructureNodeId = null;
4289
+ syncSelectionToCaret(next);
4290
+ appendDebug(next, "insertUnderset", "");
4291
+ return next;
4292
+ }
4293
+ function insertLimitsSeriesTemplate(session, commandId) {
4294
+ const base = replaceSelectionFirst(session);
4295
+ const next = cloneEditorSession(base);
4296
+ const { english, arabic } = buildLimitsSeriesTemplate(commandId);
4297
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
4298
+ syncSubtreeRecursive(next, english, arabic);
4299
+ next.caret = limitsSeriesTemplateInitialCaret(english);
4300
+ next.selectedStructureNodeId = null;
4301
+ syncSelectionToCaret(next);
4302
+ appendDebug(next, "insertLimitsSeriesTemplate", commandId);
4303
+ return next;
4304
+ }
4305
+ function insertAccentPair(session) {
4306
+ const base = replaceSelectionFirst(session);
4307
+ const next = cloneEditorSession(base);
4308
+ const { english, arabic } = buildAccentPairTemplate();
4309
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
4310
+ syncSubtreeRecursive(next, english, arabic);
4311
+ next.caret = accentPairTemplateInitialCaret(english);
4312
+ next.selectedStructureNodeId = null;
4313
+ syncSelectionToCaret(next);
4314
+ appendDebug(next, "insertAccentPair", "");
4315
+ return next;
4316
+ }
4317
+ function insertAccent(session, accentId) {
4318
+ const next = cloneEditorSession(session);
4319
+ const englishNode = createAccentNode(accentId);
4320
+ const arabicNode = createAccentNode(accentId);
4321
+ arabicNode.id = englishNode.id;
4322
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4323
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4324
+ }
4325
+ if (isSelectionActive(next.selection) && next.selection) {
4326
+ const sel = next.selection;
4327
+ const { start, end } = normalizeSelection(sel);
4328
+ const englishLocated = findChainById(next.englishTree, sel.chainId);
4329
+ const arabicLocated = findChainById(next.arabicTree, sel.chainId);
4330
+ if (englishLocated && arabicLocated && end > start) {
4331
+ const count = end - start;
4332
+ const englishWrapped = englishLocated.chain.nodes.splice(start, count);
4333
+ const arabicWrapped = arabicLocated.chain.nodes.splice(start, count);
4334
+ englishNode.baseExpr.nodes = englishWrapped;
4335
+ arabicNode.baseExpr.nodes = arabicWrapped;
4336
+ englishLocated.chain.nodes.splice(start, 0, englishNode);
4337
+ arabicLocated.chain.nodes.splice(start, 0, arabicNode);
4338
+ syncSubtreeRecursive(next, englishNode, arabicNode);
4339
+ next.caret = {
4340
+ chainId: englishNode.baseExpr.id,
4341
+ index: englishWrapped.length
4342
+ };
4343
+ next.selection = null;
4344
+ next.selectedStructureNodeId = null;
4345
+ syncSelectionToCaret(next);
4346
+ appendDebug(next, "insertAccent", `${accentId}:wrap`);
4347
+ return next;
4348
+ }
4349
+ }
4350
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4351
+ syncSubtreeRecursive(next, englishNode, arabicNode);
4352
+ next.caret = {
4353
+ chainId: englishNode.baseExpr ? englishNode.baseExpr.id : next.caret.chainId,
4354
+ index: 0
4355
+ };
4356
+ next.selection = null;
4357
+ next.selectedStructureNodeId = null;
4358
+ syncSelectionToCaret(next);
4359
+ appendDebug(next, "insertAccent", `${accentId}:empty`);
4360
+ return next;
4361
+ }
3717
4362
  function insertMatrixEnv(session, style, rows, columns) {
3718
4363
  return insertGridEnv(session, style, rows, columns);
3719
4364
  }
@@ -3961,7 +4606,7 @@ function deleteBackward(session) {
3961
4606
  if (!previousNode) {
3962
4607
  return session;
3963
4608
  }
3964
- if ((previousNode.kind === "delimiter" || previousNode.kind === "frac" || previousNode.kind === "sqrt" || previousNode.kind === "env") && next.selectedStructureNodeId !== previousNode.id) {
4609
+ 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) {
3965
4610
  next.selectedStructureNodeId = previousNode.id;
3966
4611
  next.selectedNodeId = previousNode.id;
3967
4612
  appendDebug(next, "deleteBackward", "selected structure");
@@ -4451,6 +5096,168 @@ function buildSqrtNodeBody(node, buildChain, side) {
4451
5096
  return wrap;
4452
5097
  }
4453
5098
 
5099
+ // src/editor/display/overset.ts
5100
+ var OVERSET_CSS = `
5101
+ .overset {
5102
+ display: inline-flex;
5103
+ flex-direction: column;
5104
+ align-items: center;
5105
+ justify-content: center;
5106
+ vertical-align: middle;
5107
+ margin: 0 2px;
5108
+ min-width: 1.2em;
5109
+ }
5110
+
5111
+ .overset-over {
5112
+ display: inline-flex;
5113
+ justify-content: center;
5114
+ align-items: center;
5115
+ min-height: 0.85em;
5116
+ padding: 0 2px;
5117
+ font-size: 0.72em;
5118
+ line-height: 1.1;
5119
+ }
5120
+
5121
+ .overset-base {
5122
+ display: inline-flex;
5123
+ justify-content: center;
5124
+ align-items: center;
5125
+ min-height: 1.05em;
5126
+ padding: 0 2px;
5127
+ }
5128
+ `.trim();
5129
+ function buildOversetNodeBody(node, buildChain) {
5130
+ const wrap = document.createElement("span");
5131
+ wrap.className = "overset";
5132
+ const over = document.createElement("span");
5133
+ over.className = "overset-over";
5134
+ if (node.overExpr) {
5135
+ over.appendChild(buildChain(node.overExpr));
5136
+ }
5137
+ const base = document.createElement("span");
5138
+ base.className = "overset-base";
5139
+ if (node.baseExpr) {
5140
+ base.appendChild(buildChain(node.baseExpr));
5141
+ }
5142
+ wrap.appendChild(over);
5143
+ wrap.appendChild(base);
5144
+ return wrap;
5145
+ }
5146
+
5147
+ // src/editor/display/underset.ts
5148
+ var UNDERSET_CSS = `
5149
+ .underset {
5150
+ display: inline-flex;
5151
+ flex-direction: column;
5152
+ align-items: center;
5153
+ justify-content: center;
5154
+ vertical-align: middle;
5155
+ margin: 0 2px;
5156
+ min-width: 1.2em;
5157
+ }
5158
+
5159
+ .underset-base {
5160
+ display: inline-flex;
5161
+ justify-content: center;
5162
+ align-items: center;
5163
+ min-height: 1.05em;
5164
+ padding: 0 2px;
5165
+ }
5166
+
5167
+ .underset-under {
5168
+ display: inline-flex;
5169
+ justify-content: center;
5170
+ align-items: center;
5171
+ min-height: 0.85em;
5172
+ padding: 0 2px;
5173
+ font-size: 0.72em;
5174
+ line-height: 1.1;
5175
+ }
5176
+ `.trim();
5177
+ function buildUndersetNodeBody(node, buildChain) {
5178
+ const wrap = document.createElement("span");
5179
+ wrap.className = "underset";
5180
+ const base = document.createElement("span");
5181
+ base.className = "underset-base";
5182
+ if (node.baseExpr) {
5183
+ base.appendChild(buildChain(node.baseExpr));
5184
+ }
5185
+ const under = document.createElement("span");
5186
+ under.className = "underset-under";
5187
+ if (node.underExpr) {
5188
+ under.appendChild(buildChain(node.underExpr));
5189
+ }
5190
+ wrap.appendChild(base);
5191
+ wrap.appendChild(under);
5192
+ return wrap;
5193
+ }
5194
+
5195
+ // src/editor/display/accent.ts
5196
+ var ACCENT_CSS = `
5197
+ .math-accent {
5198
+ display: inline-flex;
5199
+ flex-direction: column;
5200
+ align-items: center;
5201
+ justify-content: center;
5202
+ vertical-align: middle;
5203
+ margin: 0 2px;
5204
+ min-width: 1.1em;
5205
+ }
5206
+
5207
+ .math-accent-chrome {
5208
+ display: inline-flex;
5209
+ justify-content: center;
5210
+ align-items: center;
5211
+ min-height: 0.7em;
5212
+ padding: 0 2px;
5213
+ font-size: 0.85em;
5214
+ line-height: 1;
5215
+ color: var(--butex-script-color, #475569);
5216
+ user-select: none;
5217
+ }
5218
+
5219
+ .math-accent-base {
5220
+ display: inline-flex;
5221
+ justify-content: center;
5222
+ align-items: center;
5223
+ min-height: 1.05em;
5224
+ padding: 0 2px;
5225
+ }
5226
+
5227
+ .math-accent--under .math-accent-chrome {
5228
+ min-height: 0.45em;
5229
+ border-top: 1.5px solid currentColor;
5230
+ width: 100%;
5231
+ font-size: 0;
5232
+ color: var(--butex-surface-fg, #0f172a);
5233
+ }
5234
+ `.trim();
5235
+ function buildAccentNodeBody(node, buildChain) {
5236
+ const wrap = document.createElement("span");
5237
+ const spec = accentCommandSpec(node.expr);
5238
+ const placement = spec?.placement ?? "over";
5239
+ wrap.className = placement === "under" ? "math-accent math-accent--under" : "math-accent math-accent--over";
5240
+ const chrome = document.createElement("span");
5241
+ chrome.className = "math-accent-chrome";
5242
+ chrome.setAttribute("aria-hidden", "true");
5243
+ if (placement === "over") {
5244
+ chrome.textContent = spec?.label ?? "\u02C6";
5245
+ }
5246
+ const base = document.createElement("span");
5247
+ base.className = "math-accent-base";
5248
+ if (node.baseExpr) {
5249
+ base.appendChild(buildChain(node.baseExpr));
5250
+ }
5251
+ if (placement === "under") {
5252
+ wrap.appendChild(base);
5253
+ wrap.appendChild(chrome);
5254
+ } else {
5255
+ wrap.appendChild(chrome);
5256
+ wrap.appendChild(base);
5257
+ }
5258
+ return wrap;
5259
+ }
5260
+
4454
5261
  // src/editor/display/env.ts
4455
5262
  var WRAPPERS = {
4456
5263
  matrix: { left: "", right: "" },
@@ -4823,6 +5630,15 @@ function buildNodeBody(node, buildChain, side, digitForm = "western", uiLocale =
4823
5630
  if (node.kind === "sqrt") {
4824
5631
  return buildSqrtNodeBody(node, buildChain, side);
4825
5632
  }
5633
+ if (node.kind === "overset") {
5634
+ return buildOversetNodeBody(node, buildChain);
5635
+ }
5636
+ if (node.kind === "underset") {
5637
+ return buildUndersetNodeBody(node, buildChain);
5638
+ }
5639
+ if (node.kind === "accent") {
5640
+ return buildAccentNodeBody(node, buildChain);
5641
+ }
4826
5642
  if (node.kind === "env") {
4827
5643
  return buildMatrixEnvNodeBody(node, buildChain, side);
4828
5644
  }
@@ -5022,6 +5838,12 @@ ${FRAC_CSS}
5022
5838
 
5023
5839
  ${SQRT_CSS}
5024
5840
 
5841
+ ${OVERSET_CSS}
5842
+
5843
+ ${UNDERSET_CSS}
5844
+
5845
+ ${ACCENT_CSS}
5846
+
5025
5847
  ${MATRIX_ENV_CSS}
5026
5848
 
5027
5849
  ${ATOMIC_COMMAND_CSS}
@@ -5741,6 +6563,26 @@ function createEditorRuntime(options) {
5741
6563
  applyStructural((prev) => insertSqrt(prev));
5742
6564
  surfaceEl.focus();
5743
6565
  },
6566
+ insertOverset: () => {
6567
+ applyStructural((prev) => insertOverset(prev));
6568
+ surfaceEl.focus();
6569
+ },
6570
+ insertUnderset: () => {
6571
+ applyStructural((prev) => insertUnderset(prev));
6572
+ surfaceEl.focus();
6573
+ },
6574
+ insertLimitsSeriesTemplate: (commandId) => {
6575
+ applyStructural((prev) => insertLimitsSeriesTemplate(prev, commandId));
6576
+ surfaceEl.focus();
6577
+ },
6578
+ insertAccentPair: () => {
6579
+ applyStructural((prev) => insertAccentPair(prev));
6580
+ surfaceEl.focus();
6581
+ },
6582
+ insertAccent: (accentId) => {
6583
+ applyStructural((prev) => insertAccent(prev, accentId));
6584
+ surfaceEl.focus();
6585
+ },
5744
6586
  insertAtomicCommand: (commandId) => {
5745
6587
  applyStructural((prev) => insertAtomicCommand(prev, commandId));
5746
6588
  surfaceEl.focus();
@@ -6067,6 +6909,56 @@ function commandToEditorNode(node) {
6067
6909
  }
6068
6910
  return { editable: true, node: sqrt };
6069
6911
  }
6912
+ if (node.name === "\\overset" && node.mandatoryArgs.length >= 2) {
6913
+ const overset = createOversetNode();
6914
+ const overExpr = chainToEditorChain(node.mandatoryArgs[0]);
6915
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
6916
+ if (!overExpr.editable) {
6917
+ return overExpr;
6918
+ }
6919
+ if (!baseExpr.editable) {
6920
+ return baseExpr;
6921
+ }
6922
+ overset.overExpr = overExpr.chain;
6923
+ overset.baseExpr = baseExpr.chain;
6924
+ const scripts = attachScripts(node, overset);
6925
+ if (scripts) {
6926
+ return scripts;
6927
+ }
6928
+ return { editable: true, node: overset };
6929
+ }
6930
+ if (node.name === "\\underset" && node.mandatoryArgs.length >= 2) {
6931
+ const underset = createUndersetNode();
6932
+ const underExpr = chainToEditorChain(node.mandatoryArgs[0]);
6933
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
6934
+ if (!underExpr.editable) {
6935
+ return underExpr;
6936
+ }
6937
+ if (!baseExpr.editable) {
6938
+ return baseExpr;
6939
+ }
6940
+ underset.underExpr = underExpr.chain;
6941
+ underset.baseExpr = baseExpr.chain;
6942
+ const scripts = attachScripts(node, underset);
6943
+ if (scripts) {
6944
+ return scripts;
6945
+ }
6946
+ return { editable: true, node: underset };
6947
+ }
6948
+ const accentId = accentCommandIdFromTex(node.name);
6949
+ if (accentId && node.mandatoryArgs.length >= 1) {
6950
+ const accent = createAccentNode(accentId);
6951
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[0]);
6952
+ if (!baseExpr.editable) {
6953
+ return baseExpr;
6954
+ }
6955
+ accent.baseExpr = baseExpr.chain;
6956
+ const scripts = attachScripts(node, accent);
6957
+ if (scripts) {
6958
+ return scripts;
6959
+ }
6960
+ return { editable: true, node: accent };
6961
+ }
6070
6962
  const atomicId = commandIdForTex(node.name);
6071
6963
  if (atomicId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
6072
6964
  const atomic = createEditorNode("atomicCommand", atomicId);
@@ -6203,6 +7095,21 @@ function editorEnvToAstNode(node) {
6203
7095
  const scripts = attachAstScripts(node, env);
6204
7096
  return scripts ?? { ok: true, node: env };
6205
7097
  }
7098
+ function accentChainForExport(accent) {
7099
+ if (!accent) {
7100
+ return { ok: true, chain: new ChainNode([]) };
7101
+ }
7102
+ return editorChainToAstChain(accent);
7103
+ }
7104
+ function nodeFromAccentBase(command, accent, base) {
7105
+ if (accent.chain.length > 0) {
7106
+ return new CommandNode(command, [], [accent, base]);
7107
+ }
7108
+ if (base.chain.length === 1) {
7109
+ return base.chain[0];
7110
+ }
7111
+ return new DelimiterNode("", base, "");
7112
+ }
6206
7113
  function editorNodeToAstNode(node) {
6207
7114
  let astNode;
6208
7115
  if (node.kind === "char") {
@@ -6241,6 +7148,36 @@ function editorNodeToAstNode(node) {
6241
7148
  optionalArgs.push(rootIndex.chain);
6242
7149
  }
6243
7150
  astNode = new CommandNode("\\sqrt", optionalArgs, [radicand.chain]);
7151
+ } else if (node.kind === "overset" && node.baseExpr) {
7152
+ const accent = accentChainForExport(node.overExpr);
7153
+ if (!accent.ok) {
7154
+ return accent;
7155
+ }
7156
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7157
+ if (!baseExpr.ok) {
7158
+ return baseExpr;
7159
+ }
7160
+ astNode = nodeFromAccentBase("\\overset", accent.chain, baseExpr.chain);
7161
+ } else if (node.kind === "underset" && node.baseExpr) {
7162
+ const accent = accentChainForExport(node.underExpr);
7163
+ if (!accent.ok) {
7164
+ return accent;
7165
+ }
7166
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7167
+ if (!baseExpr.ok) {
7168
+ return baseExpr;
7169
+ }
7170
+ astNode = nodeFromAccentBase("\\underset", accent.chain, baseExpr.chain);
7171
+ } else if (node.kind === "accent" && node.baseExpr) {
7172
+ const tex = accentCommandSpec(node.expr)?.englishTex;
7173
+ if (!tex) {
7174
+ return { ok: false, reason: `\u0644\u0643\u0646\u0629 \u0631\u064A\u0627\u0636\u064A\u0629 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645\u0629 \u062D\u0627\u0644\u064A\u0627: ${node.expr}` };
7175
+ }
7176
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7177
+ if (!baseExpr.ok) {
7178
+ return baseExpr;
7179
+ }
7180
+ astNode = new CommandNode(tex, [], [baseExpr.chain]);
6244
7181
  } else if (node.kind === "atomicCommand") {
6245
7182
  const tex = atomicCommandSpec(node.expr)?.englishTex;
6246
7183
  if (!tex) {
@@ -7370,6 +8307,29 @@ var WIDGET_CHROME_CSS = `
7370
8307
  padding-top: 1px;
7371
8308
  }
7372
8309
 
8310
+ .butex-widget .toolbar-accent-icon {
8311
+ display: inline-flex;
8312
+ flex-direction: column;
8313
+ align-items: center;
8314
+ justify-content: center;
8315
+ min-width: 14px;
8316
+ font-size: 10px;
8317
+ line-height: 1;
8318
+ gap: 1px;
8319
+ }
8320
+
8321
+ .butex-widget .toolbar-accent-icon--over span:first-child,
8322
+ .butex-widget .toolbar-accent-icon--under span:last-child {
8323
+ font-size: 8px;
8324
+ opacity: 0.85;
8325
+ }
8326
+
8327
+ .butex-widget .toolbar-accent-icon--pair span:first-child,
8328
+ .butex-widget .toolbar-accent-icon--pair span:last-child {
8329
+ font-size: 8px;
8330
+ opacity: 0.85;
8331
+ }
8332
+
7373
8333
  .butex-widget .matrix-command-menu {
7374
8334
  position: relative;
7375
8335
  display: inline-flex;
@@ -8111,6 +9071,7 @@ var CHARACTER_FONT_OPTIONS = [
8111
9071
  { id: "diwani", label: "\u062F\u064A\u0648" },
8112
9072
  { id: "diwaniOutline", label: "\u0638\u0644" }
8113
9073
  ];
9074
+ var ACCENT_COMMANDS_LIST = accentCommandsList();
8114
9075
  var FUNCTION_COMMANDS = atomicCommandsByCategory("function");
8115
9076
  var LIMITS_SERIES_COMMANDS = atomicCommandsByCategory("limits_series");
8116
9077
  var FUNCTION2_COMMANDS = atomicCommandsByCategory("function2");
@@ -8258,6 +9219,7 @@ var ButexEditor = forwardRef(
8258
9219
  const [operator2MenuOpen, setOperator2MenuOpen] = useState(false);
8259
9220
  const [operator3MenuOpen, setOperator3MenuOpen] = useState(false);
8260
9221
  const [dotMenuOpen, setDotMenuOpen] = useState(false);
9222
+ const [accentMenuOpen, setAccentMenuOpen] = useState(false);
8261
9223
  const [integralMenuOpen, setIntegralMenuOpen] = useState(false);
8262
9224
  const [selectedDigitForm2, setSelectedDigitForm] = useState("western");
8263
9225
  const [selectedCharacterFont, setSelectedCharacterFont] = useState("default");
@@ -8527,7 +9489,78 @@ ${arabic || currentMessages.empty}`;
8527
9489
  /* @__PURE__ */ jsx("button", { id: "insert-sqrt", className: "icon-btn", type: "button", title: messages.insertRoot, "aria-label": messages.insertRoot, onClick: () => {
8528
9490
  runtimeRef.current?.insertSqrt();
8529
9491
  focusSurface();
8530
- }, children: /* @__PURE__ */ jsx("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) })
9492
+ }, children: /* @__PURE__ */ jsx("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) }),
9493
+ /* @__PURE__ */ jsx("button", { id: "insert-overset", className: "icon-btn", type: "button", title: messages.insertOverset, "aria-label": messages.insertOverset, onClick: () => {
9494
+ runtimeRef.current?.insertOverset();
9495
+ focusSurface();
9496
+ }, children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
9497
+ /* @__PURE__ */ jsx("span", { children: "\u2217" }),
9498
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" })
9499
+ ] }) }),
9500
+ /* @__PURE__ */ jsx("button", { id: "insert-underset", className: "icon-btn", type: "button", title: messages.insertUnderset, "aria-label": messages.insertUnderset, onClick: () => {
9501
+ runtimeRef.current?.insertUnderset();
9502
+ focusSurface();
9503
+ }, children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--under", "aria-hidden": "true", children: [
9504
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" }),
9505
+ /* @__PURE__ */ jsx("span", { children: "\u2217" })
9506
+ ] }) }),
9507
+ /* @__PURE__ */ jsx("button", { id: "insert-accent-pair", className: "icon-btn", type: "button", title: messages.insertAccentPair, "aria-label": messages.insertAccentPair, onClick: () => {
9508
+ runtimeRef.current?.insertAccentPair();
9509
+ focusSurface();
9510
+ }, children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--pair", "aria-hidden": "true", children: [
9511
+ /* @__PURE__ */ jsx("span", { children: "\u2217" }),
9512
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" }),
9513
+ /* @__PURE__ */ jsx("span", { children: "\u2217" })
9514
+ ] }) }),
9515
+ /* @__PURE__ */ jsxs(
9516
+ "div",
9517
+ {
9518
+ className: "function-command-menu operator-command-menu",
9519
+ onBlur: (event) => {
9520
+ const nextFocus = event.relatedTarget;
9521
+ if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) {
9522
+ setAccentMenuOpen(false);
9523
+ }
9524
+ },
9525
+ children: [
9526
+ /* @__PURE__ */ jsx(
9527
+ "button",
9528
+ {
9529
+ id: "insert-accents",
9530
+ className: "icon-btn function-command-btn operator-command-btn",
9531
+ type: "button",
9532
+ title: messages.insertAccents,
9533
+ "aria-label": messages.insertAccents,
9534
+ "aria-haspopup": "listbox",
9535
+ "aria-expanded": accentMenuOpen,
9536
+ onClick: () => setAccentMenuOpen((open) => !open),
9537
+ children: /* @__PURE__ */ jsxs("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
9538
+ /* @__PURE__ */ jsx("span", { children: "\u02C6" }),
9539
+ /* @__PURE__ */ jsx("span", { children: "\u25A1" })
9540
+ ] })
9541
+ }
9542
+ ),
9543
+ /* @__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(
9544
+ "button",
9545
+ {
9546
+ className: "function-command-option operator-command-option",
9547
+ type: "button",
9548
+ role: "option",
9549
+ title: accentCommandTitle(command, uiLocale),
9550
+ "aria-label": accentCommandTitle(command, uiLocale),
9551
+ onMouseDown: (event) => event.preventDefault(),
9552
+ onClick: () => {
9553
+ runtimeRef.current?.insertAccent(command.id);
9554
+ setAccentMenuOpen(false);
9555
+ focusSurface();
9556
+ },
9557
+ children: /* @__PURE__ */ jsx("span", { className: "operator-command-label", children: command.label })
9558
+ },
9559
+ command.id
9560
+ )) })
9561
+ ]
9562
+ }
9563
+ )
8531
9564
  ] }),
8532
9565
  /* @__PURE__ */ jsx("div", { className: "toolbar-group toolbar-environments", role: "group", "aria-label": messages.environments, children: /* @__PURE__ */ jsxs(
8533
9566
  "div",
@@ -8855,7 +9888,7 @@ ${arabic || currentMessages.empty}`;
8855
9888
  "aria-label": atomicCommandTitle(command, uiLocale),
8856
9889
  onMouseDown: (event) => event.preventDefault(),
8857
9890
  onClick: () => {
8858
- runtimeRef.current?.insertAtomicCommand(command.id);
9891
+ runtimeRef.current?.insertLimitsSeriesTemplate(command.id);
8859
9892
  setLimitsSeriesMenuOpen(false);
8860
9893
  focusSurface();
8861
9894
  },