@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.
@@ -1353,6 +1353,158 @@ function createEmptyDocument2() {
1353
1353
  };
1354
1354
  }
1355
1355
 
1356
+ // src/editor/accentCommands.ts
1357
+ var ACCENT_COMMANDS = {
1358
+ vec: {
1359
+ id: "vec",
1360
+ englishTex: "\\vec",
1361
+ arabicTex: "\\arabvec",
1362
+ placement: "over",
1363
+ label: "\u20D7",
1364
+ title: "\u0633\u0647\u0645 \u0641\u0648\u0642"
1365
+ },
1366
+ hat: {
1367
+ id: "hat",
1368
+ englishTex: "\\hat",
1369
+ arabicTex: "\\hat",
1370
+ placement: "over",
1371
+ label: "\u02C6",
1372
+ title: "\u0642\u0628\u0639\u0629"
1373
+ },
1374
+ tilde: {
1375
+ id: "tilde",
1376
+ englishTex: "\\tilde",
1377
+ arabicTex: "\\tilde",
1378
+ placement: "over",
1379
+ label: "\u02DC",
1380
+ title: "\u0645\u062F\u0629"
1381
+ },
1382
+ dot: {
1383
+ id: "dot",
1384
+ englishTex: "\\dot",
1385
+ arabicTex: "\\dot",
1386
+ placement: "over",
1387
+ label: "\u02D9",
1388
+ title: "\u0646\u0642\u0637\u0629 \u0641\u0648\u0642"
1389
+ },
1390
+ ddot: {
1391
+ id: "ddot",
1392
+ englishTex: "\\ddot",
1393
+ arabicTex: "\\ddot",
1394
+ placement: "over",
1395
+ label: "\xA8",
1396
+ title: "\u0646\u0642\u0637\u062A\u0627\u0646 \u0641\u0648\u0642"
1397
+ },
1398
+ dddot: {
1399
+ id: "dddot",
1400
+ englishTex: "\\dddot",
1401
+ arabicTex: "\\dddot",
1402
+ placement: "over",
1403
+ label: "\u20DB",
1404
+ title: "\u062B\u0644\u0627\u062B \u0646\u0642\u0627\u0637 \u0641\u0648\u0642"
1405
+ },
1406
+ bar: {
1407
+ id: "bar",
1408
+ englishTex: "\\bar",
1409
+ arabicTex: "\\bar",
1410
+ placement: "over",
1411
+ label: "\xAF",
1412
+ title: "\u062E\u0637 \u0641\u0648\u0642"
1413
+ },
1414
+ check: {
1415
+ id: "check",
1416
+ englishTex: "\\check",
1417
+ arabicTex: "\\check",
1418
+ placement: "over",
1419
+ label: "\u02C7",
1420
+ title: "\u0639\u0644\u0627\u0645\u0629 \u062A\u062D\u0642\u0642"
1421
+ },
1422
+ breve: {
1423
+ id: "breve",
1424
+ englishTex: "\\breve",
1425
+ arabicTex: "\\breve",
1426
+ placement: "over",
1427
+ label: "\u02D8",
1428
+ title: "\u0642\u0648\u0633 \u0642\u0635\u064A\u0631"
1429
+ },
1430
+ acute: {
1431
+ id: "acute",
1432
+ englishTex: "\\acute",
1433
+ arabicTex: "\\grave",
1434
+ placement: "over",
1435
+ label: "\xB4",
1436
+ title: "\u0646\u0628\u0631\u0629 \u062D\u0627\u062F\u0629"
1437
+ },
1438
+ grave: {
1439
+ id: "grave",
1440
+ englishTex: "\\grave",
1441
+ arabicTex: "\\acute",
1442
+ placement: "over",
1443
+ label: "`",
1444
+ title: "\u0646\u0628\u0631\u0629 \u062B\u0642\u064A\u0644\u0629"
1445
+ },
1446
+ overline: {
1447
+ id: "overline",
1448
+ englishTex: "\\overline",
1449
+ arabicTex: "\\overline",
1450
+ placement: "over",
1451
+ label: "\u203E",
1452
+ title: "\u062E\u0637 \u0639\u0644\u0648\u064A \u0645\u0645\u062A\u062F"
1453
+ },
1454
+ underline: {
1455
+ id: "underline",
1456
+ englishTex: "\\underline",
1457
+ arabicTex: "\\underline",
1458
+ placement: "under",
1459
+ label: "_",
1460
+ title: "\u062E\u0637 \u0633\u0641\u0644\u064A"
1461
+ },
1462
+ overleftarrow: {
1463
+ id: "overleftarrow",
1464
+ englishTex: "\\overleftarrow",
1465
+ arabicTex: "\\overrightarrow",
1466
+ placement: "over",
1467
+ label: "\u2190",
1468
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631"
1469
+ },
1470
+ overrightarrow: {
1471
+ id: "overrightarrow",
1472
+ englishTex: "\\overrightarrow",
1473
+ arabicTex: "\\overleftarrow",
1474
+ placement: "over",
1475
+ label: "\u2192",
1476
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646"
1477
+ },
1478
+ overleftrightarrow: {
1479
+ id: "overleftrightarrow",
1480
+ englishTex: "\\overleftrightarrow",
1481
+ arabicTex: "\\overleftrightarrow",
1482
+ placement: "over",
1483
+ label: "\u2194",
1484
+ title: "\u0633\u0647\u0645 \u0639\u0644\u0648\u064A \u0645\u0632\u062F\u0648\u062C \u0627\u0644\u0627\u062A\u062C\u0627\u0647"
1485
+ }
1486
+ };
1487
+ function accentCommandSpec(id) {
1488
+ return Object.prototype.hasOwnProperty.call(ACCENT_COMMANDS, id) ? ACCENT_COMMANDS[id] : null;
1489
+ }
1490
+ function accentCommandsList() {
1491
+ return Object.values(ACCENT_COMMANDS);
1492
+ }
1493
+ function accentCommandIdFromTex(tex) {
1494
+ const normalized = tex.startsWith("\\") ? tex : `\\${tex}`;
1495
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
1496
+ if (spec.englishTex === normalized) {
1497
+ return spec.id;
1498
+ }
1499
+ }
1500
+ for (const spec of Object.values(ACCENT_COMMANDS)) {
1501
+ if (spec.arabicTex === normalized) {
1502
+ return spec.id;
1503
+ }
1504
+ }
1505
+ return null;
1506
+ }
1507
+
1356
1508
  // src/editor/atomicCommands.ts
1357
1509
  var ATOMIC_COMMANDS = {
1358
1510
  ///////////////////////// Functions ////////////////////////////////
@@ -2327,6 +2479,9 @@ function createEditorNode(kind, expr) {
2327
2479
  denominator: null,
2328
2480
  radicand: null,
2329
2481
  rootIndex: null,
2482
+ overExpr: null,
2483
+ underExpr: null,
2484
+ baseExpr: null,
2330
2485
  envName: null,
2331
2486
  matrixStyle: null,
2332
2487
  matrixRows: null,
@@ -2355,6 +2510,26 @@ function createSqrtNode() {
2355
2510
  rootIndex: createEditorChain()
2356
2511
  };
2357
2512
  }
2513
+ function createOversetNode() {
2514
+ return {
2515
+ ...createEditorNode("overset", ""),
2516
+ overExpr: createEditorChain(),
2517
+ baseExpr: createEditorChain()
2518
+ };
2519
+ }
2520
+ function createUndersetNode() {
2521
+ return {
2522
+ ...createEditorNode("underset", ""),
2523
+ underExpr: createEditorChain(),
2524
+ baseExpr: createEditorChain()
2525
+ };
2526
+ }
2527
+ function createAccentNode(accentId) {
2528
+ return {
2529
+ ...createEditorNode("accent", accentId),
2530
+ baseExpr: createEditorChain()
2531
+ };
2532
+ }
2358
2533
  function createGridEnvNode(envName, rows, columns, columnAlignments) {
2359
2534
  const safeRows = Math.max(1, Math.min(12, Math.trunc(rows)));
2360
2535
  const safeColumns = Math.max(1, Math.min(12, Math.trunc(columns)));
@@ -2419,6 +2594,15 @@ function collectNodeIds(chain, ids) {
2419
2594
  if (node.radicand) {
2420
2595
  collectNodeIds(node.radicand, ids);
2421
2596
  }
2597
+ if (node.overExpr) {
2598
+ collectNodeIds(node.overExpr, ids);
2599
+ }
2600
+ if (node.underExpr) {
2601
+ collectNodeIds(node.underExpr, ids);
2602
+ }
2603
+ if (node.baseExpr) {
2604
+ collectNodeIds(node.baseExpr, ids);
2605
+ }
2422
2606
  if (node.matrixRows) {
2423
2607
  for (const row of node.matrixRows) {
2424
2608
  for (const cell of row) {
@@ -2555,6 +2739,22 @@ function renderMatrixRowsArabic(node, options) {
2555
2739
  }
2556
2740
  return `\\begin{${envName}}${rows.join(" \\\\\n")}\\end{${envName}}`;
2557
2741
  }
2742
+ function renderAccentEnglish(command, accent, base, options) {
2743
+ const baseTex = base ? renderChainEnglish(base, options) : "";
2744
+ const accentTex = accent ? renderChainEnglish(accent, options) : "";
2745
+ if (accentTex === "") {
2746
+ return baseTex;
2747
+ }
2748
+ return `\\${command}{${accentTex}}{${baseTex}}`;
2749
+ }
2750
+ function renderAccentArabic(command, accent, base, options) {
2751
+ const baseTex = base ? renderChainArabic(base, options) : "";
2752
+ const accentTex = accent ? renderChainArabic(accent, options) : "";
2753
+ if (accentTex === "") {
2754
+ return baseTex;
2755
+ }
2756
+ return `\\${command}{${accentTex}}{${baseTex}}`;
2757
+ }
2558
2758
  function escapeText(s) {
2559
2759
  return s.replace(/\\/g, "\\textbackslash{}").replace(/[{}]/g, "\\$&");
2560
2760
  }
@@ -2584,6 +2784,19 @@ function renderNodeEnglish(node, options) {
2584
2784
  const body = `\\sqrt${optionalRootIndexEnglish(node, options)}{${renderChainEnglish(node.radicand, options)}}`;
2585
2785
  return `${body}${latinScripts(node, options)}`;
2586
2786
  }
2787
+ if (node.kind === "overset" && node.baseExpr) {
2788
+ const body = renderAccentEnglish("overset", node.overExpr, node.baseExpr, options);
2789
+ return `${body}${latinScripts(node, options)}`;
2790
+ }
2791
+ if (node.kind === "underset" && node.baseExpr) {
2792
+ const body = renderAccentEnglish("underset", node.underExpr, node.baseExpr, options);
2793
+ return `${body}${latinScripts(node, options)}`;
2794
+ }
2795
+ if (node.kind === "accent" && node.baseExpr) {
2796
+ const cmd = accentCommandSpec(node.expr)?.englishTex ?? `\\${node.expr}`;
2797
+ const body = `${cmd}{${renderChainEnglish(node.baseExpr, options)}}`;
2798
+ return `${body}${latinScripts(node, options)}`;
2799
+ }
2587
2800
  if (node.kind === "env") {
2588
2801
  return `${renderMatrixRowsEnglish(node, options)}${latinScripts(node, options)}`;
2589
2802
  }
@@ -2616,6 +2829,19 @@ function renderNodeArabic(node, options) {
2616
2829
  const content = `\\arabsqrt${optionalRootIndexArabic(node, options)}{${renderChainArabic(node.radicand, options)}}`;
2617
2830
  return arabicScripts(node, content, options);
2618
2831
  }
2832
+ if (node.kind === "overset" && node.baseExpr) {
2833
+ const content = renderAccentArabic("overset", node.overExpr, node.baseExpr, options);
2834
+ return arabicScripts(node, content, options);
2835
+ }
2836
+ if (node.kind === "underset" && node.baseExpr) {
2837
+ const content = renderAccentArabic("underset", node.underExpr, node.baseExpr, options);
2838
+ return arabicScripts(node, content, options);
2839
+ }
2840
+ if (node.kind === "accent" && node.baseExpr) {
2841
+ const cmd = accentCommandSpec(node.expr)?.arabicTex ?? `\\${node.expr}`;
2842
+ const content = `${cmd}{${renderChainArabic(node.baseExpr, options)}}`;
2843
+ return arabicScripts(node, content, options);
2844
+ }
2619
2845
  if (node.kind === "env") {
2620
2846
  return arabicScripts(node, renderMatrixRowsArabic(node, options), options);
2621
2847
  }
@@ -2672,6 +2898,35 @@ function collectCaretPositions(chain) {
2672
2898
  if (node.radicand) {
2673
2899
  walkChain(node.radicand);
2674
2900
  }
2901
+ if (node.kind === "overset") {
2902
+ if (node.overExpr) {
2903
+ walkChain(node.overExpr);
2904
+ }
2905
+ if (node.baseExpr) {
2906
+ walkChain(node.baseExpr);
2907
+ }
2908
+ } else if (node.kind === "underset") {
2909
+ if (node.baseExpr) {
2910
+ walkChain(node.baseExpr);
2911
+ }
2912
+ if (node.underExpr) {
2913
+ walkChain(node.underExpr);
2914
+ }
2915
+ } else if (node.kind === "accent") {
2916
+ if (node.baseExpr) {
2917
+ walkChain(node.baseExpr);
2918
+ }
2919
+ } else {
2920
+ if (node.overExpr) {
2921
+ walkChain(node.overExpr);
2922
+ }
2923
+ if (node.underExpr) {
2924
+ walkChain(node.underExpr);
2925
+ }
2926
+ if (node.baseExpr) {
2927
+ walkChain(node.baseExpr);
2928
+ }
2929
+ }
2675
2930
  if (node.matrixRows) {
2676
2931
  for (const row of node.matrixRows) {
2677
2932
  for (const cell of row) {
@@ -2805,6 +3060,42 @@ function findChainById(root, chainId) {
2805
3060
  return nested;
2806
3061
  }
2807
3062
  }
3063
+ if (node.overExpr) {
3064
+ if (node.overExpr.id === chainId) {
3065
+ return {
3066
+ chain: node.overExpr,
3067
+ relation: { kind: "overExpr", ownerNodeId: node.id, ownerChainId: root.id }
3068
+ };
3069
+ }
3070
+ const nested = findChainById(node.overExpr, chainId);
3071
+ if (nested) {
3072
+ return nested;
3073
+ }
3074
+ }
3075
+ if (node.underExpr) {
3076
+ if (node.underExpr.id === chainId) {
3077
+ return {
3078
+ chain: node.underExpr,
3079
+ relation: { kind: "underExpr", ownerNodeId: node.id, ownerChainId: root.id }
3080
+ };
3081
+ }
3082
+ const nested = findChainById(node.underExpr, chainId);
3083
+ if (nested) {
3084
+ return nested;
3085
+ }
3086
+ }
3087
+ if (node.baseExpr) {
3088
+ if (node.baseExpr.id === chainId) {
3089
+ return {
3090
+ chain: node.baseExpr,
3091
+ relation: { kind: "baseExpr", ownerNodeId: node.id, ownerChainId: root.id }
3092
+ };
3093
+ }
3094
+ const nested = findChainById(node.baseExpr, chainId);
3095
+ if (nested) {
3096
+ return nested;
3097
+ }
3098
+ }
2808
3099
  if (node.matrixRows) {
2809
3100
  for (let rowIndex = 0; rowIndex < node.matrixRows.length; rowIndex += 1) {
2810
3101
  const row = node.matrixRows[rowIndex];
@@ -2878,6 +3169,24 @@ function findNodeById(root, nodeId) {
2878
3169
  return nested;
2879
3170
  }
2880
3171
  }
3172
+ if (node.overExpr) {
3173
+ const nested = findNodeById(node.overExpr, nodeId);
3174
+ if (nested) {
3175
+ return nested;
3176
+ }
3177
+ }
3178
+ if (node.underExpr) {
3179
+ const nested = findNodeById(node.underExpr, nodeId);
3180
+ if (nested) {
3181
+ return nested;
3182
+ }
3183
+ }
3184
+ if (node.baseExpr) {
3185
+ const nested = findNodeById(node.baseExpr, nodeId);
3186
+ if (nested) {
3187
+ return nested;
3188
+ }
3189
+ }
2881
3190
  if (node.matrixRows) {
2882
3191
  for (const row of node.matrixRows) {
2883
3192
  for (const cell of row) {
@@ -2913,6 +3222,15 @@ function nodeChildChains(node) {
2913
3222
  if (node.radicand) {
2914
3223
  chains.push(node.radicand);
2915
3224
  }
3225
+ if (node.overExpr) {
3226
+ chains.push(node.overExpr);
3227
+ }
3228
+ if (node.underExpr) {
3229
+ chains.push(node.underExpr);
3230
+ }
3231
+ if (node.baseExpr) {
3232
+ chains.push(node.baseExpr);
3233
+ }
2916
3234
  if (node.matrixRows) {
2917
3235
  for (const row of node.matrixRows) {
2918
3236
  chains.push(...row);
@@ -3283,6 +3601,91 @@ function derivativeTemplateInitialCaret(englishFrac) {
3283
3601
  };
3284
3602
  }
3285
3603
 
3604
+ // src/editor/limitsTemplates.ts
3605
+ var LIMITS_SERIES_TEMPLATE_IDS = /* @__PURE__ */ new Set([
3606
+ "sum",
3607
+ "prod",
3608
+ "lim",
3609
+ "max",
3610
+ "min",
3611
+ "sup",
3612
+ "inf"
3613
+ ]);
3614
+ function isLimitsSeriesTemplateId(commandId) {
3615
+ return LIMITS_SERIES_TEMPLATE_IDS.has(commandId);
3616
+ }
3617
+ function syncChainIds2(english, arabic) {
3618
+ arabic.id = english.id;
3619
+ const limit = Math.min(english.nodes.length, arabic.nodes.length);
3620
+ for (let index = 0; index < limit; index += 1) {
3621
+ arabic.nodes[index].id = english.nodes[index].id;
3622
+ }
3623
+ }
3624
+ function syncOversetIds(english, arabic) {
3625
+ arabic.id = english.id;
3626
+ if (english.overExpr && arabic.overExpr) {
3627
+ syncChainIds2(english.overExpr, arabic.overExpr);
3628
+ }
3629
+ if (english.baseExpr && arabic.baseExpr) {
3630
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
3631
+ }
3632
+ }
3633
+ function syncUndersetIds(english, arabic) {
3634
+ arabic.id = english.id;
3635
+ if (english.underExpr && arabic.underExpr) {
3636
+ syncChainIds2(english.underExpr, arabic.underExpr);
3637
+ }
3638
+ if (english.baseExpr && arabic.baseExpr) {
3639
+ syncChainIds2(english.baseExpr, arabic.baseExpr);
3640
+ }
3641
+ }
3642
+ function buildLimitsSeriesTemplate(commandId) {
3643
+ if (!isLimitsSeriesTemplateId(commandId)) {
3644
+ throw new Error(`BuTeX: unsupported limits-series template id: ${commandId}`);
3645
+ }
3646
+ const englishOp = createAtomicCommandNode(commandId);
3647
+ const arabicOp = createAtomicCommandNode(commandId);
3648
+ arabicOp.id = englishOp.id;
3649
+ const englishOverset = createOversetNode();
3650
+ const arabicOverset = createOversetNode();
3651
+ syncOversetIds(englishOverset, arabicOverset);
3652
+ englishOverset.baseExpr = createEditorChain([englishOp]);
3653
+ arabicOverset.baseExpr = createEditorChain([arabicOp]);
3654
+ syncChainIds2(englishOverset.baseExpr, arabicOverset.baseExpr);
3655
+ const englishUnderset = createUndersetNode();
3656
+ const arabicUnderset = createUndersetNode();
3657
+ syncUndersetIds(englishUnderset, arabicUnderset);
3658
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
3659
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
3660
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
3661
+ return { english: englishUnderset, arabic: arabicUnderset };
3662
+ }
3663
+ function buildAccentPairTemplate() {
3664
+ const englishOverset = createOversetNode();
3665
+ const arabicOverset = createOversetNode();
3666
+ syncOversetIds(englishOverset, arabicOverset);
3667
+ const englishUnderset = createUndersetNode();
3668
+ const arabicUnderset = createUndersetNode();
3669
+ syncUndersetIds(englishUnderset, arabicUnderset);
3670
+ englishUnderset.baseExpr = createEditorChain([englishOverset]);
3671
+ arabicUnderset.baseExpr = createEditorChain([arabicOverset]);
3672
+ syncChainIds2(englishUnderset.baseExpr, arabicUnderset.baseExpr);
3673
+ return { english: englishUnderset, arabic: arabicUnderset };
3674
+ }
3675
+ function accentPairTemplateInitialCaret(englishUnderset) {
3676
+ const overset = englishUnderset.baseExpr?.nodes[0];
3677
+ return {
3678
+ chainId: overset?.baseExpr?.id ?? englishUnderset.underExpr?.id ?? "",
3679
+ index: 0
3680
+ };
3681
+ }
3682
+ function limitsSeriesTemplateInitialCaret(englishUnderset) {
3683
+ return {
3684
+ chainId: englishUnderset.underExpr?.id ?? englishUnderset.baseExpr?.id ?? "",
3685
+ index: 0
3686
+ };
3687
+ }
3688
+
3286
3689
  // src/editor/uiLocale.ts
3287
3690
  var ENGLISH_COMMAND_TITLES = {
3288
3691
  cos: "Cosine",
@@ -3326,6 +3729,24 @@ var ENGLISH_COMMAND_TITLES = {
3326
3729
  quadSpace: "Large space",
3327
3730
  qquadSpace: "Extra-large space"
3328
3731
  };
3732
+ var ENGLISH_ACCENT_TITLES = {
3733
+ vec: "Vector arrow",
3734
+ hat: "Hat",
3735
+ tilde: "Tilde",
3736
+ dot: "Dot",
3737
+ ddot: "Double dot",
3738
+ dddot: "Triple dot",
3739
+ bar: "Bar",
3740
+ check: "Check",
3741
+ breve: "Breve",
3742
+ acute: "Acute",
3743
+ grave: "Grave",
3744
+ overline: "Overline",
3745
+ underline: "Underline",
3746
+ overleftarrow: "Over left arrow",
3747
+ overrightarrow: "Over right arrow",
3748
+ overleftrightarrow: "Over left-right arrow"
3749
+ };
3329
3750
  var ENGLISH_OPERATOR_TITLES = {
3330
3751
  inf: "Infinity",
3331
3752
  times: "Multiplication",
@@ -3399,6 +3820,10 @@ var EQUATION_EDITOR_MESSAGES = {
3399
3820
  insertBraces: "\u0625\u062F\u0631\u0627\u062C \u062D\u0627\u0635\u0631\u062A\u064A\u0646",
3400
3821
  insertFraction: "\u0625\u062F\u0631\u0627\u062C \u0643\u0633\u0631",
3401
3822
  insertRoot: "\u0625\u062F\u0631\u0627\u062C \u062C\u0630\u0631",
3823
+ insertOverset: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642",
3824
+ insertUnderset: "\u0625\u062F\u0631\u0627\u062C \u062A\u062D\u062A",
3825
+ insertAccentPair: "\u0625\u062F\u0631\u0627\u062C \u0641\u0648\u0642 \u0648\u062A\u062D\u062A",
3826
+ insertAccents: "\u0625\u062F\u0631\u0627\u062C \u0644\u0643\u0646\u0629",
3402
3827
  environments: "\u0627\u0644\u0628\u064A\u0626\u0627\u062A",
3403
3828
  insertEnvironment: "\u0625\u062F\u0631\u0627\u062C \u0628\u064A\u0626\u0629 \u0634\u0628\u0643\u064A\u0629",
3404
3829
  environmentType: "\u0646\u0648\u0639 \u0627\u0644\u0628\u064A\u0626\u0629",
@@ -3506,6 +3931,10 @@ var EQUATION_EDITOR_MESSAGES = {
3506
3931
  insertBraces: "Insert braces",
3507
3932
  insertFraction: "Insert fraction",
3508
3933
  insertRoot: "Insert root",
3934
+ insertOverset: "Insert overset",
3935
+ insertUnderset: "Insert underset",
3936
+ insertAccentPair: "Insert overset and underset",
3937
+ insertAccents: "Insert accent",
3509
3938
  environments: "Environments",
3510
3939
  insertEnvironment: "Insert grid environment",
3511
3940
  environmentType: "Environment type",
@@ -3606,6 +4035,9 @@ function atomicCommandTitle(spec, locale) {
3606
4035
  function atomicOperatorCommandTitle(spec, locale) {
3607
4036
  return locale === "en" ? ENGLISH_OPERATOR_TITLES[spec.id] ?? spec.title : spec.title;
3608
4037
  }
4038
+ function accentCommandTitle(spec, locale) {
4039
+ return locale === "en" ? ENGLISH_ACCENT_TITLES[spec.id] ?? spec.title : spec.title;
4040
+ }
3609
4041
 
3610
4042
  // src/editor/state.ts
3611
4043
  function ensureSyncEntry(session, nodeId) {
@@ -3640,7 +4072,7 @@ function syncSelectionToCaret(session) {
3640
4072
  session.selectedNodeId = located.relation.ownerNodeId;
3641
4073
  return;
3642
4074
  }
3643
- 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") {
4075
+ 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") {
3644
4076
  session.selectedNodeId = located.relation.ownerNodeId;
3645
4077
  return;
3646
4078
  }
@@ -3997,6 +4429,106 @@ function moveCaretVertical(session, direction) {
3997
4429
  appendDebug(next, "moveCaretVertical", "sqrt-radicand-down-exit");
3998
4430
  return next;
3999
4431
  }
4432
+ if (located.relation.kind === "overExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
4433
+ if (direction === "down") {
4434
+ next.caret = {
4435
+ chainId: ownerNode.baseExpr.id,
4436
+ index: ownerNode.baseExpr.nodes.length
4437
+ };
4438
+ next.selectedStructureNodeId = null;
4439
+ syncSelectionToCaret(next);
4440
+ appendDebug(next, "moveCaretVertical", "overset-over->base");
4441
+ return next;
4442
+ }
4443
+ next.caret = {
4444
+ chainId: ownerLocated.chain.id,
4445
+ index: ownerLocated.index
4446
+ };
4447
+ next.selectedStructureNodeId = null;
4448
+ syncSelectionToCaret(next);
4449
+ appendDebug(next, "moveCaretVertical", "overset-over-up-exit");
4450
+ return next;
4451
+ }
4452
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "overset" && ownerNode.overExpr) {
4453
+ if (direction === "up") {
4454
+ next.caret = {
4455
+ chainId: ownerNode.overExpr.id,
4456
+ index: ownerNode.overExpr.nodes.length
4457
+ };
4458
+ next.selectedStructureNodeId = null;
4459
+ syncSelectionToCaret(next);
4460
+ appendDebug(next, "moveCaretVertical", "overset-base->over");
4461
+ return next;
4462
+ }
4463
+ next.caret = {
4464
+ chainId: ownerLocated.chain.id,
4465
+ index: ownerLocated.index + 1
4466
+ };
4467
+ next.selectedStructureNodeId = null;
4468
+ syncSelectionToCaret(next);
4469
+ appendDebug(next, "moveCaretVertical", "overset-base-down-exit");
4470
+ return next;
4471
+ }
4472
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "underset" && ownerNode.underExpr) {
4473
+ if (direction === "down") {
4474
+ next.caret = {
4475
+ chainId: ownerNode.underExpr.id,
4476
+ index: ownerNode.underExpr.nodes.length
4477
+ };
4478
+ next.selectedStructureNodeId = null;
4479
+ syncSelectionToCaret(next);
4480
+ appendDebug(next, "moveCaretVertical", "underset-base->under");
4481
+ return next;
4482
+ }
4483
+ next.caret = {
4484
+ chainId: ownerLocated.chain.id,
4485
+ index: ownerLocated.index
4486
+ };
4487
+ next.selectedStructureNodeId = null;
4488
+ syncSelectionToCaret(next);
4489
+ appendDebug(next, "moveCaretVertical", "underset-base-up-exit");
4490
+ return next;
4491
+ }
4492
+ if (located.relation.kind === "underExpr" && ownerLocated && ownerNode && ownerNode.baseExpr) {
4493
+ if (direction === "up") {
4494
+ next.caret = {
4495
+ chainId: ownerNode.baseExpr.id,
4496
+ index: ownerNode.baseExpr.nodes.length
4497
+ };
4498
+ next.selectedStructureNodeId = null;
4499
+ syncSelectionToCaret(next);
4500
+ appendDebug(next, "moveCaretVertical", "underset-under->base");
4501
+ return next;
4502
+ }
4503
+ next.caret = {
4504
+ chainId: ownerLocated.chain.id,
4505
+ index: ownerLocated.index + 1
4506
+ };
4507
+ next.selectedStructureNodeId = null;
4508
+ syncSelectionToCaret(next);
4509
+ appendDebug(next, "moveCaretVertical", "underset-under-down-exit");
4510
+ return next;
4511
+ }
4512
+ if (located.relation.kind === "baseExpr" && ownerLocated && ownerNode && ownerNode.kind === "accent") {
4513
+ if (direction === "up") {
4514
+ next.caret = {
4515
+ chainId: ownerLocated.chain.id,
4516
+ index: ownerLocated.index
4517
+ };
4518
+ next.selectedStructureNodeId = null;
4519
+ syncSelectionToCaret(next);
4520
+ appendDebug(next, "moveCaretVertical", "accent-base-up-exit");
4521
+ return next;
4522
+ }
4523
+ next.caret = {
4524
+ chainId: ownerLocated.chain.id,
4525
+ index: ownerLocated.index + 1
4526
+ };
4527
+ next.selectedStructureNodeId = null;
4528
+ syncSelectionToCaret(next);
4529
+ appendDebug(next, "moveCaretVertical", "accent-base-down-exit");
4530
+ return next;
4531
+ }
4000
4532
  if (located.relation.kind === "matrixCell" && ownerLocated && ownerNode?.matrixRows) {
4001
4533
  const rowIndex = located.relation.rowIndex ?? 0;
4002
4534
  const columnIndex = located.relation.columnIndex ?? 0;
@@ -4374,6 +4906,119 @@ function insertSqrt(session) {
4374
4906
  appendDebug(next, "insertSqrt", "");
4375
4907
  return next;
4376
4908
  }
4909
+ function insertOverset(session) {
4910
+ const base = replaceSelectionFirst(session);
4911
+ const next = cloneEditorSession(base);
4912
+ const englishNode = createOversetNode();
4913
+ const arabicNode = createOversetNode();
4914
+ arabicNode.id = englishNode.id;
4915
+ if (arabicNode.overExpr && englishNode.overExpr) {
4916
+ arabicNode.overExpr.id = englishNode.overExpr.id;
4917
+ }
4918
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4919
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4920
+ }
4921
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4922
+ next.caret = {
4923
+ chainId: englishNode.overExpr ? englishNode.overExpr.id : next.caret.chainId,
4924
+ index: 0
4925
+ };
4926
+ next.selectedStructureNodeId = null;
4927
+ syncSelectionToCaret(next);
4928
+ appendDebug(next, "insertOverset", "");
4929
+ return next;
4930
+ }
4931
+ function insertUnderset(session) {
4932
+ const base = replaceSelectionFirst(session);
4933
+ const next = cloneEditorSession(base);
4934
+ const englishNode = createUndersetNode();
4935
+ const arabicNode = createUndersetNode();
4936
+ arabicNode.id = englishNode.id;
4937
+ if (arabicNode.underExpr && englishNode.underExpr) {
4938
+ arabicNode.underExpr.id = englishNode.underExpr.id;
4939
+ }
4940
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4941
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4942
+ }
4943
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
4944
+ next.caret = {
4945
+ chainId: englishNode.underExpr ? englishNode.underExpr.id : next.caret.chainId,
4946
+ index: 0
4947
+ };
4948
+ next.selectedStructureNodeId = null;
4949
+ syncSelectionToCaret(next);
4950
+ appendDebug(next, "insertUnderset", "");
4951
+ return next;
4952
+ }
4953
+ function insertLimitsSeriesTemplate(session, commandId) {
4954
+ const base = replaceSelectionFirst(session);
4955
+ const next = cloneEditorSession(base);
4956
+ const { english, arabic } = buildLimitsSeriesTemplate(commandId);
4957
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
4958
+ syncSubtreeRecursive(next, english, arabic);
4959
+ next.caret = limitsSeriesTemplateInitialCaret(english);
4960
+ next.selectedStructureNodeId = null;
4961
+ syncSelectionToCaret(next);
4962
+ appendDebug(next, "insertLimitsSeriesTemplate", commandId);
4963
+ return next;
4964
+ }
4965
+ function insertAccentPair(session) {
4966
+ const base = replaceSelectionFirst(session);
4967
+ const next = cloneEditorSession(base);
4968
+ const { english, arabic } = buildAccentPairTemplate();
4969
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
4970
+ syncSubtreeRecursive(next, english, arabic);
4971
+ next.caret = accentPairTemplateInitialCaret(english);
4972
+ next.selectedStructureNodeId = null;
4973
+ syncSelectionToCaret(next);
4974
+ appendDebug(next, "insertAccentPair", "");
4975
+ return next;
4976
+ }
4977
+ function insertAccent(session, accentId) {
4978
+ const next = cloneEditorSession(session);
4979
+ const englishNode = createAccentNode(accentId);
4980
+ const arabicNode = createAccentNode(accentId);
4981
+ arabicNode.id = englishNode.id;
4982
+ if (arabicNode.baseExpr && englishNode.baseExpr) {
4983
+ arabicNode.baseExpr.id = englishNode.baseExpr.id;
4984
+ }
4985
+ if (isSelectionActive(next.selection) && next.selection) {
4986
+ const sel = next.selection;
4987
+ const { start, end } = normalizeSelection(sel);
4988
+ const englishLocated = findChainById(next.englishTree, sel.chainId);
4989
+ const arabicLocated = findChainById(next.arabicTree, sel.chainId);
4990
+ if (englishLocated && arabicLocated && end > start) {
4991
+ const count = end - start;
4992
+ const englishWrapped = englishLocated.chain.nodes.splice(start, count);
4993
+ const arabicWrapped = arabicLocated.chain.nodes.splice(start, count);
4994
+ englishNode.baseExpr.nodes = englishWrapped;
4995
+ arabicNode.baseExpr.nodes = arabicWrapped;
4996
+ englishLocated.chain.nodes.splice(start, 0, englishNode);
4997
+ arabicLocated.chain.nodes.splice(start, 0, arabicNode);
4998
+ syncSubtreeRecursive(next, englishNode, arabicNode);
4999
+ next.caret = {
5000
+ chainId: englishNode.baseExpr.id,
5001
+ index: englishWrapped.length
5002
+ };
5003
+ next.selection = null;
5004
+ next.selectedStructureNodeId = null;
5005
+ syncSelectionToCaret(next);
5006
+ appendDebug(next, "insertAccent", `${accentId}:wrap`);
5007
+ return next;
5008
+ }
5009
+ }
5010
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
5011
+ syncSubtreeRecursive(next, englishNode, arabicNode);
5012
+ next.caret = {
5013
+ chainId: englishNode.baseExpr ? englishNode.baseExpr.id : next.caret.chainId,
5014
+ index: 0
5015
+ };
5016
+ next.selection = null;
5017
+ next.selectedStructureNodeId = null;
5018
+ syncSelectionToCaret(next);
5019
+ appendDebug(next, "insertAccent", `${accentId}:empty`);
5020
+ return next;
5021
+ }
4377
5022
  function insertMatrixEnv(session, style, rows, columns) {
4378
5023
  return insertGridEnv(session, style, rows, columns);
4379
5024
  }
@@ -4621,7 +5266,7 @@ function deleteBackward(session) {
4621
5266
  if (!previousNode) {
4622
5267
  return session;
4623
5268
  }
4624
- if ((previousNode.kind === "delimiter" || previousNode.kind === "frac" || previousNode.kind === "sqrt" || previousNode.kind === "env") && next.selectedStructureNodeId !== previousNode.id) {
5269
+ 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) {
4625
5270
  next.selectedStructureNodeId = previousNode.id;
4626
5271
  next.selectedNodeId = previousNode.id;
4627
5272
  appendDebug(next, "deleteBackward", "selected structure");
@@ -5111,6 +5756,168 @@ function buildSqrtNodeBody(node, buildChain, side) {
5111
5756
  return wrap;
5112
5757
  }
5113
5758
 
5759
+ // src/editor/display/overset.ts
5760
+ var OVERSET_CSS = `
5761
+ .overset {
5762
+ display: inline-flex;
5763
+ flex-direction: column;
5764
+ align-items: center;
5765
+ justify-content: center;
5766
+ vertical-align: middle;
5767
+ margin: 0 2px;
5768
+ min-width: 1.2em;
5769
+ }
5770
+
5771
+ .overset-over {
5772
+ display: inline-flex;
5773
+ justify-content: center;
5774
+ align-items: center;
5775
+ min-height: 0.85em;
5776
+ padding: 0 2px;
5777
+ font-size: 0.72em;
5778
+ line-height: 1.1;
5779
+ }
5780
+
5781
+ .overset-base {
5782
+ display: inline-flex;
5783
+ justify-content: center;
5784
+ align-items: center;
5785
+ min-height: 1.05em;
5786
+ padding: 0 2px;
5787
+ }
5788
+ `.trim();
5789
+ function buildOversetNodeBody(node, buildChain) {
5790
+ const wrap = document.createElement("span");
5791
+ wrap.className = "overset";
5792
+ const over = document.createElement("span");
5793
+ over.className = "overset-over";
5794
+ if (node.overExpr) {
5795
+ over.appendChild(buildChain(node.overExpr));
5796
+ }
5797
+ const base = document.createElement("span");
5798
+ base.className = "overset-base";
5799
+ if (node.baseExpr) {
5800
+ base.appendChild(buildChain(node.baseExpr));
5801
+ }
5802
+ wrap.appendChild(over);
5803
+ wrap.appendChild(base);
5804
+ return wrap;
5805
+ }
5806
+
5807
+ // src/editor/display/underset.ts
5808
+ var UNDERSET_CSS = `
5809
+ .underset {
5810
+ display: inline-flex;
5811
+ flex-direction: column;
5812
+ align-items: center;
5813
+ justify-content: center;
5814
+ vertical-align: middle;
5815
+ margin: 0 2px;
5816
+ min-width: 1.2em;
5817
+ }
5818
+
5819
+ .underset-base {
5820
+ display: inline-flex;
5821
+ justify-content: center;
5822
+ align-items: center;
5823
+ min-height: 1.05em;
5824
+ padding: 0 2px;
5825
+ }
5826
+
5827
+ .underset-under {
5828
+ display: inline-flex;
5829
+ justify-content: center;
5830
+ align-items: center;
5831
+ min-height: 0.85em;
5832
+ padding: 0 2px;
5833
+ font-size: 0.72em;
5834
+ line-height: 1.1;
5835
+ }
5836
+ `.trim();
5837
+ function buildUndersetNodeBody(node, buildChain) {
5838
+ const wrap = document.createElement("span");
5839
+ wrap.className = "underset";
5840
+ const base = document.createElement("span");
5841
+ base.className = "underset-base";
5842
+ if (node.baseExpr) {
5843
+ base.appendChild(buildChain(node.baseExpr));
5844
+ }
5845
+ const under = document.createElement("span");
5846
+ under.className = "underset-under";
5847
+ if (node.underExpr) {
5848
+ under.appendChild(buildChain(node.underExpr));
5849
+ }
5850
+ wrap.appendChild(base);
5851
+ wrap.appendChild(under);
5852
+ return wrap;
5853
+ }
5854
+
5855
+ // src/editor/display/accent.ts
5856
+ var ACCENT_CSS = `
5857
+ .math-accent {
5858
+ display: inline-flex;
5859
+ flex-direction: column;
5860
+ align-items: center;
5861
+ justify-content: center;
5862
+ vertical-align: middle;
5863
+ margin: 0 2px;
5864
+ min-width: 1.1em;
5865
+ }
5866
+
5867
+ .math-accent-chrome {
5868
+ display: inline-flex;
5869
+ justify-content: center;
5870
+ align-items: center;
5871
+ min-height: 0.7em;
5872
+ padding: 0 2px;
5873
+ font-size: 0.85em;
5874
+ line-height: 1;
5875
+ color: var(--butex-script-color, #475569);
5876
+ user-select: none;
5877
+ }
5878
+
5879
+ .math-accent-base {
5880
+ display: inline-flex;
5881
+ justify-content: center;
5882
+ align-items: center;
5883
+ min-height: 1.05em;
5884
+ padding: 0 2px;
5885
+ }
5886
+
5887
+ .math-accent--under .math-accent-chrome {
5888
+ min-height: 0.45em;
5889
+ border-top: 1.5px solid currentColor;
5890
+ width: 100%;
5891
+ font-size: 0;
5892
+ color: var(--butex-surface-fg, #0f172a);
5893
+ }
5894
+ `.trim();
5895
+ function buildAccentNodeBody(node, buildChain) {
5896
+ const wrap = document.createElement("span");
5897
+ const spec = accentCommandSpec(node.expr);
5898
+ const placement = spec?.placement ?? "over";
5899
+ wrap.className = placement === "under" ? "math-accent math-accent--under" : "math-accent math-accent--over";
5900
+ const chrome = document.createElement("span");
5901
+ chrome.className = "math-accent-chrome";
5902
+ chrome.setAttribute("aria-hidden", "true");
5903
+ if (placement === "over") {
5904
+ chrome.textContent = spec?.label ?? "\u02C6";
5905
+ }
5906
+ const base = document.createElement("span");
5907
+ base.className = "math-accent-base";
5908
+ if (node.baseExpr) {
5909
+ base.appendChild(buildChain(node.baseExpr));
5910
+ }
5911
+ if (placement === "under") {
5912
+ wrap.appendChild(base);
5913
+ wrap.appendChild(chrome);
5914
+ } else {
5915
+ wrap.appendChild(chrome);
5916
+ wrap.appendChild(base);
5917
+ }
5918
+ return wrap;
5919
+ }
5920
+
5114
5921
  // src/editor/display/env.ts
5115
5922
  var WRAPPERS = {
5116
5923
  matrix: { left: "", right: "" },
@@ -5483,6 +6290,15 @@ function buildNodeBody(node, buildChain, side, digitForm = "western", uiLocale =
5483
6290
  if (node.kind === "sqrt") {
5484
6291
  return buildSqrtNodeBody(node, buildChain, side);
5485
6292
  }
6293
+ if (node.kind === "overset") {
6294
+ return buildOversetNodeBody(node, buildChain);
6295
+ }
6296
+ if (node.kind === "underset") {
6297
+ return buildUndersetNodeBody(node, buildChain);
6298
+ }
6299
+ if (node.kind === "accent") {
6300
+ return buildAccentNodeBody(node, buildChain);
6301
+ }
5486
6302
  if (node.kind === "env") {
5487
6303
  return buildMatrixEnvNodeBody(node, buildChain, side);
5488
6304
  }
@@ -5682,6 +6498,12 @@ ${FRAC_CSS}
5682
6498
 
5683
6499
  ${SQRT_CSS}
5684
6500
 
6501
+ ${OVERSET_CSS}
6502
+
6503
+ ${UNDERSET_CSS}
6504
+
6505
+ ${ACCENT_CSS}
6506
+
5685
6507
  ${MATRIX_ENV_CSS}
5686
6508
 
5687
6509
  ${ATOMIC_COMMAND_CSS}
@@ -6401,6 +7223,26 @@ function createEditorRuntime(options) {
6401
7223
  applyStructural((prev) => insertSqrt(prev));
6402
7224
  surfaceEl.focus();
6403
7225
  },
7226
+ insertOverset: () => {
7227
+ applyStructural((prev) => insertOverset(prev));
7228
+ surfaceEl.focus();
7229
+ },
7230
+ insertUnderset: () => {
7231
+ applyStructural((prev) => insertUnderset(prev));
7232
+ surfaceEl.focus();
7233
+ },
7234
+ insertLimitsSeriesTemplate: (commandId) => {
7235
+ applyStructural((prev) => insertLimitsSeriesTemplate(prev, commandId));
7236
+ surfaceEl.focus();
7237
+ },
7238
+ insertAccentPair: () => {
7239
+ applyStructural((prev) => insertAccentPair(prev));
7240
+ surfaceEl.focus();
7241
+ },
7242
+ insertAccent: (accentId) => {
7243
+ applyStructural((prev) => insertAccent(prev, accentId));
7244
+ surfaceEl.focus();
7245
+ },
6404
7246
  insertAtomicCommand: (commandId) => {
6405
7247
  applyStructural((prev) => insertAtomicCommand(prev, commandId));
6406
7248
  surfaceEl.focus();
@@ -6727,6 +7569,56 @@ function commandToEditorNode(node) {
6727
7569
  }
6728
7570
  return { editable: true, node: sqrt };
6729
7571
  }
7572
+ if (node.name === "\\overset" && node.mandatoryArgs.length >= 2) {
7573
+ const overset = createOversetNode();
7574
+ const overExpr = chainToEditorChain(node.mandatoryArgs[0]);
7575
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
7576
+ if (!overExpr.editable) {
7577
+ return overExpr;
7578
+ }
7579
+ if (!baseExpr.editable) {
7580
+ return baseExpr;
7581
+ }
7582
+ overset.overExpr = overExpr.chain;
7583
+ overset.baseExpr = baseExpr.chain;
7584
+ const scripts = attachScripts(node, overset);
7585
+ if (scripts) {
7586
+ return scripts;
7587
+ }
7588
+ return { editable: true, node: overset };
7589
+ }
7590
+ if (node.name === "\\underset" && node.mandatoryArgs.length >= 2) {
7591
+ const underset = createUndersetNode();
7592
+ const underExpr = chainToEditorChain(node.mandatoryArgs[0]);
7593
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[1]);
7594
+ if (!underExpr.editable) {
7595
+ return underExpr;
7596
+ }
7597
+ if (!baseExpr.editable) {
7598
+ return baseExpr;
7599
+ }
7600
+ underset.underExpr = underExpr.chain;
7601
+ underset.baseExpr = baseExpr.chain;
7602
+ const scripts = attachScripts(node, underset);
7603
+ if (scripts) {
7604
+ return scripts;
7605
+ }
7606
+ return { editable: true, node: underset };
7607
+ }
7608
+ const accentId = accentCommandIdFromTex(node.name);
7609
+ if (accentId && node.mandatoryArgs.length >= 1) {
7610
+ const accent = createAccentNode(accentId);
7611
+ const baseExpr = chainToEditorChain(node.mandatoryArgs[0]);
7612
+ if (!baseExpr.editable) {
7613
+ return baseExpr;
7614
+ }
7615
+ accent.baseExpr = baseExpr.chain;
7616
+ const scripts = attachScripts(node, accent);
7617
+ if (scripts) {
7618
+ return scripts;
7619
+ }
7620
+ return { editable: true, node: accent };
7621
+ }
6730
7622
  const atomicId = commandIdForTex(node.name);
6731
7623
  if (atomicId && node.optionalArgs.length === 0 && node.mandatoryArgs.length === 0) {
6732
7624
  const atomic = createEditorNode("atomicCommand", atomicId);
@@ -6863,6 +7755,21 @@ function editorEnvToAstNode(node) {
6863
7755
  const scripts = attachAstScripts(node, env);
6864
7756
  return scripts ?? { ok: true, node: env };
6865
7757
  }
7758
+ function accentChainForExport(accent) {
7759
+ if (!accent) {
7760
+ return { ok: true, chain: new ChainNode([]) };
7761
+ }
7762
+ return editorChainToAstChain(accent);
7763
+ }
7764
+ function nodeFromAccentBase(command, accent, base) {
7765
+ if (accent.chain.length > 0) {
7766
+ return new CommandNode(command, [], [accent, base]);
7767
+ }
7768
+ if (base.chain.length === 1) {
7769
+ return base.chain[0];
7770
+ }
7771
+ return new DelimiterNode("", base, "");
7772
+ }
6866
7773
  function editorNodeToAstNode(node) {
6867
7774
  let astNode;
6868
7775
  if (node.kind === "char") {
@@ -6901,6 +7808,36 @@ function editorNodeToAstNode(node) {
6901
7808
  optionalArgs.push(rootIndex.chain);
6902
7809
  }
6903
7810
  astNode = new CommandNode("\\sqrt", optionalArgs, [radicand.chain]);
7811
+ } else if (node.kind === "overset" && node.baseExpr) {
7812
+ const accent = accentChainForExport(node.overExpr);
7813
+ if (!accent.ok) {
7814
+ return accent;
7815
+ }
7816
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7817
+ if (!baseExpr.ok) {
7818
+ return baseExpr;
7819
+ }
7820
+ astNode = nodeFromAccentBase("\\overset", accent.chain, baseExpr.chain);
7821
+ } else if (node.kind === "underset" && node.baseExpr) {
7822
+ const accent = accentChainForExport(node.underExpr);
7823
+ if (!accent.ok) {
7824
+ return accent;
7825
+ }
7826
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7827
+ if (!baseExpr.ok) {
7828
+ return baseExpr;
7829
+ }
7830
+ astNode = nodeFromAccentBase("\\underset", accent.chain, baseExpr.chain);
7831
+ } else if (node.kind === "accent" && node.baseExpr) {
7832
+ const tex = accentCommandSpec(node.expr)?.englishTex;
7833
+ if (!tex) {
7834
+ 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}` };
7835
+ }
7836
+ const baseExpr = editorChainToAstChain(node.baseExpr);
7837
+ if (!baseExpr.ok) {
7838
+ return baseExpr;
7839
+ }
7840
+ astNode = new CommandNode(tex, [], [baseExpr.chain]);
6904
7841
  } else if (node.kind === "atomicCommand") {
6905
7842
  const tex = atomicCommandSpec(node.expr)?.englishTex;
6906
7843
  if (!tex) {
@@ -10971,6 +11908,29 @@ var WIDGET_CHROME_CSS = `
10971
11908
  padding-top: 1px;
10972
11909
  }
10973
11910
 
11911
+ .butex-widget .toolbar-accent-icon {
11912
+ display: inline-flex;
11913
+ flex-direction: column;
11914
+ align-items: center;
11915
+ justify-content: center;
11916
+ min-width: 14px;
11917
+ font-size: 10px;
11918
+ line-height: 1;
11919
+ gap: 1px;
11920
+ }
11921
+
11922
+ .butex-widget .toolbar-accent-icon--over span:first-child,
11923
+ .butex-widget .toolbar-accent-icon--under span:last-child {
11924
+ font-size: 8px;
11925
+ opacity: 0.85;
11926
+ }
11927
+
11928
+ .butex-widget .toolbar-accent-icon--pair span:first-child,
11929
+ .butex-widget .toolbar-accent-icon--pair span:last-child {
11930
+ font-size: 8px;
11931
+ opacity: 0.85;
11932
+ }
11933
+
10974
11934
  .butex-widget .matrix-command-menu {
10975
11935
  position: relative;
10976
11936
  display: inline-flex;
@@ -11707,6 +12667,7 @@ var CHARACTER_FONT_OPTIONS = [
11707
12667
  { id: "diwani", label: "\u062F\u064A\u0648" },
11708
12668
  { id: "diwaniOutline", label: "\u0638\u0644" }
11709
12669
  ];
12670
+ var ACCENT_COMMANDS_LIST = accentCommandsList();
11710
12671
  var FUNCTION_COMMANDS = atomicCommandsByCategory("function");
11711
12672
  var LIMITS_SERIES_COMMANDS = atomicCommandsByCategory("limits_series");
11712
12673
  var FUNCTION2_COMMANDS = atomicCommandsByCategory("function2");
@@ -11854,6 +12815,7 @@ var ButexEditor = forwardRef(
11854
12815
  const [operator2MenuOpen, setOperator2MenuOpen] = useState7(false);
11855
12816
  const [operator3MenuOpen, setOperator3MenuOpen] = useState7(false);
11856
12817
  const [dotMenuOpen, setDotMenuOpen] = useState7(false);
12818
+ const [accentMenuOpen, setAccentMenuOpen] = useState7(false);
11857
12819
  const [integralMenuOpen, setIntegralMenuOpen] = useState7(false);
11858
12820
  const [selectedDigitForm2, setSelectedDigitForm] = useState7("western");
11859
12821
  const [selectedCharacterFont, setSelectedCharacterFont] = useState7("default");
@@ -12123,7 +13085,78 @@ ${arabic || currentMessages.empty}`;
12123
13085
  /* @__PURE__ */ jsx12("button", { id: "insert-sqrt", className: "icon-btn", type: "button", title: messages.insertRoot, "aria-label": messages.insertRoot, onClick: () => {
12124
13086
  runtimeRef.current?.insertSqrt();
12125
13087
  focusSurface();
12126
- }, children: /* @__PURE__ */ jsx12("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) })
13088
+ }, children: /* @__PURE__ */ jsx12("span", { className: "toolbar-sqrt-icon", "aria-hidden": "true", children: "\u221A" }) }),
13089
+ /* @__PURE__ */ jsx12("button", { id: "insert-overset", className: "icon-btn", type: "button", title: messages.insertOverset, "aria-label": messages.insertOverset, onClick: () => {
13090
+ runtimeRef.current?.insertOverset();
13091
+ focusSurface();
13092
+ }, children: /* @__PURE__ */ jsxs10("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
13093
+ /* @__PURE__ */ jsx12("span", { children: "\u2217" }),
13094
+ /* @__PURE__ */ jsx12("span", { children: "\u25A1" })
13095
+ ] }) }),
13096
+ /* @__PURE__ */ jsx12("button", { id: "insert-underset", className: "icon-btn", type: "button", title: messages.insertUnderset, "aria-label": messages.insertUnderset, onClick: () => {
13097
+ runtimeRef.current?.insertUnderset();
13098
+ focusSurface();
13099
+ }, children: /* @__PURE__ */ jsxs10("span", { className: "toolbar-accent-icon toolbar-accent-icon--under", "aria-hidden": "true", children: [
13100
+ /* @__PURE__ */ jsx12("span", { children: "\u25A1" }),
13101
+ /* @__PURE__ */ jsx12("span", { children: "\u2217" })
13102
+ ] }) }),
13103
+ /* @__PURE__ */ jsx12("button", { id: "insert-accent-pair", className: "icon-btn", type: "button", title: messages.insertAccentPair, "aria-label": messages.insertAccentPair, onClick: () => {
13104
+ runtimeRef.current?.insertAccentPair();
13105
+ focusSurface();
13106
+ }, children: /* @__PURE__ */ jsxs10("span", { className: "toolbar-accent-icon toolbar-accent-icon--pair", "aria-hidden": "true", children: [
13107
+ /* @__PURE__ */ jsx12("span", { children: "\u2217" }),
13108
+ /* @__PURE__ */ jsx12("span", { children: "\u25A1" }),
13109
+ /* @__PURE__ */ jsx12("span", { children: "\u2217" })
13110
+ ] }) }),
13111
+ /* @__PURE__ */ jsxs10(
13112
+ "div",
13113
+ {
13114
+ className: "function-command-menu operator-command-menu",
13115
+ onBlur: (event) => {
13116
+ const nextFocus = event.relatedTarget;
13117
+ if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) {
13118
+ setAccentMenuOpen(false);
13119
+ }
13120
+ },
13121
+ children: [
13122
+ /* @__PURE__ */ jsx12(
13123
+ "button",
13124
+ {
13125
+ id: "insert-accents",
13126
+ className: "icon-btn function-command-btn operator-command-btn",
13127
+ type: "button",
13128
+ title: messages.insertAccents,
13129
+ "aria-label": messages.insertAccents,
13130
+ "aria-haspopup": "listbox",
13131
+ "aria-expanded": accentMenuOpen,
13132
+ onClick: () => setAccentMenuOpen((open) => !open),
13133
+ children: /* @__PURE__ */ jsxs10("span", { className: "toolbar-accent-icon toolbar-accent-icon--over", "aria-hidden": "true", children: [
13134
+ /* @__PURE__ */ jsx12("span", { children: "\u02C6" }),
13135
+ /* @__PURE__ */ jsx12("span", { children: "\u25A1" })
13136
+ ] })
13137
+ }
13138
+ ),
13139
+ /* @__PURE__ */ jsx12("div", { className: "function-command-options operator-command-options", role: "listbox", "aria-label": messages.insertAccents, hidden: !accentMenuOpen, children: ACCENT_COMMANDS_LIST.map((command) => /* @__PURE__ */ jsx12(
13140
+ "button",
13141
+ {
13142
+ className: "function-command-option operator-command-option",
13143
+ type: "button",
13144
+ role: "option",
13145
+ title: accentCommandTitle(command, uiLocale),
13146
+ "aria-label": accentCommandTitle(command, uiLocale),
13147
+ onMouseDown: (event) => event.preventDefault(),
13148
+ onClick: () => {
13149
+ runtimeRef.current?.insertAccent(command.id);
13150
+ setAccentMenuOpen(false);
13151
+ focusSurface();
13152
+ },
13153
+ children: /* @__PURE__ */ jsx12("span", { className: "operator-command-label", children: command.label })
13154
+ },
13155
+ command.id
13156
+ )) })
13157
+ ]
13158
+ }
13159
+ )
12127
13160
  ] }),
12128
13161
  /* @__PURE__ */ jsx12("div", { className: "toolbar-group toolbar-environments", role: "group", "aria-label": messages.environments, children: /* @__PURE__ */ jsxs10(
12129
13162
  "div",
@@ -12451,7 +13484,7 @@ ${arabic || currentMessages.empty}`;
12451
13484
  "aria-label": atomicCommandTitle(command, uiLocale),
12452
13485
  onMouseDown: (event) => event.preventDefault(),
12453
13486
  onClick: () => {
12454
- runtimeRef.current?.insertAtomicCommand(command.id);
13487
+ runtimeRef.current?.insertLimitsSeriesTemplate(command.id);
12455
13488
  setLimitsSeriesMenuOpen(false);
12456
13489
  focusSurface();
12457
13490
  },