@drghaliasri/butex 5.5.4 → 5.6.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.
@@ -378,6 +378,66 @@ function fromMathObjectJson(json, mode = "english") {
378
378
  closing: json.closing
379
379
  };
380
380
  }
381
+ function scriptsToJson(node, path) {
382
+ return {
383
+ ...node.superscript ? { superscript: chainToJson(node.superscript, `${path}.superscript`) } : {},
384
+ ...node.subscript ? { subscript: chainToJson(node.subscript, `${path}.subscript`) } : {}
385
+ };
386
+ }
387
+ function nodeToJson(node, path) {
388
+ const scripts = scriptsToJson(node, path);
389
+ if (node instanceof CharNode || node instanceof NumberNode || node instanceof OperatorNode) {
390
+ return { node_type: node.nodeType, expr: node.expr, ...scripts };
391
+ }
392
+ if (node instanceof DelimiterNode) {
393
+ return {
394
+ node_type: "DelimiterObject",
395
+ left_delim_expr: node.leftDelimExpr,
396
+ inner_expr: chainToJson(node.innerExpr, `${path}.inner_expr`),
397
+ right_delim_expr: node.rightDelimExpr,
398
+ ...scripts
399
+ };
400
+ }
401
+ if (node instanceof CommandNode) {
402
+ return {
403
+ node_type: "CommandObject",
404
+ name: node.name,
405
+ optional_args: node.optionalArgs.map((arg, index) => chainToJson(arg, `${path}.optional_args[${String(index)}]`)),
406
+ mandatory_args: node.mandatoryArgs.map((arg, index) => chainToJson(arg, `${path}.mandatory_args[${String(index)}]`)),
407
+ ...scripts
408
+ };
409
+ }
410
+ if (node instanceof EnvNode) {
411
+ return {
412
+ node_type: "EnvObject",
413
+ opening: node.opening,
414
+ lines: node.lines.map((line, index) => chainToJson(line, `${path}.lines[${String(index)}]`)),
415
+ closing: node.closing,
416
+ ...scripts
417
+ };
418
+ }
419
+ throw new Error(`Unsupported runtime math node at ${path}: ${node.nodeType}`);
420
+ }
421
+ function chainToJson(chain, path) {
422
+ if (!(chain instanceof ChainNode)) {
423
+ throw new Error(`Expected live ChainNode at ${path}`);
424
+ }
425
+ return {
426
+ node_type: "ChainClass",
427
+ chain: chain.chain.map((node, index) => nodeToJson(node, `${path}.chain[${String(index)}]`))
428
+ };
429
+ }
430
+ function toMathObjectJson(math) {
431
+ if (math.nodeType !== "MathObject" || !Array.isArray(math.lines) || math.lines.length === 0) {
432
+ throw new Error("Cannot serialize invalid runtime MathObject");
433
+ }
434
+ return {
435
+ node_type: "MathObject",
436
+ math_mode: math.mathMode,
437
+ lines: math.lines.map((line, index) => chainToJson(line, `$.lines[${String(index)}]`)),
438
+ closing: math.closing
439
+ };
440
+ }
381
441
  function renderMathNodeLatex(math) {
382
442
  const lines = math.lines.map((line) => line.arabicLatex());
383
443
  if (math.mathMode === "$" || math.mathMode === "\\(") {
@@ -1267,6 +1327,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1267
1327
  category: "operators1",
1268
1328
  Tex: "\\neq",
1269
1329
  mirror: true,
1330
+ compileMirror: true,
1270
1331
  Label: "\u2260",
1271
1332
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
1272
1333
  svg_path: null
@@ -1306,6 +1367,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1306
1367
  Tex: "\\leq",
1307
1368
  mirror: true,
1308
1369
  displayMirror: false,
1370
+ compileMirror: true,
1309
1371
  Label: "\u2264",
1310
1372
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
1311
1373
  svg_path: null
@@ -1316,6 +1378,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1316
1378
  Tex: "\\geq",
1317
1379
  mirror: true,
1318
1380
  displayMirror: false,
1381
+ compileMirror: true,
1319
1382
  Label: "\u2265",
1320
1383
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
1321
1384
  svg_path: null
@@ -1326,6 +1389,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1326
1389
  Tex: "\\coloneqq",
1327
1390
  mirror: true,
1328
1391
  displayMirror: false,
1392
+ compileMirror: true,
1329
1393
  Label: "\u2254",
1330
1394
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
1331
1395
  svg_path: null
@@ -1336,6 +1400,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1336
1400
  Tex: "\\eqqcolon",
1337
1401
  mirror: true,
1338
1402
  displayMirror: false,
1403
+ compileMirror: true,
1339
1404
  Label: "\u2255",
1340
1405
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
1341
1406
  svg_path: null
@@ -1347,6 +1412,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1347
1412
  Tex: "\\propto",
1348
1413
  mirror: true,
1349
1414
  displayMirror: false,
1415
+ compileMirror: true,
1350
1416
  Label: "\u221D",
1351
1417
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
1352
1418
  svg_path: null
@@ -1357,6 +1423,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1357
1423
  Tex: "\\in",
1358
1424
  mirror: true,
1359
1425
  displayMirror: false,
1426
+ compileMirror: true,
1360
1427
  Label: "\u2208",
1361
1428
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
1362
1429
  svg_path: null
@@ -1377,6 +1444,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1377
1444
  Tex: "\\subset",
1378
1445
  mirror: true,
1379
1446
  displayMirror: false,
1447
+ compileMirror: true,
1380
1448
  Label: "\u2282",
1381
1449
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
1382
1450
  svg_path: null
@@ -1387,6 +1455,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1387
1455
  Tex: "\\supset",
1388
1456
  mirror: true,
1389
1457
  displayMirror: false,
1458
+ compileMirror: true,
1390
1459
  Label: "\u2283",
1391
1460
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
1392
1461
  svg_path: null
@@ -1397,6 +1466,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1397
1466
  Tex: "\\subseteq",
1398
1467
  mirror: true,
1399
1468
  displayMirror: false,
1469
+ compileMirror: true,
1400
1470
  Label: "\u2286",
1401
1471
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1402
1472
  svg_path: null
@@ -1407,6 +1477,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1407
1477
  Tex: "\\supseteq",
1408
1478
  mirror: true,
1409
1479
  displayMirror: false,
1480
+ compileMirror: true,
1410
1481
  Label: "\u2287",
1411
1482
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1412
1483
  svg_path: null
@@ -1447,6 +1518,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1447
1518
  Tex: "\\nsubseteq",
1448
1519
  mirror: true,
1449
1520
  displayMirror: false,
1521
+ compileMirror: true,
1450
1522
  Label: "\u2288",
1451
1523
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1452
1524
  svg_path: null
@@ -1457,6 +1529,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1457
1529
  Tex: "\\nsupseteq",
1458
1530
  mirror: true,
1459
1531
  displayMirror: false,
1532
+ compileMirror: true,
1460
1533
  Label: "\u2289",
1461
1534
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1462
1535
  svg_path: null
@@ -1504,6 +1577,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1504
1577
  Tex: "\\ddots",
1505
1578
  mirror: true,
1506
1579
  displayMirror: false,
1580
+ compileMirror: true,
1507
1581
  Label: "\u22F1",
1508
1582
  title: "\u0646\u0642\u0627\u0637 \u0642\u0637\u0631\u064A\u0629",
1509
1583
  svg_path: null
@@ -1523,6 +1597,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1523
1597
  category: "operators3",
1524
1598
  Tex: "\\implies",
1525
1599
  mirror: true,
1600
+ compileMirror: true,
1526
1601
  Label: "\u21D2",
1527
1602
  title: "\u064A\u0633\u062A\u0644\u0632\u0645",
1528
1603
  svg_path: null
@@ -1532,6 +1607,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1532
1607
  category: "operators3",
1533
1608
  Tex: "\\impliedby",
1534
1609
  mirror: true,
1610
+ compileMirror: true,
1535
1611
  Label: "\u21D0",
1536
1612
  title: "\u0645\u0633\u062A\u0644\u0632\u0645 \u0645\u0646",
1537
1613
  svg_path: null
@@ -1550,6 +1626,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1550
1626
  category: "operators3",
1551
1627
  Tex: "\\Longrightarrow",
1552
1628
  mirror: true,
1629
+ compileMirror: true,
1553
1630
  Label: "\u27F9",
1554
1631
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1555
1632
  svg_path: null
@@ -1559,6 +1636,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1559
1636
  category: "operators3",
1560
1637
  Tex: "\\Longleftarrow",
1561
1638
  mirror: true,
1639
+ compileMirror: true,
1562
1640
  Label: "\u27F8",
1563
1641
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1564
1642
  svg_path: null
@@ -1568,6 +1646,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1568
1646
  category: "operators3",
1569
1647
  Tex: "\\to",
1570
1648
  mirror: true,
1649
+ compileMirror: true,
1571
1650
  Label: "\u2192",
1572
1651
  title: "\u064A\u0624\u0648\u0644 \u0625\u0644\u0649",
1573
1652
  svg_path: null
@@ -1577,6 +1656,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1577
1656
  category: "operators3",
1578
1657
  Tex: "\\rightleftharpoons",
1579
1658
  mirror: true,
1659
+ compileMirror: true,
1580
1660
  Label: "\u21CC",
1581
1661
  title: "\u0627\u062A\u0632\u0627\u0646",
1582
1662
  svg_path: null
@@ -1586,6 +1666,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1586
1666
  category: "operators3",
1587
1667
  Tex: "\\leftarrow",
1588
1668
  mirror: true,
1669
+ compileMirror: true,
1589
1670
  Label: "\u2190",
1590
1671
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1591
1672
  svg_path: null
@@ -1595,6 +1676,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1595
1676
  category: "operators3",
1596
1677
  Tex: "\\rightarrow",
1597
1678
  mirror: true,
1679
+ compileMirror: true,
1598
1680
  Label: "\u2192",
1599
1681
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1600
1682
  svg_path: null
@@ -1604,6 +1686,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1604
1686
  category: "operators3",
1605
1687
  Tex: "\\Leftarrow",
1606
1688
  mirror: true,
1689
+ compileMirror: true,
1607
1690
  Label: "\u21D0",
1608
1691
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1609
1692
  svg_path: null
@@ -1613,6 +1696,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1613
1696
  category: "operators3",
1614
1697
  Tex: "\\Rightarrow",
1615
1698
  mirror: true,
1699
+ compileMirror: true,
1616
1700
  Label: "\u21D2",
1617
1701
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1618
1702
  svg_path: null
@@ -1622,6 +1706,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1622
1706
  category: "operators3",
1623
1707
  Tex: "\\leftharpoonup",
1624
1708
  mirror: true,
1709
+ compileMirror: true,
1625
1710
  Label: "\u21BC",
1626
1711
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1627
1712
  svg_path: null
@@ -1631,6 +1716,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1631
1716
  category: "operators3",
1632
1717
  Tex: "\\rightharpoonup",
1633
1718
  mirror: true,
1719
+ compileMirror: true,
1634
1720
  Label: "\u21C0",
1635
1721
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1636
1722
  svg_path: null
@@ -1640,6 +1726,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1640
1726
  category: "operators3",
1641
1727
  Tex: "\\leftharpoondown",
1642
1728
  mirror: true,
1729
+ compileMirror: true,
1643
1730
  Label: "\u21BD",
1644
1731
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1645
1732
  svg_path: null
@@ -1649,6 +1736,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1649
1736
  category: "operators3",
1650
1737
  Tex: "\\rightharpoondown",
1651
1738
  mirror: true,
1739
+ compileMirror: true,
1652
1740
  Label: "\u21C1",
1653
1741
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1654
1742
  svg_path: null
@@ -1659,6 +1747,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1659
1747
  category: "integrals",
1660
1748
  Tex: "\\int",
1661
1749
  mirror: true,
1750
+ compileMirror: true,
1662
1751
  Label: "\u222B",
1663
1752
  title: "\u062A\u0643\u0627\u0645\u0644",
1664
1753
  svg_path: null
@@ -1669,6 +1758,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1669
1758
  Tex: "\\iint",
1670
1759
  mirror: true,
1671
1760
  displayMirror: false,
1761
+ compileMirror: true,
1672
1762
  Label: "\u222C",
1673
1763
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
1674
1764
  svg_path: null
@@ -1679,6 +1769,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1679
1769
  Tex: "\\iiint",
1680
1770
  mirror: true,
1681
1771
  displayMirror: false,
1772
+ compileMirror: true,
1682
1773
  Label: "\u222D",
1683
1774
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
1684
1775
  svg_path: null
@@ -1689,6 +1780,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1689
1780
  Tex: "\\iiiint",
1690
1781
  mirror: true,
1691
1782
  displayMirror: false,
1783
+ compileMirror: true,
1692
1784
  Label: "\u2A0C",
1693
1785
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
1694
1786
  svg_path: null
@@ -1699,6 +1791,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1699
1791
  Tex: "\\oint",
1700
1792
  mirror: true,
1701
1793
  displayMirror: false,
1794
+ compileMirror: true,
1702
1795
  Label: "\u222E",
1703
1796
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
1704
1797
  svg_path: null
@@ -1709,6 +1802,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1709
1802
  Tex: "\\oiint",
1710
1803
  mirror: true,
1711
1804
  displayMirror: false,
1805
+ compileMirror: true,
1712
1806
  Label: "\u222F",
1713
1807
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
1714
1808
  svg_path: null
@@ -1719,6 +1813,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1719
1813
  Tex: "\\oiiint",
1720
1814
  mirror: true,
1721
1815
  displayMirror: false,
1816
+ compileMirror: true,
1722
1817
  Label: "\u2230",
1723
1818
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
1724
1819
  svg_path: null
@@ -1728,8 +1823,9 @@ function shouldMirrorOperatorDisplay(command) {
1728
1823
  return command?.displayMirror ?? command?.mirror ?? false;
1729
1824
  }
1730
1825
  var MIRRORED_OPERATOR_TEX = Object.values(ATOMIC_OPERATOR_COMMANDS).filter((command) => command.mirror).map((command) => command.Tex).sort((a, b) => b.length - a.length);
1731
- function matchingMirroredOperator(tex, index) {
1732
- for (const operatorTex of MIRRORED_OPERATOR_TEX) {
1826
+ var COMPILE_MIRRORED_OPERATOR_TEX = Object.values(ATOMIC_OPERATOR_COMMANDS).filter((command) => "compileMirror" in command && command.compileMirror).map((command) => command.Tex).sort((a, b) => b.length - a.length);
1827
+ function matchingMirroredOperatorFrom(tex, index, operatorTexList) {
1828
+ for (const operatorTex of operatorTexList) {
1733
1829
  if (!tex.startsWith(operatorTex, index)) {
1734
1830
  continue;
1735
1831
  }
@@ -1760,19 +1856,26 @@ function findMatchingBrace(tex, openIndex) {
1760
1856
  }
1761
1857
  return -1;
1762
1858
  }
1763
- function mirrorBuTeXOperatorsInTex(tex) {
1859
+ function mirrorBuTeXOperatorsInTex(tex, options = {}) {
1860
+ const mirroredOperatorTex = options.target === "compile" ? COMPILE_MIRRORED_OPERATOR_TEX : MIRRORED_OPERATOR_TEX;
1764
1861
  let result = "";
1765
1862
  let index = 0;
1766
1863
  while (index < tex.length) {
1767
1864
  if (tex.startsWith("\\butexmirror{", index)) {
1768
1865
  const end = findMatchingBrace(tex, index + "\\butexmirror".length);
1769
1866
  if (end !== -1) {
1770
- result += tex.slice(index, end + 1);
1867
+ const bodyStart = index + "\\butexmirror{".length;
1868
+ const body = tex.slice(bodyStart, end);
1869
+ if (options.target === "compile" && MIRRORED_OPERATOR_TEX.includes(body) && !mirroredOperatorTex.includes(body)) {
1870
+ result += body;
1871
+ } else {
1872
+ result += tex.slice(index, end + 1);
1873
+ }
1771
1874
  index = end + 1;
1772
1875
  continue;
1773
1876
  }
1774
1877
  }
1775
- const operatorTex = matchingMirroredOperator(tex, index);
1878
+ const operatorTex = matchingMirroredOperatorFrom(tex, index, mirroredOperatorTex);
1776
1879
  if (operatorTex) {
1777
1880
  result += `\\butexmirror{${operatorTex}}`;
1778
1881
  index += operatorTex.length;
@@ -8710,6 +8813,11 @@ var WIDGET_CHROME_CSS = `
8710
8813
  font-family: ${BUTEX_DIWANI_OUTLINE_FONT_STACK};
8711
8814
  }
8712
8815
 
8816
+ .butex-widget .font-mode-btn--maghribi,
8817
+ .butex-widget .character-font-option--maghribi {
8818
+ font-family: ${BUTEX_MAGHRIBI_FONT_STACK};
8819
+ }
8820
+
8713
8821
  .butex-widget .character-font-options {
8714
8822
  position: absolute;
8715
8823
  z-index: 10;
@@ -9244,6 +9352,36 @@ var WIDGET_CHROME_CSS = `
9244
9352
  background: var(--butex-dev-inset-bg);
9245
9353
  color: var(--butex-dev-inset-fg);
9246
9354
  }
9355
+
9356
+ .butex-widget .debug-json-import {
9357
+ display: grid;
9358
+ gap: 8px;
9359
+ margin-bottom: 10px;
9360
+ }
9361
+
9362
+ .butex-widget .debug-json-import textarea {
9363
+ min-height: 150px;
9364
+ resize: vertical;
9365
+ border: 1px solid var(--butex-dev-inset-border);
9366
+ border-radius: 8px;
9367
+ padding: 8px 10px;
9368
+ direction: ltr;
9369
+ text-align: left;
9370
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
9371
+ font-size: 11px;
9372
+ background: var(--butex-dev-inset-bg);
9373
+ color: var(--butex-dev-inset-fg);
9374
+ }
9375
+
9376
+ .butex-widget .debug-json-error {
9377
+ margin: 0;
9378
+ border: 1px solid rgba(220, 38, 38, 0.45);
9379
+ background: rgba(254, 226, 226, 0.9);
9380
+ color: #991b1b;
9381
+ border-radius: 8px;
9382
+ padding: 8px 10px;
9383
+ white-space: pre-wrap;
9384
+ }
9247
9385
  `.trim();
9248
9386
  var CHROME_STYLE_ID = "butex-widget-chrome-css";
9249
9387
  function injectWidgetChromeCss(doc) {
@@ -9262,6 +9400,32 @@ function uiLocaleDirection(locale) {
9262
9400
  return locale === "ar" ? "rtl" : "ltr";
9263
9401
  }
9264
9402
 
9403
+ // src/debugJson.ts
9404
+ function lineColumnForPosition(text, position) {
9405
+ const before = text.slice(0, Math.max(0, position));
9406
+ const lines = before.split("\n");
9407
+ return {
9408
+ line: lines.length,
9409
+ column: (lines[lines.length - 1]?.length ?? 0) + 1
9410
+ };
9411
+ }
9412
+ function parseJsonDebugInput(text) {
9413
+ try {
9414
+ return { ok: true, value: JSON.parse(text) };
9415
+ } catch (error) {
9416
+ const message = error instanceof Error ? error.message : String(error);
9417
+ const positionMatch = message.match(/position\s+(\d+)/i);
9418
+ if (positionMatch) {
9419
+ const position = Number(positionMatch[1]);
9420
+ if (Number.isFinite(position)) {
9421
+ const location = lineColumnForPosition(text, position);
9422
+ return { ok: false, error: `Invalid JSON at line ${String(location.line)}, column ${String(location.column)}: ${message}` };
9423
+ }
9424
+ }
9425
+ return { ok: false, error: `Invalid JSON: ${message}` };
9426
+ }
9427
+ }
9428
+
9265
9429
  // src/react/ButexEditor.tsx
9266
9430
  var import_jsx_runtime = require("react/jsx-runtime");
9267
9431
  var DIGIT_FORM_OPTIONS = [
@@ -9377,6 +9541,7 @@ ${latex || messages.empty}`;
9377
9541
  var ButexEditor = (0, import_react.forwardRef)(
9378
9542
  function ButexEditor2({ className, debug = false, defaultSide, uiLocale = "ar", initialSession, onSessionChange }, ref) {
9379
9543
  const messages = equationEditorMessages(uiLocale);
9544
+ const debugEnabled = debug || typeof window !== "undefined" && new URLSearchParams(window.location.search).get("debug") === "1";
9380
9545
  const wrapperRef = (0, import_react.useRef)(null);
9381
9546
  const surfaceRef = (0, import_react.useRef)(null);
9382
9547
  const inputRef = (0, import_react.useRef)(null);
@@ -9394,7 +9559,7 @@ var ButexEditor = (0, import_react.forwardRef)(
9394
9559
  const digitFormBtnRef = (0, import_react.useRef)(null);
9395
9560
  const runtimeRef = (0, import_react.useRef)(null);
9396
9561
  const debugRef = (0, import_react.useRef)(debug);
9397
- debugRef.current = debug;
9562
+ debugRef.current = debugEnabled;
9398
9563
  const uiLocaleRef = (0, import_react.useRef)(uiLocale);
9399
9564
  uiLocaleRef.current = uiLocale;
9400
9565
  const onSessionChangeRef = (0, import_react.useRef)(onSessionChange);
@@ -9402,6 +9567,9 @@ var ButexEditor = (0, import_react.forwardRef)(
9402
9567
  const debugBodyHiddenRef = (0, import_react.useRef)(false);
9403
9568
  const [debugBodyHidden, setDebugBodyHidden] = (0, import_react.useState)(false);
9404
9569
  debugBodyHiddenRef.current = debugBodyHidden;
9570
+ const [debugImportOpen, setDebugImportOpen] = (0, import_react.useState)(false);
9571
+ const [debugImportText, setDebugImportText] = (0, import_react.useState)("");
9572
+ const [debugImportError, setDebugImportError] = (0, import_react.useState)("");
9405
9573
  const [digitMenuOpen, setDigitMenuOpen] = (0, import_react.useState)(false);
9406
9574
  const [characterFontMenuOpen, setCharacterFontMenuOpen] = (0, import_react.useState)(false);
9407
9575
  const [delimiterMenuOpen, setDelimiterMenuOpen] = (0, import_react.useState)(false);
@@ -9482,6 +9650,54 @@ ${arabic || currentMessages.empty}`;
9482
9650
  }, []);
9483
9651
  const updatePreviewRef = (0, import_react.useRef)(updatePreview);
9484
9652
  updatePreviewRef.current = updatePreview;
9653
+ async function copyDebugText(text) {
9654
+ try {
9655
+ await navigator.clipboard.writeText(text);
9656
+ } catch {
9657
+ }
9658
+ }
9659
+ function currentMathObjectJsonText() {
9660
+ const session = runtimeRef.current?.getSession();
9661
+ if (!session) {
9662
+ return "";
9663
+ }
9664
+ const math = mathNodeFromEditorSession(session);
9665
+ if (!math.ok) {
9666
+ return "";
9667
+ }
9668
+ return JSON.stringify(toMathObjectJson(math.math), null, 2);
9669
+ }
9670
+ function openDebugMathObjectImport() {
9671
+ setDebugImportText(currentMathObjectJsonText());
9672
+ setDebugImportError("");
9673
+ setDebugImportOpen(true);
9674
+ }
9675
+ function applyDebugMathObjectImport() {
9676
+ const parsed = parseJsonDebugInput(debugImportText);
9677
+ if (!parsed.ok) {
9678
+ setDebugImportError(parsed.error);
9679
+ return;
9680
+ }
9681
+ try {
9682
+ const math = fromMathObjectJson(parsed.value, activeEditorSide);
9683
+ const result = mathObjectToEditorSession(math);
9684
+ if (!result.editable) {
9685
+ setDebugImportError(result.reason);
9686
+ return;
9687
+ }
9688
+ const next = { ...result.session, activeSide: activeEditorSide };
9689
+ runtimeRef.current?.setSession(next);
9690
+ setSelectedDigitForm(next.currentDigitForm ?? "western");
9691
+ setSelectedCharacterFont(next.currentCharacterFont ?? "default");
9692
+ setActiveEditorSide(next.activeSide);
9693
+ onSessionChangeRef.current?.(next);
9694
+ setDebugImportError("");
9695
+ setDebugImportOpen(false);
9696
+ void updatePreviewRef.current(next);
9697
+ } catch (importError) {
9698
+ setDebugImportError(importError instanceof Error ? importError.message : String(importError));
9699
+ }
9700
+ }
9485
9701
  (0, import_react.useEffect)(() => {
9486
9702
  injectWidgetChromeCss();
9487
9703
  injectBuTeXEditorStyles(typeof document !== "undefined" ? document : void 0);
@@ -10642,7 +10858,7 @@ ${arabic || currentMessages.empty}`;
10642
10858
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: renderErrorRef, className: "error", hidden: true })
10643
10859
  ] }),
10644
10860
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "panel panel-preview", "aria-label": messages.renderPreview, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: mathOutputRef, className: "preview-box" }) }),
10645
- debug ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "panel", children: [
10861
+ debugEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "panel", children: [
10646
10862
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dev-strip", children: [
10647
10863
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dev-badge", children: messages.development }),
10648
10864
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: latexLinesRef, className: "ascii" }),
@@ -10650,6 +10866,8 @@ ${arabic || currentMessages.empty}`;
10650
10866
  ] }),
10651
10867
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 12 }, children: [
10652
10868
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "debug-head", children: [
10869
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => void copyDebugText(currentMathObjectJsonText()), children: "Copy MathObject JSON" }),
10870
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugMathObjectImport, children: "Import MathObject JSON" }),
10653
10871
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10654
10872
  "button",
10655
10873
  {
@@ -10685,6 +10903,35 @@ ${arabic || currentMessages.empty}`;
10685
10903
  }
10686
10904
  )
10687
10905
  ] }),
10906
+ debugImportOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "debug-json-import", children: [
10907
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10908
+ "textarea",
10909
+ {
10910
+ value: debugImportText,
10911
+ "aria-label": "Import MathObject JSON",
10912
+ spellCheck: false,
10913
+ onChange: (event) => {
10914
+ setDebugImportText(event.currentTarget.value);
10915
+ setDebugImportError("");
10916
+ }
10917
+ }
10918
+ ),
10919
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "debug-head", children: [
10920
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: applyDebugMathObjectImport, children: "Apply" }),
10921
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10922
+ "button",
10923
+ {
10924
+ type: "button",
10925
+ onClick: () => {
10926
+ setDebugImportOpen(false);
10927
+ setDebugImportError("");
10928
+ },
10929
+ children: "Cancel"
10930
+ }
10931
+ )
10932
+ ] }),
10933
+ debugImportError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "debug-json-error", children: debugImportError }) : null
10934
+ ] }) : null,
10688
10935
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: debugLogRef, className: "debug-log" })
10689
10936
  ] })
10690
10937
  ] }) : null