@drghaliasri/butex 5.3.3 → 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.
@@ -694,6 +694,158 @@ function createEmptyDocument() {
694
694
  };
695
695
  }
696
696
 
697
+ // src/editor/accentCommands.ts
698
+ var ACCENT_COMMANDS = {
699
+ vec: {
700
+ id: "vec",
701
+ englishTex: "\\vec",
702
+ arabicTex: "\\arabvec",
703
+ placement: "over",
704
+ label: "\u20D7",
705
+ title: "\u0633\u0647\u0645 \u0641\u0648\u0642"
706
+ },
707
+ hat: {
708
+ id: "hat",
709
+ englishTex: "\\hat",
710
+ arabicTex: "\\hat",
711
+ placement: "over",
712
+ label: "\u02C6",
713
+ title: "\u0642\u0628\u0639\u0629"
714
+ },
715
+ tilde: {
716
+ id: "tilde",
717
+ englishTex: "\\tilde",
718
+ arabicTex: "\\tilde",
719
+ placement: "over",
720
+ label: "\u02DC",
721
+ title: "\u0645\u062F\u0629"
722
+ },
723
+ dot: {
724
+ id: "dot",
725
+ englishTex: "\\dot",
726
+ arabicTex: "\\dot",
727
+ placement: "over",
728
+ label: "\u02D9",
729
+ title: "\u0646\u0642\u0637\u0629 \u0641\u0648\u0642"
730
+ },
731
+ ddot: {
732
+ id: "ddot",
733
+ englishTex: "\\ddot",
734
+ arabicTex: "\\ddot",
735
+ placement: "over",
736
+ label: "\xA8",
737
+ title: "\u0646\u0642\u0637\u062A\u0627\u0646 \u0641\u0648\u0642"
738
+ },
739
+ dddot: {
740
+ id: "dddot",
741
+ englishTex: "\\dddot",
742
+ arabicTex: "\\dddot",
743
+ placement: "over",
744
+ label: "\u20DB",
745
+ title: "\u062B\u0644\u0627\u062B \u0646\u0642\u0627\u0637 \u0641\u0648\u0642"
746
+ },
747
+ bar: {
748
+ id: "bar",
749
+ englishTex: "\\bar",
750
+ arabicTex: "\\bar",
751
+ placement: "over",
752
+ label: "\xAF",
753
+ title: "\u062E\u0637 \u0641\u0648\u0642"
754
+ },
755
+ check: {
756
+ id: "check",
757
+ englishTex: "\\check",
758
+ arabicTex: "\\check",
759
+ placement: "over",
760
+ label: "\u02C7",
761
+ title: "\u0639\u0644\u0627\u0645\u0629 \u062A\u062D\u0642\u0642"
762
+ },
763
+ breve: {
764
+ id: "breve",
765
+ englishTex: "\\breve",
766
+ arabicTex: "\\breve",
767
+ placement: "over",
768
+ label: "\u02D8",
769
+ title: "\u0642\u0648\u0633 \u0642\u0635\u064A\u0631"
770
+ },
771
+ acute: {
772
+ id: "acute",
773
+ englishTex: "\\acute",
774
+ arabicTex: "\\grave",
775
+ placement: "over",
776
+ label: "\xB4",
777
+ title: "\u0646\u0628\u0631\u0629 \u062D\u0627\u062F\u0629"
778
+ },
779
+ grave: {
780
+ id: "grave",
781
+ englishTex: "\\grave",
782
+ arabicTex: "\\acute",
783
+ placement: "over",
784
+ label: "`",
785
+ title: "\u0646\u0628\u0631\u0629 \u062B\u0642\u064A\u0644\u0629"
786
+ },
787
+ overline: {
788
+ id: "overline",
789
+ englishTex: "\\overline",
790
+ arabicTex: "\\overline",
791
+ placement: "over",
792
+ label: "\u203E",
793
+ title: "\u062E\u0637 \u0639\u0644\u0648\u064A \u0645\u0645\u062A\u062F"
794
+ },
795
+ underline: {
796
+ id: "underline",
797
+ englishTex: "\\underline",
798
+ arabicTex: "\\underline",
799
+ placement: "under",
800
+ label: "_",
801
+ title: "\u062E\u0637 \u0633\u0641\u0644\u064A"
802
+ },
803
+ overleftarrow: {
804
+ id: "overleftarrow",
805
+ englishTex: "\\overleftarrow",
806
+ arabicTex: "\\overrightarrow",
807
+ placement: "over",
808
+ label: "\u2190",
809
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631"
810
+ },
811
+ overrightarrow: {
812
+ id: "overrightarrow",
813
+ englishTex: "\\overrightarrow",
814
+ arabicTex: "\\overleftarrow",
815
+ placement: "over",
816
+ label: "\u2192",
817
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646"
818
+ },
819
+ overleftrightarrow: {
820
+ id: "overleftrightarrow",
821
+ englishTex: "\\overleftrightarrow",
822
+ arabicTex: "\\overleftrightarrow",
823
+ placement: "over",
824
+ label: "\u2194",
825
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0645\u0632\u062F\u0648\u062C \u0627\u0644\u0627\u062A\u062C\u0627\u0647"
826
+ }
827
+ };
828
+ function accentCommandSpec(id) {
829
+ return Object.prototype.hasOwnProperty.call(ACCENT_COMMANDS, id) ? ACCENT_COMMANDS[id] : null;
830
+ }
831
+ function accentCommandsList() {
832
+ return Object.values(ACCENT_COMMANDS);
833
+ }
834
+ function accentCommandIdFromTex(tex) {
835
+ const normalized = tex.startsWith("\\") ? tex : `\\${tex}`;
836
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
837
+ if (spec.englishTex === normalized) {
838
+ return spec.id;
839
+ }
840
+ }
841
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
842
+ if (spec.arabicTex === normalized) {
843
+ return spec.id;
844
+ }
845
+ }
846
+ return null;
847
+ }
848
+
697
849
  // src/editor/atomicCommands.ts
698
850
  var ATOMIC_COMMANDS = {
699
851
  ///////////////////////// Functions ////////////////////////////////
@@ -1668,6 +1820,9 @@ function createEditorNode(kind, expr) {
1668
1820
  denominator: null,
1669
1821
  radicand: null,
1670
1822
  rootIndex: null,
1823
+ overExpr: null,
1824
+ underExpr: null,
1825
+ baseExpr: null,
1671
1826
  envName: null,
1672
1827
  matrixStyle: null,
1673
1828
  matrixRows: null,
@@ -1696,6 +1851,26 @@ function createSqrtNode() {
1696
1851
  rootIndex: createEditorChain()
1697
1852
  };
1698
1853
  }
1854
+ function createOversetNode() {
1855
+ return {
1856
+ ...createEditorNode("overset", ""),
1857
+ overExpr: createEditorChain(),
1858
+ baseExpr: createEditorChain()
1859
+ };
1860
+ }
1861
+ function createUndersetNode() {
1862
+ return {
1863
+ ...createEditorNode("underset", ""),
1864
+ underExpr: createEditorChain(),
1865
+ baseExpr: createEditorChain()
1866
+ };
1867
+ }
1868
+ function createAccentNode(accentId) {
1869
+ return {
1870
+ ...createEditorNode("accent", accentId),
1871
+ baseExpr: createEditorChain()
1872
+ };
1873
+ }
1699
1874
  function createGridEnvNode(envName, rows, columns, columnAlignments) {
1700
1875
  const safeRows = Math.max(1, Math.min(12, Math.trunc(rows)));
1701
1876
  const safeColumns = Math.max(1, Math.min(12, Math.trunc(columns)));
@@ -1760,6 +1935,15 @@ function collectNodeIds(chain, ids) {
1760
1935
  if (node.radicand) {
1761
1936
  collectNodeIds(node.radicand, ids);
1762
1937
  }
1938
+ if (node.overExpr) {
1939
+ collectNodeIds(node.overExpr, ids);
1940
+ }
1941
+ if (node.underExpr) {
1942
+ collectNodeIds(node.underExpr, ids);
1943
+ }
1944
+ if (node.baseExpr) {
1945
+ collectNodeIds(node.baseExpr, ids);
1946
+ }
1763
1947
  if (node.matrixRows) {
1764
1948
  for (const row of node.matrixRows) {
1765
1949
  for (const cell of row) {
@@ -1924,6 +2108,22 @@ function renderMatrixRowsArabic(node, options) {
1924
2108
  }
1925
2109
  return `\\begin{${envName}}${rows.join(" \\\\\n")}\\end{${envName}}`;
1926
2110
  }
2111
+ function renderAccentEnglish(command, accent, base, options) {
2112
+ const baseTex = base ? renderChainEnglish(base, options) : "";
2113
+ const accentTex = accent ? renderChainEnglish(accent, options) : "";
2114
+ if (accentTex === "") {
2115
+ return baseTex;
2116
+ }
2117
+ return `\\${command}{${accentTex}}{${baseTex}}`;
2118
+ }
2119
+ function renderAccentArabic(command, accent, base, options) {
2120
+ const baseTex = base ? renderChainArabic(base, options) : "";
2121
+ const accentTex = accent ? renderChainArabic(accent, options) : "";
2122
+ if (accentTex === "") {
2123
+ return baseTex;
2124
+ }
2125
+ return `\\${command}{${accentTex}}{${baseTex}}`;
2126
+ }
1927
2127
  function escapeText(s) {
1928
2128
  return s.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
1929
2129
  }
@@ -1953,6 +2153,19 @@ function renderNodeEnglish(node, options) {
1953
2153
  const body = `\\sqrt${optionalRootIndexEnglish(node, options)}{${renderChainEnglish(node.radicand, options)}}`;
1954
2154
  return `${body}${latinScripts(node, options)}`;
1955
2155
  }
2156
+ if (node.kind === "overset" && node.baseExpr) {
2157
+ const body = renderAccentEnglish("overset", node.overExpr, node.baseExpr, options);
2158
+ return `${body}${latinScripts(node, options)}`;
2159
+ }
2160
+ if (node.kind === "underset" && node.baseExpr) {
2161
+ const body = renderAccentEnglish("underset", node.underExpr, node.baseExpr, options);
2162
+ return `${body}${latinScripts(node, options)}`;
2163
+ }
2164
+ if (node.kind === "accent" && node.baseExpr) {
2165
+ const cmd = accentCommandSpec(node.expr)?.englishTex ?? `\\${node.expr}`;
2166
+ const body = `${cmd}{${renderChainEnglish(node.baseExpr, options)}}`;
2167
+ return `${body}${latinScripts(node, options)}`;
2168
+ }
1956
2169
  if (node.kind === "env") {
1957
2170
  return `${renderMatrixRowsEnglish(node, options)}${latinScripts(node, options)}`;
1958
2171
  }
@@ -1985,6 +2198,19 @@ function renderNodeArabic(node, options) {
1985
2198
  const content = `\\arabsqrt${optionalRootIndexArabic(node, options)}{${renderChainArabic(node.radicand, options)}}`;
1986
2199
  return arabicScripts(node, content, options);
1987
2200
  }
2201
+ if (node.kind === "overset" && node.baseExpr) {
2202
+ const content = renderAccentArabic("overset", node.overExpr, node.baseExpr, options);
2203
+ return arabicScripts(node, content, options);
2204
+ }
2205
+ if (node.kind === "underset" && node.baseExpr) {
2206
+ const content = renderAccentArabic("underset", node.underExpr, node.baseExpr, options);
2207
+ return arabicScripts(node, content, options);
2208
+ }
2209
+ if (node.kind === "accent" && node.baseExpr) {
2210
+ const cmd = accentCommandSpec(node.expr)?.arabicTex ?? `\\${node.expr}`;
2211
+ const content = `${cmd}{${renderChainArabic(node.baseExpr, options)}}`;
2212
+ return arabicScripts(node, content, options);
2213
+ }
1988
2214
  if (node.kind === "env") {
1989
2215
  return arabicScripts(node, renderMatrixRowsArabic(node, options), options);
1990
2216
  }
@@ -2041,6 +2267,35 @@ function collectCaretPositions(chain) {
2041
2267
  if (node.radicand) {
2042
2268
  walkChain(node.radicand);
2043
2269
  }
2270
+ if (node.kind === "overset") {
2271
+ if (node.overExpr) {
2272
+ walkChain(node.overExpr);
2273
+ }
2274
+ if (node.baseExpr) {
2275
+ walkChain(node.baseExpr);
2276
+ }
2277
+ } else if (node.kind === "underset") {
2278
+ if (node.baseExpr) {
2279
+ walkChain(node.baseExpr);
2280
+ }
2281
+ if (node.underExpr) {
2282
+ walkChain(node.underExpr);
2283
+ }
2284
+ } else if (node.kind === "accent") {
2285
+ if (node.baseExpr) {
2286
+ walkChain(node.baseExpr);
2287
+ }
2288
+ } else {
2289
+ if (node.overExpr) {
2290
+ walkChain(node.overExpr);
2291
+ }
2292
+ if (node.underExpr) {
2293
+ walkChain(node.underExpr);
2294
+ }
2295
+ if (node.baseExpr) {
2296
+ walkChain(node.baseExpr);
2297
+ }
2298
+ }
2044
2299
  if (node.matrixRows) {
2045
2300
  for (const row of node.matrixRows) {
2046
2301
  for (const cell of row) {
@@ -2174,6 +2429,42 @@ function findChainById(root, chainId) {
2174
2429
  return nested;
2175
2430
  }
2176
2431
  }
2432
+ if (node.overExpr) {
2433
+ if (node.overExpr.id === chainId) {
2434
+ return {
2435
+ chain: node.overExpr,
2436
+ relation: { kind: "overExpr", ownerNodeId: node.id, ownerChainId: root.id }
2437
+ };
2438
+ }
2439
+ const nested = findChainById(node.overExpr, chainId);
2440
+ if (nested) {
2441
+ return nested;
2442
+ }
2443
+ }
2444
+ if (node.underExpr) {
2445
+ if (node.underExpr.id === chainId) {
2446
+ return {
2447
+ chain: node.underExpr,
2448
+ relation: { kind: "underExpr", ownerNodeId: node.id, ownerChainId: root.id }
2449
+ };
2450
+ }
2451
+ const nested = findChainById(node.underExpr, chainId);
2452
+ if (nested) {
2453
+ return nested;
2454
+ }
2455
+ }
2456
+ if (node.baseExpr) {
2457
+ if (node.baseExpr.id === chainId) {
2458
+ return {
2459
+ chain: node.baseExpr,
2460
+ relation: { kind: "baseExpr", ownerNodeId: node.id, ownerChainId: root.id }
2461
+ };
2462
+ }
2463
+ const nested = findChainById(node.baseExpr, chainId);
2464
+ if (nested) {
2465
+ return nested;
2466
+ }
2467
+ }
2177
2468
  if (node.matrixRows) {
2178
2469
  for (let rowIndex = 0; rowIndex < node.matrixRows.length; rowIndex += 1) {
2179
2470
  const row = node.matrixRows[rowIndex];
@@ -2247,6 +2538,24 @@ function findNodeById(root, nodeId) {
2247
2538
  return nested;
2248
2539
  }
2249
2540
  }
2541
+ if (node.overExpr) {
2542
+ const nested = findNodeById(node.overExpr, nodeId);
2543
+ if (nested) {
2544
+ return nested;
2545
+ }
2546
+ }
2547
+ if (node.underExpr) {
2548
+ const nested = findNodeById(node.underExpr, nodeId);
2549
+ if (nested) {
2550
+ return nested;
2551
+ }
2552
+ }
2553
+ if (node.baseExpr) {
2554
+ const nested = findNodeById(node.baseExpr, nodeId);
2555
+ if (nested) {
2556
+ return nested;
2557
+ }
2558
+ }
2250
2559
  if (node.matrixRows) {
2251
2560
  for (const row of node.matrixRows) {
2252
2561
  for (const cell of row) {
@@ -2282,6 +2591,15 @@ function nodeChildChains(node) {
2282
2591
  if (node.radicand) {
2283
2592
  chains.push(node.radicand);
2284
2593
  }
2594
+ if (node.overExpr) {
2595
+ chains.push(node.overExpr);
2596
+ }
2597
+ if (node.underExpr) {
2598
+ chains.push(node.underExpr);
2599
+ }
2600
+ if (node.baseExpr) {
2601
+ chains.push(node.baseExpr);
2602
+ }
2285
2603
  if (node.matrixRows) {
2286
2604
  for (const row of node.matrixRows) {
2287
2605
  chains.push(...row);
@@ -2652,6 +2970,91 @@ function derivativeTemplateInitialCaret(englishFrac) {
2652
2970
  };
2653
2971
  }
2654
2972
 
2973
+ // src/editor/limitsTemplates.ts
2974
+ var LIMITS_SERIES_TEMPLATE_IDS = /* @__PURE__ */ new Set([
2975
+ "sum",
2976
+ "prod",
2977
+ "lim",
2978
+ "max",
2979
+ "min",
2980
+ "sup",
2981
+ "inf"
2982
+ ]);
2983
+ function isLimitsSeriesTemplateId(commandId) {
2984
+ return LIMITS_SERIES_TEMPLATE_IDS.has(commandId);
2985
+ }
2986
+ function syncChainIds2(english, arabic) {
2987
+ arabic.id = english.id;
2988
+ const limit = Math.min(english.nodes.length, arabic.nodes.length);
2989
+ for (let index = 0; index < limit; index += 1) {
2990
+ arabic.nodes[index].id = english.nodes[index].id;
2991
+ }
2992
+ }
2993
+ function syncOversetIds(english, arabic) {
2994
+ arabic.id = english.id;
2995
+ if (english.overExpr && arabic.overExpr) {
2996
+ syncChainIds2(english.overExpr, arabic.overExpr);
2997
+ }
2998
+ if (english.baseExpr && arabic.baseExpr) {
2999
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
3000
+ }
3001
+ }
3002
+ function syncUndersetIds(english, arabic) {
3003
+ arabic.id = english.id;
3004
+ if (english.underExpr && arabic.underExpr) {
3005
+ syncChainIds2(english.underExpr, arabic.underExpr);
3006
+ }
3007
+ if (english.baseExpr && arabic.baseExpr) {
3008
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
3009
+ }
3010
+ }
3011
+ function buildLimitsSeriesTemplate(commandId) {
3012
+ if (!isLimitsSeriesTemplateId(commandId)) {
3013
+ throw new Error(`BuTeX: unsupported limits-series template id: ${commandId}`);
3014
+ }
3015
+ const englishOp = createAtomicCommandNode(commandId);
3016
+ const arabicOp = createAtomicCommandNode(commandId);
3017
+ arabicOp.id = englishOp.id;
3018
+ const englishOverset = createOversetNode();
3019
+ const arabicOverset = createOversetNode();
3020
+ syncOversetIds(englishOverset, arabicOverset);
3021
+ englishOverset.baseExpr = createEditorChain([englishOp]);
3022
+ arabicOverset.baseExpr = createEditorChain([arabicOp]);
3023
+ syncChainIds2(englishOverset.baseExpr, arabicOverset.baseExpr);
3024
+ const englishUnderset = createUndersetNode();
3025
+ const arabicUnderset = createUndersetNode();
3026
+ syncUndersetIds(englishUnderset, arabicUnderset);
3027
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
3028
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
3029
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
3030
+ return { english: englishUnderset, arabic: arabicUnderset };
3031
+ }
3032
+ function buildAccentPairTemplate() {
3033
+ const englishOverset = createOversetNode();
3034
+ const arabicOverset = createOversetNode();
3035
+ syncOversetIds(englishOverset, arabicOverset);
3036
+ const englishUnderset = createUndersetNode();
3037
+ const arabicUnderset = createUndersetNode();
3038
+ syncUndersetIds(englishUnderset, arabicUnderset);
3039
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
3040
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
3041
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
3042
+ return { english: englishUnderset, arabic: arabicUnderset };
3043
+ }
3044
+ function accentPairTemplateInitialCaret(englishUnderset) {
3045
+ const overset = englishUnderset.baseExpr?.nodes[0];
3046
+ return {
3047
+ chainId: overset?.baseExpr?.id ?? englishUnderset.underExpr?.id ?? "",
3048
+ index: 0
3049
+ };
3050
+ }
3051
+ function limitsSeriesTemplateInitialCaret(englishUnderset) {
3052
+ return {
3053
+ chainId: englishUnderset.underExpr?.id ?? englishUnderset.baseExpr?.id ?? "",
3054
+ index: 0
3055
+ };
3056
+ }
3057
+
2655
3058
  // src/editor/uiLocale.ts
2656
3059
  var ENGLISH_COMMAND_TITLES = {
2657
3060
  cos: "Cosine",
@@ -2695,6 +3098,24 @@ var ENGLISH_COMMAND_TITLES = {
2695
3098
  quadSpace: "Large space",
2696
3099
  qquadSpace: "Extra-large space"
2697
3100
  };
3101
+ var ENGLISH_ACCENT_TITLES = {
3102
+ vec: "Vector arrow",
3103
+ hat: "Hat",
3104
+ tilde: "Tilde",
3105
+ dot: "Dot",
3106
+ ddot: "Double dot",
3107
+ dddot: "Triple dot",
3108
+ bar: "Bar",
3109
+ check: "Check",
3110
+ breve: "Breve",
3111
+ acute: "Acute",
3112
+ grave: "Grave",
3113
+ overline: "Overline",
3114
+ underline: "Underline",
3115
+ overleftarrow: "Over left arrow",
3116
+ overrightarrow: "Over right arrow",
3117
+ overleftrightarrow: "Over left-right arrow"
3118
+ };
2698
3119
  var ENGLISH_OPERATOR_TITLES = {
2699
3120
  inf: "Infinity",
2700
3121
  times: "Multiplication",
@@ -2768,6 +3189,10 @@ var EQUATION_EDITOR_MESSAGES = {
2768
3189
  insertBraces: "\u0625\u062F\u0631\u0627\u062C \u062D\u0627\u0635\u0631\u062A\u064A\u0646",
2769
3190
  insertFraction: "\u0625\u062F\u0631\u0627\u062C \u0643\u0633\u0631",
2770
3191
  insertRoot: "\u0625\u062F\u0631\u0627\u062C \u062C\u0630\u0631",
3192
+ insertOverset: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642",
3193
+ insertUnderset: "\u0625\u062F\u0631\u0627\u062C \u062A\u062D\u062A",
3194
+ insertAccentPair: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642 \u0648\u062A\u062D\u062A",
3195
+ insertAccents: "\u0625\u062F\u0631\u0627\u062C \u0644\u0643\u0646\u0629",
2771
3196
  environments: "\u0627\u0644\u0628\u064A\u0626\u0627\u062A",
2772
3197
  insertEnvironment: "\u0625\u062F\u0631\u0627\u062C \u0628\u064A\u0626\u0629 \u0634\u0628\u0643\u064A\u0629",
2773
3198
  environmentType: "\u0646\u0648\u0639 \u0627\u0644\u0628\u064A\u0626\u0629",
@@ -2875,6 +3300,10 @@ var EQUATION_EDITOR_MESSAGES = {
2875
3300
  insertBraces: "Insert braces",
2876
3301
  insertFraction: "Insert fraction",
2877
3302
  insertRoot: "Insert root",
3303
+ insertOverset: "Insert overset",
3304
+ insertUnderset: "Insert underset",
3305
+ insertAccentPair: "Insert overset and underset",
3306
+ insertAccents: "Insert accent",
2878
3307
  environments: "Environments",
2879
3308
  insertEnvironment: "Insert grid environment",
2880
3309
  environmentType: "Environment type",
@@ -2975,6 +3404,9 @@ function atomicCommandTitle(spec, locale) {
2975
3404
  function atomicOperatorCommandTitle(spec, locale) {
2976
3405
  return locale === "en" ? ENGLISH_OPERATOR_TITLES[spec.id] ?? spec.title : spec.title;
2977
3406
  }
3407
+ function accentCommandTitle(spec, locale) {
3408
+ return locale === "en" ? ENGLISH_ACCENT_TITLES[spec.id] ?? spec.title : spec.title;
3409
+ }
2978
3410
 
2979
3411
  // src/editor/state.ts
2980
3412
  function ensureSyncEntry(session, nodeId) {
@@ -3009,7 +3441,7 @@ function syncSelectionToCaret(session) {
3009
3441
  session.selectedNodeId = located.relation.ownerNodeId;
3010
3442
  return;
3011
3443
  }
3012
- 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") {
3444
+ 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") {
3013
3445
  session.selectedNodeId = located.relation.ownerNodeId;
3014
3446
  return;
3015
3447
  }
@@ -3366,6 +3798,106 @@ function moveCaretVertical(session, direction) {
3366
3798
  appendDebug(next, "moveCaretVertical", "sqrt-radicand-down-exit");
3367
3799
  return next;
3368
3800
  }
3801
+ if (located.relation.kind === "overExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3802
+ if (direction === "down") {
3803
+ next.caret = {
3804
+ chainId: ownerNode.baseExpr.id,
3805
+ index: ownerNode.baseExpr.nodes.length
3806
+ };
3807
+ next.selectedStructureNodeId = null;
3808
+ syncSelectionToCaret(next);
3809
+ appendDebug(next, "moveCaretVertical", "overset-over->base");
3810
+ return next;
3811
+ }
3812
+ next.caret = {
3813
+ chainId: ownerLocated.chain.id,
3814
+ index: ownerLocated.index
3815
+ };
3816
+ next.selectedStructureNodeId = null;
3817
+ syncSelectionToCaret(next);
3818
+ appendDebug(next, "moveCaretVertical", "overset-over-up-exit");
3819
+ return next;
3820
+ }
3821
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "overset" && ownerNode.overExpr) {
3822
+ if (direction === "up") {
3823
+ next.caret = {
3824
+ chainId: ownerNode.overExpr.id,
3825
+ index: ownerNode.overExpr.nodes.length
3826
+ };
3827
+ next.selectedStructureNodeId = null;
3828
+ syncSelectionToCaret(next);
3829
+ appendDebug(next, "moveCaretVertical", "overset-base->over");
3830
+ return next;
3831
+ }
3832
+ next.caret = {
3833
+ chainId: ownerLocated.chain.id,
3834
+ index: ownerLocated.index + 1
3835
+ };
3836
+ next.selectedStructureNodeId = null;
3837
+ syncSelectionToCaret(next);
3838
+ appendDebug(next, "moveCaretVertical", "overset-base-down-exit");
3839
+ return next;
3840
+ }
3841
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "underset" && ownerNode.underExpr) {
3842
+ if (direction === "down") {
3843
+ next.caret = {
3844
+ chainId: ownerNode.underExpr.id,
3845
+ index: ownerNode.underExpr.nodes.length
3846
+ };
3847
+ next.selectedStructureNodeId = null;
3848
+ syncSelectionToCaret(next);
3849
+ appendDebug(next, "moveCaretVertical", "underset-base->under");
3850
+ return next;
3851
+ }
3852
+ next.caret = {
3853
+ chainId: ownerLocated.chain.id,
3854
+ index: ownerLocated.index
3855
+ };
3856
+ next.selectedStructureNodeId = null;
3857
+ syncSelectionToCaret(next);
3858
+ appendDebug(next, "moveCaretVertical", "underset-base-up-exit");
3859
+ return next;
3860
+ }
3861
+ if (located.relation.kind === "underExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
3862
+ if (direction === "up") {
3863
+ next.caret = {
3864
+ chainId: ownerNode.baseExpr.id,
3865
+ index: ownerNode.baseExpr.nodes.length
3866
+ };
3867
+ next.selectedStructureNodeId = null;
3868
+ syncSelectionToCaret(next);
3869
+ appendDebug(next, "moveCaretVertical", "underset-under->base");
3870
+ return next;
3871
+ }
3872
+ next.caret = {
3873
+ chainId: ownerLocated.chain.id,
3874
+ index: ownerLocated.index + 1
3875
+ };
3876
+ next.selectedStructureNodeId = null;
3877
+ syncSelectionToCaret(next);
3878
+ appendDebug(next, "moveCaretVertical", "underset-under-down-exit");
3879
+ return next;
3880
+ }
3881
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "accent") {
3882
+ if (direction === "up") {
3883
+ next.caret = {
3884
+ chainId: ownerLocated.chain.id,
3885
+ index: ownerLocated.index
3886
+ };
3887
+ next.selectedStructureNodeId = null;
3888
+ syncSelectionToCaret(next);
3889
+ appendDebug(next, "moveCaretVertical", "accent-base-up-exit");
3890
+ return next;
3891
+ }
3892
+ next.caret = {
3893
+ chainId: ownerLocated.chain.id,
3894
+ index: ownerLocated.index + 1
3895
+ };
3896
+ next.selectedStructureNodeId = null;
3897
+ syncSelectionToCaret(next);
3898
+ appendDebug(next, "moveCaretVertical", "accent-base-down-exit");
3899
+ return next;
3900
+ }
3369
3901
  if (located.relation.kind === "matrixCell" && ownerLocated && ownerNode?.matrixRows) {
3370
3902
  const rowIndex = located.relation.rowIndex ?? 0;
3371
3903
  const columnIndex = located.relation.columnIndex ?? 0;
@@ -3743,6 +4275,119 @@ function insertSqrt(session) {
3743
4275
  appendDebug(next, "insertSqrt", "");
3744
4276
  return next;
3745
4277
  }
4278
+ function insertOverset(session) {
4279
+ const base = replaceSelectionFirst(session);
4280
+ const next = cloneEditorSession(base);
4281
+ const englishNode = createOversetNode();
4282
+ const arabicNode = createOversetNode();
4283
+ arabicNode.id = englishNode.id;
4284
+ if (arabicNode.overExpr && englishNode.overExpr) {
4285
+ arabicNode.overExpr.id = englishNode.overExpr.id;
4286
+ }
4287
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4288
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4289
+ }
4290
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4291
+ next.caret = {
4292
+ chainId: englishNode.overExpr ? englishNode.overExpr.id : next.caret.chainId,
4293
+ index: 0
4294
+ };
4295
+ next.selectedStructureNodeId = null;
4296
+ syncSelectionToCaret(next);
4297
+ appendDebug(next, "insertOverset", "");
4298
+ return next;
4299
+ }
4300
+ function insertUnderset(session) {
4301
+ const base = replaceSelectionFirst(session);
4302
+ const next = cloneEditorSession(base);
4303
+ const englishNode = createUndersetNode();
4304
+ const arabicNode = createUndersetNode();
4305
+ arabicNode.id = englishNode.id;
4306
+ if (arabicNode.underExpr && englishNode.underExpr) {
4307
+ arabicNode.underExpr.id = englishNode.underExpr.id;
4308
+ }
4309
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4310
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4311
+ }
4312
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4313
+ next.caret = {
4314
+ chainId: englishNode.underExpr ? englishNode.underExpr.id : next.caret.chainId,
4315
+ index: 0
4316
+ };
4317
+ next.selectedStructureNodeId = null;
4318
+ syncSelectionToCaret(next);
4319
+ appendDebug(next, "insertUnderset", "");
4320
+ return next;
4321
+ }
4322
+ function insertLimitsSeriesTemplate(session, commandId) {
4323
+ const base = replaceSelectionFirst(session);
4324
+ const next = cloneEditorSession(base);
4325
+ const { english, arabic } = buildLimitsSeriesTemplate(commandId);
4326
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
4327
+ syncSubtreeRecursive(next, english, arabic);
4328
+ next.caret = limitsSeriesTemplateInitialCaret(english);
4329
+ next.selectedStructureNodeId = null;
4330
+ syncSelectionToCaret(next);
4331
+ appendDebug(next, "insertLimitsSeriesTemplate", commandId);
4332
+ return next;
4333
+ }
4334
+ function insertAccentPair(session) {
4335
+ const base = replaceSelectionFirst(session);
4336
+ const next = cloneEditorSession(base);
4337
+ const { english, arabic } = buildAccentPairTemplate();
4338
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
4339
+ syncSubtreeRecursive(next, english, arabic);
4340
+ next.caret = accentPairTemplateInitialCaret(english);
4341
+ next.selectedStructureNodeId = null;
4342
+ syncSelectionToCaret(next);
4343
+ appendDebug(next, "insertAccentPair", "");
4344
+ return next;
4345
+ }
4346
+ function insertAccent(session, accentId) {
4347
+ const next = cloneEditorSession(session);
4348
+ const englishNode = createAccentNode(accentId);
4349
+ const arabicNode = createAccentNode(accentId);
4350
+ arabicNode.id = englishNode.id;
4351
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4352
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4353
+ }
4354
+ if (isSelectionActive(next.selection) && next.selection) {
4355
+ const sel = next.selection;
4356
+ const { start, end } = normalizeSelection(sel);
4357
+ const englishLocated = findChainById(next.englishTree, sel.chainId);
4358
+ const arabicLocated = findChainById(next.arabicTree, sel.chainId);
4359
+ if (englishLocated && arabicLocated && end > start) {
4360
+ const count = end - start;
4361
+ const englishWrapped = englishLocated.chain.nodes.splice(start, count);
4362
+ const arabicWrapped = arabicLocated.chain.nodes.splice(start, count);
4363
+ englishNode.baseExpr.nodes = englishWrapped;
4364
+ arabicNode.baseExpr.nodes = arabicWrapped;
4365
+ englishLocated.chain.nodes.splice(start, 0, englishNode);
4366
+ arabicLocated.chain.nodes.splice(start, 0, arabicNode);
4367
+ syncSubtreeRecursive(next, englishNode, arabicNode);
4368
+ next.caret = {
4369
+ chainId: englishNode.baseExpr.id,
4370
+ index: englishWrapped.length
4371
+ };
4372
+ next.selection = null;
4373
+ next.selectedStructureNodeId = null;
4374
+ syncSelectionToCaret(next);
4375
+ appendDebug(next, "insertAccent", `${accentId}:wrap`);
4376
+ return next;
4377
+ }
4378
+ }
4379
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4380
+ syncSubtreeRecursive(next, englishNode, arabicNode);
4381
+ next.caret = {
4382
+ chainId: englishNode.baseExpr ? englishNode.baseExpr.id : next.caret.chainId,
4383
+ index: 0
4384
+ };
4385
+ next.selection = null;
4386
+ next.selectedStructureNodeId = null;
4387
+ syncSelectionToCaret(next);
4388
+ appendDebug(next, "insertAccent", `${accentId}:empty`);
4389
+ return next;
4390
+ }
3746
4391
  function insertMatrixEnv(session, style, rows, columns) {
3747
4392
  return insertGridEnv(session, style, rows, columns);
3748
4393
  }
@@ -3990,7 +4635,7 @@ function deleteBackward(session) {
3990
4635
  if (!previousNode) {
3991
4636
  return session;
3992
4637
  }
3993
- if ((previousNode.kind === "delimiter" || previousNode.kind === "frac" || previousNode.kind === "sqrt" || previousNode.kind === "env") && next.selectedStructureNodeId !== previousNode.id) {
4638
+ 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) {
3994
4639
  next.selectedStructureNodeId = previousNode.id;
3995
4640
  next.selectedNodeId = previousNode.id;
3996
4641
  appendDebug(next, "deleteBackward", "selected structure");
@@ -4480,6 +5125,168 @@ function buildSqrtNodeBody(node, buildChain, side) {
4480
5125
  return wrap;
4481
5126
  }
4482
5127
 
5128
+ // src/editor/display/overset.ts
5129
+ var OVERSET_CSS = `
5130
+ .overset {
5131
+ display: inline-flex;
5132
+ flex-direction: column;
5133
+ align-items: center;
5134
+ justify-content: center;
5135
+ vertical-align: middle;
5136
+ margin: 0 2px;
5137
+ min-width: 1.2em;
5138
+ }
5139
+
5140
+ .overset-over {
5141
+ display: inline-flex;
5142
+ justify-content: center;
5143
+ align-items: center;
5144
+ min-height: 0.85em;
5145
+ padding: 0 2px;
5146
+ font-size: 0.72em;
5147
+ line-height: 1.1;
5148
+ }
5149
+
5150
+ .overset-base {
5151
+ display: inline-flex;
5152
+ justify-content: center;
5153
+ align-items: center;
5154
+ min-height: 1.05em;
5155
+ padding: 0 2px;
5156
+ }
5157
+ `.trim();
5158
+ function buildOversetNodeBody(node, buildChain) {
5159
+ const wrap = document.createElement("span");
5160
+ wrap.className = "overset";
5161
+ const over = document.createElement("span");
5162
+ over.className = "overset-over";
5163
+ if (node.overExpr) {
5164
+ over.appendChild(buildChain(node.overExpr));
5165
+ }
5166
+ const base = document.createElement("span");
5167
+ base.className = "overset-base";
5168
+ if (node.baseExpr) {
5169
+ base.appendChild(buildChain(node.baseExpr));
5170
+ }
5171
+ wrap.appendChild(over);
5172
+ wrap.appendChild(base);
5173
+ return wrap;
5174
+ }
5175
+
5176
+ // src/editor/display/underset.ts
5177
+ var UNDERSET_CSS = `
5178
+ .underset {
5179
+ display: inline-flex;
5180
+ flex-direction: column;
5181
+ align-items: center;
5182
+ justify-content: center;
5183
+ vertical-align: middle;
5184
+ margin: 0 2px;
5185
+ min-width: 1.2em;
5186
+ }
5187
+
5188
+ .underset-base {
5189
+ display: inline-flex;
5190
+ justify-content: center;
5191
+ align-items: center;
5192
+ min-height: 1.05em;
5193
+ padding: 0 2px;
5194
+ }
5195
+
5196
+ .underset-under {
5197
+ display: inline-flex;
5198
+ justify-content: center;
5199
+ align-items: center;
5200
+ min-height: 0.85em;
5201
+ padding: 0 2px;
5202
+ font-size: 0.72em;
5203
+ line-height: 1.1;
5204
+ }
5205
+ `.trim();
5206
+ function buildUndersetNodeBody(node, buildChain) {
5207
+ const wrap = document.createElement("span");
5208
+ wrap.className = "underset";
5209
+ const base = document.createElement("span");
5210
+ base.className = "underset-base";
5211
+ if (node.baseExpr) {
5212
+ base.appendChild(buildChain(node.baseExpr));
5213
+ }
5214
+ const under = document.createElement("span");
5215
+ under.className = "underset-under";
5216
+ if (node.underExpr) {
5217
+ under.appendChild(buildChain(node.underExpr));
5218
+ }
5219
+ wrap.appendChild(base);
5220
+ wrap.appendChild(under);
5221
+ return wrap;
5222
+ }
5223
+
5224
+ // src/editor/display/accent.ts
5225
+ var ACCENT_CSS = `
5226
+ .math-accent {
5227
+ display: inline-flex;
5228
+ flex-direction: column;
5229
+ align-items: center;
5230
+ justify-content: center;
5231
+ vertical-align: middle;
5232
+ margin: 0 2px;
5233
+ min-width: 1.1em;
5234
+ }
5235
+
5236
+ .math-accent-chrome {
5237
+ display: inline-flex;
5238
+ justify-content: center;
5239
+ align-items: center;
5240
+ min-height: 0.7em;
5241
+ padding: 0 2px;
5242
+ font-size: 0.85em;
5243
+ line-height: 1;
5244
+ color: var(--butex-script-color, #475569);
5245
+ user-select: none;
5246
+ }
5247
+
5248
+ .math-accent-base {
5249
+ display: inline-flex;
5250
+ justify-content: center;
5251
+ align-items: center;
5252
+ min-height: 1.05em;
5253
+ padding: 0 2px;
5254
+ }
5255
+
5256
+ .math-accent--under .math-accent-chrome {
5257
+ min-height: 0.45em;
5258
+ border-top: 1.5px solid currentColor;
5259
+ width: 100%;
5260
+ font-size: 0;
5261
+ color: var(--butex-surface-fg, #0f172a);
5262
+ }
5263
+ `.trim();
5264
+ function buildAccentNodeBody(node, buildChain) {
5265
+ const wrap = document.createElement("span");
5266
+ const spec = accentCommandSpec(node.expr);
5267
+ const placement = spec?.placement ?? "over";
5268
+ wrap.className = placement === "under" ? "math-accent math-accent--under" : "math-accent math-accent--over";
5269
+ const chrome = document.createElement("span");
5270
+ chrome.className = "math-accent-chrome";
5271
+ chrome.setAttribute("aria-hidden", "true");
5272
+ if (placement === "over") {
5273
+ chrome.textContent = spec?.label ?? "\u02C6";
5274
+ }
5275
+ const base = document.createElement("span");
5276
+ base.className = "math-accent-base";
5277
+ if (node.baseExpr) {
5278
+ base.appendChild(buildChain(node.baseExpr));
5279
+ }
5280
+ if (placement === "under") {
5281
+ wrap.appendChild(base);
5282
+ wrap.appendChild(chrome);
5283
+ } else {
5284
+ wrap.appendChild(chrome);
5285
+ wrap.appendChild(base);
5286
+ }
5287
+ return wrap;
5288
+ }
5289
+
4483
5290
  // src/editor/display/env.ts
4484
5291
  var WRAPPERS = {
4485
5292
  matrix: { left: "", right: "" },
@@ -4852,6 +5659,15 @@ function buildNodeBody(node, buildChain, side, digitForm = "western", uiLocale =
4852
5659
  if (node.kind === "sqrt") {
4853
5660
  return buildSqrtNodeBody(node, buildChain, side);
4854
5661
  }
5662
+ if (node.kind === "overset") {
5663
+ return buildOversetNodeBody(node, buildChain);
5664
+ }
5665
+ if (node.kind === "underset") {
5666
+ return buildUndersetNodeBody(node, buildChain);
5667
+ }
5668
+ if (node.kind === "accent") {
5669
+ return buildAccentNodeBody(node, buildChain);
5670
+ }
4855
5671
  if (node.kind === "env") {
4856
5672
  return buildMatrixEnvNodeBody(node, buildChain, side);
4857
5673
  }
@@ -5051,6 +5867,12 @@ ${FRAC_CSS}
5051
5867
 
5052
5868
  ${SQRT_CSS}
5053
5869
 
5870
+ ${OVERSET_CSS}
5871
+
5872
+ ${UNDERSET_CSS}
5873
+
5874
+ ${ACCENT_CSS}
5875
+
5054
5876
  ${MATRIX_ENV_CSS}
5055
5877
 
5056
5878
  ${ATOMIC_COMMAND_CSS}
@@ -5770,6 +6592,26 @@ function createEditorRuntime(options) {
5770
6592
  applyStructural((prev) => insertSqrt(prev));
5771
6593
  surfaceEl.focus();
5772
6594
  },
6595
+ insertOverset: () => {
6596
+ applyStructural((prev) => insertOverset(prev));
6597
+ surfaceEl.focus();
6598
+ },
6599
+ insertUnderset: () => {
6600
+ applyStructural((prev) => insertUnderset(prev));
6601
+ surfaceEl.focus();
6602
+ },
6603
+ insertLimitsSeriesTemplate: (commandId) => {
6604
+ applyStructural((prev) => insertLimitsSeriesTemplate(prev, commandId));
6605
+ surfaceEl.focus();
6606
+ },
6607
+ insertAccentPair: () => {
6608
+ applyStructural((prev) => insertAccentPair(prev));
6609
+ surfaceEl.focus();
6610
+ },
6611
+ insertAccent: (accentId) => {
6612
+ applyStructural((prev) => insertAccent(prev, accentId));
6613
+ surfaceEl.focus();
6614
+ },
5773
6615
  insertAtomicCommand: (commandId) => {
5774
6616
  applyStructural((prev) => insertAtomicCommand(prev, commandId));
5775
6617
  surfaceEl.focus();
@@ -6096,6 +6938,56 @@ function commandToEditorNode(node) {
6096
6938
  }
6097
6939
  return { editable: true, node: sqrt };
6098
6940
  }
6941
+ if (node.name === "\\overset" && node.mandatoryArgs.length >= 2) {
6942
+ const overset = createOversetNode();
6943
+ const overExpr = chainToEditorChain(node.mandatoryArgs[0]);
6944
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
6945
+ if (!overExpr.editable) {
6946
+ return overExpr;
6947
+ }
6948
+ if (!baseExpr.editable) {
6949
+ return baseExpr;
6950
+ }
6951
+ overset.overExpr = overExpr.chain;
6952
+ overset.baseExpr = baseExpr.chain;
6953
+ const scripts = attachScripts(node, overset);
6954
+ if (scripts) {
6955
+ return scripts;
6956
+ }
6957
+ return { editable: true, node: overset };
6958
+ }
6959
+ if (node.name === "\\underset" && node.mandatoryArgs.length >= 2) {
6960
+ const underset = createUndersetNode();
6961
+ const underExpr = chainToEditorChain(node.mandatoryArgs[0]);
6962
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
6963
+ if (!underExpr.editable) {
6964
+ return underExpr;
6965
+ }
6966
+ if (!baseExpr.editable) {
6967
+ return baseExpr;
6968
+ }
6969
+ underset.underExpr = underExpr.chain;
6970
+ underset.baseExpr = baseExpr.chain;
6971
+ const scripts = attachScripts(node, underset);
6972
+ if (scripts) {
6973
+ return scripts;
6974
+ }
6975
+ return { editable: true, node: underset };
6976
+ }
6977
+ const accentId = accentCommandIdFromTex(node.name);
6978
+ if (accentId && node.mandatoryArgs.length >= 1) {
6979
+ const accent = createAccentNode(accentId);
6980
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[0]);
6981
+ if (!baseExpr.editable) {
6982
+ return baseExpr;
6983
+ }
6984
+ accent.baseExpr = baseExpr.chain;
6985
+ const scripts = attachScripts(node, accent);
6986
+ if (scripts) {
6987
+ return scripts;
6988
+ }
6989
+ return { editable: true, node: accent };
6990
+ }
6099
6991
  const atomicId = commandIdForTex(node.name);
6100
6992
  if (atomicId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
6101
6993
  const atomic = createEditorNode("atomicCommand", atomicId);
@@ -6232,6 +7124,21 @@ function editorEnvToAstNode(node) {
6232
7124
  const scripts = attachAstScripts(node, env);
6233
7125
  return scripts ?? { ok: true, node: env };
6234
7126
  }
7127
+ function accentChainForExport(accent) {
7128
+ if (!accent) {
7129
+ return { ok: true, chain: new ChainNode([]) };
7130
+ }
7131
+ return editorChainToAstChain(accent);
7132
+ }
7133
+ function nodeFromAccentBase(command, accent, base) {
7134
+ if (accent.chain.length > 0) {
7135
+ return new CommandNode(command, [], [accent, base]);
7136
+ }
7137
+ if (base.chain.length === 1) {
7138
+ return base.chain[0];
7139
+ }
7140
+ return new DelimiterNode("", base, "");
7141
+ }
6235
7142
  function editorNodeToAstNode(node) {
6236
7143
  let astNode;
6237
7144
  if (node.kind === "char") {
@@ -6270,6 +7177,36 @@ function editorNodeToAstNode(node) {
6270
7177
  optionalArgs.push(rootIndex.chain);
6271
7178
  }
6272
7179
  astNode = new CommandNode("\\sqrt", optionalArgs, [radicand.chain]);
7180
+ } else if (node.kind === "overset" && node.baseExpr) {
7181
+ const accent = accentChainForExport(node.overExpr);
7182
+ if (!accent.ok) {
7183
+ return accent;
7184
+ }
7185
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7186
+ if (!baseExpr.ok) {
7187
+ return baseExpr;
7188
+ }
7189
+ astNode = nodeFromAccentBase("\\overset", accent.chain, baseExpr.chain);
7190
+ } else if (node.kind === "underset" && node.baseExpr) {
7191
+ const accent = accentChainForExport(node.underExpr);
7192
+ if (!accent.ok) {
7193
+ return accent;
7194
+ }
7195
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7196
+ if (!baseExpr.ok) {
7197
+ return baseExpr;
7198
+ }
7199
+ astNode = nodeFromAccentBase("\\underset", accent.chain, baseExpr.chain);
7200
+ } else if (node.kind === "accent" && node.baseExpr) {
7201
+ const tex = accentCommandSpec(node.expr)?.englishTex;
7202
+ if (!tex) {
7203
+ 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}` };
7204
+ }
7205
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7206
+ if (!baseExpr.ok) {
7207
+ return baseExpr;
7208
+ }
7209
+ astNode = new CommandNode(tex, [], [baseExpr.chain]);
6273
7210
  } else if (node.kind === "atomicCommand") {
6274
7211
  const tex = atomicCommandSpec(node.expr)?.englishTex;
6275
7212
  if (!tex) {
@@ -7392,6 +8329,29 @@ var WIDGET_CHROME_CSS = `
7392
8329
  padding-top: 1px;
7393
8330
  }
7394
8331
 
8332
+ .butex-widget .toolbar-accent-icon {
8333
+ display: inline-flex;
8334
+ flex-direction: column;
8335
+ align-items: center;
8336
+ justify-content: center;
8337
+ min-width: 14px;
8338
+ font-size: 10px;
8339
+ line-height: 1;
8340
+ gap: 1px;
8341
+ }
8342
+
8343
+ .butex-widget .toolbar-accent-icon--over span:first-child,
8344
+ .butex-widget .toolbar-accent-icon--under span:last-child {
8345
+ font-size: 8px;
8346
+ opacity: 0.85;
8347
+ }
8348
+
8349
+ .butex-widget .toolbar-accent-icon--pair span:first-child,
8350
+ .butex-widget .toolbar-accent-icon--pair span:last-child {
8351
+ font-size: 8px;
8352
+ opacity: 0.85;
8353
+ }
8354
+
7395
8355
  .butex-widget .matrix-command-menu {
7396
8356
  position: relative;
7397
8357
  display: inline-flex;
@@ -8133,6 +9093,7 @@ var CHARACTER_FONT_OPTIONS = [
8133
9093
  { id: "diwani", label: "\u062F\u064A\u0648" },
8134
9094
  { id: "diwaniOutline", label: "\u0638\u0644" }
8135
9095
  ];
9096
+ var ACCENT_COMMANDS_LIST = accentCommandsList();
8136
9097
  var FUNCTION_COMMANDS = atomicCommandsByCategory("function");
8137
9098
  var LIMITS_SERIES_COMMANDS = atomicCommandsByCategory("limits_series");
8138
9099
  var FUNCTION2_COMMANDS = atomicCommandsByCategory("function2");
@@ -8280,6 +9241,7 @@ var ButexEditor = (0, import_react.forwardRef)(
8280
9241
  const [operator2MenuOpen, setOperator2MenuOpen] = (0, import_react.useState)(false);
8281
9242
  const [operator3MenuOpen, setOperator3MenuOpen] = (0, import_react.useState)(false);
8282
9243
  const [dotMenuOpen, setDotMenuOpen] = (0, import_react.useState)(false);
9244
+ const [accentMenuOpen, setAccentMenuOpen] = (0, import_react.useState)(false);
8283
9245
  const [integralMenuOpen, setIntegralMenuOpen] = (0, import_react.useState)(false);
8284
9246
  const [selectedDigitForm2, setSelectedDigitForm] = (0, import_react.useState)("western");
8285
9247
  const [selectedCharacterFont, setSelectedCharacterFont] = (0, import_react.useState)("default");
@@ -8549,7 +9511,78 @@ ${arabic || currentMessages.empty}`;
8549
9511
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-sqrt", className: "icon-btn", type: "button", title: messages.insertRoot, "aria-label": messages.insertRoot, onClick: () => {
8550
9512
  runtimeRef.current?.insertSqrt();
8551
9513
  focusSurface();
8552
- }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) })
9514
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) }),
9515
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-overset", className: "icon-btn", type: "button", title: messages.insertOverset, "aria-label": messages.insertOverset, onClick: () => {
9516
+ runtimeRef.current?.insertOverset();
9517
+ focusSurface();
9518
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
9519
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" }),
9520
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" })
9521
+ ] }) }),
9522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-underset", className: "icon-btn", type: "button", title: messages.insertUnderset, "aria-label": messages.insertUnderset, onClick: () => {
9523
+ runtimeRef.current?.insertUnderset();
9524
+ focusSurface();
9525
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--under", "aria-hidden": "true", children: [
9526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" }),
9527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" })
9528
+ ] }) }),
9529
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { id: "insert-accent-pair", className: "icon-btn", type: "button", title: messages.insertAccentPair, "aria-label": messages.insertAccentPair, onClick: () => {
9530
+ runtimeRef.current?.insertAccentPair();
9531
+ focusSurface();
9532
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--pair", "aria-hidden": "true", children: [
9533
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" }),
9534
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" }),
9535
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u2217" })
9536
+ ] }) }),
9537
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
9538
+ "div",
9539
+ {
9540
+ className: "function-command-menu operator-command-menu",
9541
+ onBlur: (event) => {
9542
+ const nextFocus = event.relatedTarget;
9543
+ if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) {
9544
+ setAccentMenuOpen(false);
9545
+ }
9546
+ },
9547
+ children: [
9548
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9549
+ "button",
9550
+ {
9551
+ id: "insert-accents",
9552
+ className: "icon-btn function-command-btn operator-command-btn",
9553
+ type: "button",
9554
+ title: messages.insertAccents,
9555
+ "aria-label": messages.insertAccents,
9556
+ "aria-haspopup": "listbox",
9557
+ "aria-expanded": accentMenuOpen,
9558
+ onClick: () => setAccentMenuOpen((open) => !open),
9559
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
9560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u02C6" }),
9561
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u25A1" })
9562
+ ] })
9563
+ }
9564
+ ),
9565
+ /* @__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)(
9566
+ "button",
9567
+ {
9568
+ className: "function-command-option operator-command-option",
9569
+ type: "button",
9570
+ role: "option",
9571
+ title: accentCommandTitle(command, uiLocale),
9572
+ "aria-label": accentCommandTitle(command, uiLocale),
9573
+ onMouseDown: (event) => event.preventDefault(),
9574
+ onClick: () => {
9575
+ runtimeRef.current?.insertAccent(command.id);
9576
+ setAccentMenuOpen(false);
9577
+ focusSurface();
9578
+ },
9579
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "operator-command-label", children: command.label })
9580
+ },
9581
+ command.id
9582
+ )) })
9583
+ ]
9584
+ }
9585
+ )
8553
9586
  ] }),
8554
9587
  /* @__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)(
8555
9588
  "div",
@@ -8877,7 +9910,7 @@ ${arabic || currentMessages.empty}`;
8877
9910
  "aria-label": atomicCommandTitle(command, uiLocale),
8878
9911
  onMouseDown: (event) => event.preventDefault(),
8879
9912
  onClick: () => {
8880
- runtimeRef.current?.insertAtomicCommand(command.id);
9913
+ runtimeRef.current?.insertLimitsSeriesTemplate(command.id);
8881
9914
  setLimitsSeriesMenuOpen(false);
8882
9915
  focusSurface();
8883
9916
  },