@drghaliasri/butex 5.5.5 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -349,6 +349,66 @@ function fromMathObjectJson(json, mode = "english") {
349
349
  closing: json.closing
350
350
  };
351
351
  }
352
+ function scriptsToJson(node, path) {
353
+ return {
354
+ ...node.superscript ? { superscript: chainToJson(node.superscript, `${path}.superscript`) } : {},
355
+ ...node.subscript ? { subscript: chainToJson(node.subscript, `${path}.subscript`) } : {}
356
+ };
357
+ }
358
+ function nodeToJson(node, path) {
359
+ const scripts = scriptsToJson(node, path);
360
+ if (node instanceof CharNode || node instanceof NumberNode || node instanceof OperatorNode) {
361
+ return { node_type: node.nodeType, expr: node.expr, ...scripts };
362
+ }
363
+ if (node instanceof DelimiterNode) {
364
+ return {
365
+ node_type: "DelimiterObject",
366
+ left_delim_expr: node.leftDelimExpr,
367
+ inner_expr: chainToJson(node.innerExpr, `${path}.inner_expr`),
368
+ right_delim_expr: node.rightDelimExpr,
369
+ ...scripts
370
+ };
371
+ }
372
+ if (node instanceof CommandNode) {
373
+ return {
374
+ node_type: "CommandObject",
375
+ name: node.name,
376
+ optional_args: node.optionalArgs.map((arg, index) => chainToJson(arg, `${path}.optional_args[${String(index)}]`)),
377
+ mandatory_args: node.mandatoryArgs.map((arg, index) => chainToJson(arg, `${path}.mandatory_args[${String(index)}]`)),
378
+ ...scripts
379
+ };
380
+ }
381
+ if (node instanceof EnvNode) {
382
+ return {
383
+ node_type: "EnvObject",
384
+ opening: node.opening,
385
+ lines: node.lines.map((line, index) => chainToJson(line, `${path}.lines[${String(index)}]`)),
386
+ closing: node.closing,
387
+ ...scripts
388
+ };
389
+ }
390
+ throw new Error(`Unsupported runtime math node at ${path}: ${node.nodeType}`);
391
+ }
392
+ function chainToJson(chain, path) {
393
+ if (!(chain instanceof ChainNode)) {
394
+ throw new Error(`Expected live ChainNode at ${path}`);
395
+ }
396
+ return {
397
+ node_type: "ChainClass",
398
+ chain: chain.chain.map((node, index) => nodeToJson(node, `${path}.chain[${String(index)}]`))
399
+ };
400
+ }
401
+ function toMathObjectJson(math) {
402
+ if (math.nodeType !== "MathObject" || !Array.isArray(math.lines) || math.lines.length === 0) {
403
+ throw new Error("Cannot serialize invalid runtime MathObject");
404
+ }
405
+ return {
406
+ node_type: "MathObject",
407
+ math_mode: math.mathMode,
408
+ lines: math.lines.map((line, index) => chainToJson(line, `$.lines[${String(index)}]`)),
409
+ closing: math.closing
410
+ };
411
+ }
352
412
  function renderMathNodeLatex(math) {
353
413
  const lines = math.lines.map((line) => line.arabicLatex());
354
414
  if (math.mathMode === "$" || math.mathMode === "\\(") {
@@ -1238,6 +1298,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1238
1298
  category: "operators1",
1239
1299
  Tex: "\\neq",
1240
1300
  mirror: true,
1301
+ compileMirror: true,
1241
1302
  Label: "\u2260",
1242
1303
  title: "\u0644\u0627 \u064A\u0633\u0627\u0648\u064A",
1243
1304
  svg_path: null
@@ -1277,6 +1338,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1277
1338
  Tex: "\\leq",
1278
1339
  mirror: true,
1279
1340
  displayMirror: false,
1341
+ compileMirror: true,
1280
1342
  Label: "\u2264",
1281
1343
  title: "\u0623\u0635\u063A\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
1282
1344
  svg_path: null
@@ -1287,6 +1349,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1287
1349
  Tex: "\\geq",
1288
1350
  mirror: true,
1289
1351
  displayMirror: false,
1352
+ compileMirror: true,
1290
1353
  Label: "\u2265",
1291
1354
  title: "\u0623\u0643\u0628\u0631 \u0645\u0646 \u0623\u0648 \u064A\u0633\u0627\u0648\u064A",
1292
1355
  svg_path: null
@@ -1297,6 +1360,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1297
1360
  Tex: "\\coloneqq",
1298
1361
  mirror: true,
1299
1362
  displayMirror: false,
1363
+ compileMirror: true,
1300
1364
  Label: "\u2254",
1301
1365
  title: "\u064A\u0639\u0631\u0641 \u0628\u0623\u0646\u0647 \u064A\u0633\u0627\u0648\u064A",
1302
1366
  svg_path: null
@@ -1307,6 +1371,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1307
1371
  Tex: "\\eqqcolon",
1308
1372
  mirror: true,
1309
1373
  displayMirror: false,
1374
+ compileMirror: true,
1310
1375
  Label: "\u2255",
1311
1376
  title: "\u064A\u0633\u0627\u0648\u064A \u0628\u0627\u0644\u062A\u0639\u0631\u064A\u0641",
1312
1377
  svg_path: null
@@ -1318,6 +1383,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1318
1383
  Tex: "\\propto",
1319
1384
  mirror: true,
1320
1385
  displayMirror: false,
1386
+ compileMirror: true,
1321
1387
  Label: "\u221D",
1322
1388
  title: "\u064A\u062A\u0646\u0627\u0633\u0628 \u0645\u0639",
1323
1389
  svg_path: null
@@ -1328,6 +1394,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1328
1394
  Tex: "\\in",
1329
1395
  mirror: true,
1330
1396
  displayMirror: false,
1397
+ compileMirror: true,
1331
1398
  Label: "\u2208",
1332
1399
  title: "\u064A\u0646\u062A\u0645\u064A \u0625\u0644\u0649",
1333
1400
  svg_path: null
@@ -1348,6 +1415,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1348
1415
  Tex: "\\subset",
1349
1416
  mirror: true,
1350
1417
  displayMirror: false,
1418
+ compileMirror: true,
1351
1419
  Label: "\u2282",
1352
1420
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0645\u0646",
1353
1421
  svg_path: null
@@ -1358,6 +1426,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1358
1426
  Tex: "\\supset",
1359
1427
  mirror: true,
1360
1428
  displayMirror: false,
1429
+ compileMirror: true,
1361
1430
  Label: "\u2283",
1362
1431
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0644\u0640",
1363
1432
  svg_path: null
@@ -1368,6 +1437,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1368
1437
  Tex: "\\subseteq",
1369
1438
  mirror: true,
1370
1439
  displayMirror: false,
1440
+ compileMirror: true,
1371
1441
  Label: "\u2286",
1372
1442
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1373
1443
  svg_path: null
@@ -1378,6 +1448,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1378
1448
  Tex: "\\supseteq",
1379
1449
  mirror: true,
1380
1450
  displayMirror: false,
1451
+ compileMirror: true,
1381
1452
  Label: "\u2287",
1382
1453
  title: "\u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1383
1454
  svg_path: null
@@ -1418,6 +1489,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1418
1489
  Tex: "\\nsubseteq",
1419
1490
  mirror: true,
1420
1491
  displayMirror: false,
1492
+ compileMirror: true,
1421
1493
  Label: "\u2288",
1422
1494
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u062C\u0632\u0626\u064A\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1423
1495
  svg_path: null
@@ -1428,6 +1500,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1428
1500
  Tex: "\\nsupseteq",
1429
1501
  mirror: true,
1430
1502
  displayMirror: false,
1503
+ compileMirror: true,
1431
1504
  Label: "\u2289",
1432
1505
  title: "\u0644\u064A\u0633\u062A \u0645\u062C\u0645\u0648\u0639\u0629 \u0634\u0627\u0645\u0644\u0629 \u0623\u0648 \u062A\u0633\u0627\u0648\u064A",
1433
1506
  svg_path: null
@@ -1475,6 +1548,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1475
1548
  Tex: "\\ddots",
1476
1549
  mirror: true,
1477
1550
  displayMirror: false,
1551
+ compileMirror: true,
1478
1552
  Label: "\u22F1",
1479
1553
  title: "\u0646\u0642\u0627\u0637 \u0642\u0637\u0631\u064A\u0629",
1480
1554
  svg_path: null
@@ -1494,6 +1568,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1494
1568
  category: "operators3",
1495
1569
  Tex: "\\implies",
1496
1570
  mirror: true,
1571
+ compileMirror: true,
1497
1572
  Label: "\u21D2",
1498
1573
  title: "\u064A\u0633\u062A\u0644\u0632\u0645",
1499
1574
  svg_path: null
@@ -1503,6 +1578,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1503
1578
  category: "operators3",
1504
1579
  Tex: "\\impliedby",
1505
1580
  mirror: true,
1581
+ compileMirror: true,
1506
1582
  Label: "\u21D0",
1507
1583
  title: "\u0645\u0633\u062A\u0644\u0632\u0645 \u0645\u0646",
1508
1584
  svg_path: null
@@ -1521,6 +1597,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1521
1597
  category: "operators3",
1522
1598
  Tex: "\\Longrightarrow",
1523
1599
  mirror: true,
1600
+ compileMirror: true,
1524
1601
  Label: "\u27F9",
1525
1602
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1526
1603
  svg_path: null
@@ -1530,6 +1607,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1530
1607
  category: "operators3",
1531
1608
  Tex: "\\Longleftarrow",
1532
1609
  mirror: true,
1610
+ compileMirror: true,
1533
1611
  Label: "\u27F8",
1534
1612
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0637\u0648\u064A\u0644 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1535
1613
  svg_path: null
@@ -1539,6 +1617,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1539
1617
  category: "operators3",
1540
1618
  Tex: "\\to",
1541
1619
  mirror: true,
1620
+ compileMirror: true,
1542
1621
  Label: "\u2192",
1543
1622
  title: "\u064A\u0624\u0648\u0644 \u0625\u0644\u0649",
1544
1623
  svg_path: null
@@ -1548,6 +1627,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1548
1627
  category: "operators3",
1549
1628
  Tex: "\\rightleftharpoons",
1550
1629
  mirror: true,
1630
+ compileMirror: true,
1551
1631
  Label: "\u21CC",
1552
1632
  title: "\u0627\u062A\u0632\u0627\u0646",
1553
1633
  svg_path: null
@@ -1557,6 +1637,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1557
1637
  category: "operators3",
1558
1638
  Tex: "\\leftarrow",
1559
1639
  mirror: true,
1640
+ compileMirror: true,
1560
1641
  Label: "\u2190",
1561
1642
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1562
1643
  svg_path: null
@@ -1566,6 +1647,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1566
1647
  category: "operators3",
1567
1648
  Tex: "\\rightarrow",
1568
1649
  mirror: true,
1650
+ compileMirror: true,
1569
1651
  Label: "\u2192",
1570
1652
  title: "\u0633\u0647\u0645 \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1571
1653
  svg_path: null
@@ -1575,6 +1657,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1575
1657
  category: "operators3",
1576
1658
  Tex: "\\Leftarrow",
1577
1659
  mirror: true,
1660
+ compileMirror: true,
1578
1661
  Label: "\u21D0",
1579
1662
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1580
1663
  svg_path: null
@@ -1584,6 +1667,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1584
1667
  category: "operators3",
1585
1668
  Tex: "\\Rightarrow",
1586
1669
  mirror: true,
1670
+ compileMirror: true,
1587
1671
  Label: "\u21D2",
1588
1672
  title: "\u0633\u0647\u0645 \u0645\u0632\u062F\u0648\u062C \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1589
1673
  svg_path: null
@@ -1593,6 +1677,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1593
1677
  category: "operators3",
1594
1678
  Tex: "\\leftharpoonup",
1595
1679
  mirror: true,
1680
+ compileMirror: true,
1596
1681
  Label: "\u21BC",
1597
1682
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1598
1683
  svg_path: null
@@ -1602,6 +1687,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1602
1687
  category: "operators3",
1603
1688
  Tex: "\\rightharpoonup",
1604
1689
  mirror: true,
1690
+ compileMirror: true,
1605
1691
  Label: "\u21C0",
1606
1692
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0639\u0644\u0648\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1607
1693
  svg_path: null
@@ -1611,6 +1697,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1611
1697
  category: "operators3",
1612
1698
  Tex: "\\leftharpoondown",
1613
1699
  mirror: true,
1700
+ compileMirror: true,
1614
1701
  Label: "\u21BD",
1615
1702
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631",
1616
1703
  svg_path: null
@@ -1620,6 +1707,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1620
1707
  category: "operators3",
1621
1708
  Tex: "\\rightharpoondown",
1622
1709
  mirror: true,
1710
+ compileMirror: true,
1623
1711
  Label: "\u21C1",
1624
1712
  title: "\u0633\u0647\u0645 \u062E\u0637\u0627\u0641\u064A \u0633\u0641\u0644\u064A \u0625\u0644\u0649 \u0627\u0644\u064A\u0645\u064A\u0646",
1625
1713
  svg_path: null
@@ -1630,6 +1718,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1630
1718
  category: "integrals",
1631
1719
  Tex: "\\int",
1632
1720
  mirror: true,
1721
+ compileMirror: true,
1633
1722
  Label: "\u222B",
1634
1723
  title: "\u062A\u0643\u0627\u0645\u0644",
1635
1724
  svg_path: null
@@ -1640,6 +1729,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1640
1729
  Tex: "\\iint",
1641
1730
  mirror: true,
1642
1731
  displayMirror: false,
1732
+ compileMirror: true,
1643
1733
  Label: "\u222C",
1644
1734
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u0632\u062F\u0648\u062C",
1645
1735
  svg_path: null
@@ -1650,6 +1740,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1650
1740
  Tex: "\\iiint",
1651
1741
  mirror: true,
1652
1742
  displayMirror: false,
1743
+ compileMirror: true,
1653
1744
  Label: "\u222D",
1654
1745
  title: "\u062A\u0643\u0627\u0645\u0644 \u062B\u0644\u0627\u062B\u064A",
1655
1746
  svg_path: null
@@ -1660,6 +1751,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1660
1751
  Tex: "\\iiiint",
1661
1752
  mirror: true,
1662
1753
  displayMirror: false,
1754
+ compileMirror: true,
1663
1755
  Label: "\u2A0C",
1664
1756
  title: "\u062A\u0643\u0627\u0645\u0644 \u0631\u0628\u0627\u0639\u064A",
1665
1757
  svg_path: null
@@ -1670,6 +1762,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1670
1762
  Tex: "\\oint",
1671
1763
  mirror: true,
1672
1764
  displayMirror: false,
1765
+ compileMirror: true,
1673
1766
  Label: "\u222E",
1674
1767
  title: "\u062A\u0643\u0627\u0645\u0644 \u0645\u063A\u0644\u0642",
1675
1768
  svg_path: null
@@ -1680,6 +1773,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1680
1773
  Tex: "\\oiint",
1681
1774
  mirror: true,
1682
1775
  displayMirror: false,
1776
+ compileMirror: true,
1683
1777
  Label: "\u222F",
1684
1778
  title: "\u062A\u0643\u0627\u0645\u0644 \u0633\u0637\u062D\u064A \u0645\u063A\u0644\u0642",
1685
1779
  svg_path: null
@@ -1690,6 +1784,7 @@ var ATOMIC_OPERATOR_COMMANDS = {
1690
1784
  Tex: "\\oiiint",
1691
1785
  mirror: true,
1692
1786
  displayMirror: false,
1787
+ compileMirror: true,
1693
1788
  Label: "\u2230",
1694
1789
  title: "\u062A\u0643\u0627\u0645\u0644 \u062D\u062C\u0645\u064A \u0645\u063A\u0644\u0642",
1695
1790
  svg_path: null
@@ -1699,8 +1794,9 @@ function shouldMirrorOperatorDisplay(command) {
1699
1794
  return command?.displayMirror ?? command?.mirror ?? false;
1700
1795
  }
1701
1796
  var MIRRORED_OPERATOR_TEX = Object.values(ATOMIC_OPERATOR_COMMANDS).filter((command) => command.mirror).map((command) => command.Tex).sort((a, b) => b.length - a.length);
1702
- function matchingMirroredOperator(tex, index) {
1703
- for (const operatorTex of MIRRORED_OPERATOR_TEX) {
1797
+ 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);
1798
+ function matchingMirroredOperatorFrom(tex, index, operatorTexList) {
1799
+ for (const operatorTex of operatorTexList) {
1704
1800
  if (!tex.startsWith(operatorTex, index)) {
1705
1801
  continue;
1706
1802
  }
@@ -1731,19 +1827,26 @@ function findMatchingBrace(tex, openIndex) {
1731
1827
  }
1732
1828
  return -1;
1733
1829
  }
1734
- function mirrorBuTeXOperatorsInTex(tex) {
1830
+ function mirrorBuTeXOperatorsInTex(tex, options = {}) {
1831
+ const mirroredOperatorTex = options.target === "compile" ? COMPILE_MIRRORED_OPERATOR_TEX : MIRRORED_OPERATOR_TEX;
1735
1832
  let result = "";
1736
1833
  let index = 0;
1737
1834
  while (index < tex.length) {
1738
1835
  if (tex.startsWith("\\butexmirror{", index)) {
1739
1836
  const end = findMatchingBrace(tex, index + "\\butexmirror".length);
1740
1837
  if (end !== -1) {
1741
- result += tex.slice(index, end + 1);
1838
+ const bodyStart = index + "\\butexmirror{".length;
1839
+ const body = tex.slice(bodyStart, end);
1840
+ if (options.target === "compile" && MIRRORED_OPERATOR_TEX.includes(body) && !mirroredOperatorTex.includes(body)) {
1841
+ result += body;
1842
+ } else {
1843
+ result += tex.slice(index, end + 1);
1844
+ }
1742
1845
  index = end + 1;
1743
1846
  continue;
1744
1847
  }
1745
1848
  }
1746
- const operatorTex = matchingMirroredOperator(tex, index);
1849
+ const operatorTex = matchingMirroredOperatorFrom(tex, index, mirroredOperatorTex);
1747
1850
  if (operatorTex) {
1748
1851
  result += `\\butexmirror{${operatorTex}}`;
1749
1852
  index += operatorTex.length;
@@ -8688,6 +8791,11 @@ var WIDGET_CHROME_CSS = `
8688
8791
  font-family: ${BUTEX_DIWANI_OUTLINE_FONT_STACK};
8689
8792
  }
8690
8793
 
8794
+ .butex-widget .font-mode-btn--maghribi,
8795
+ .butex-widget .character-font-option--maghribi {
8796
+ font-family: ${BUTEX_MAGHRIBI_FONT_STACK};
8797
+ }
8798
+
8691
8799
  .butex-widget .character-font-options {
8692
8800
  position: absolute;
8693
8801
  z-index: 10;
@@ -9222,6 +9330,36 @@ var WIDGET_CHROME_CSS = `
9222
9330
  background: var(--butex-dev-inset-bg);
9223
9331
  color: var(--butex-dev-inset-fg);
9224
9332
  }
9333
+
9334
+ .butex-widget .debug-json-import {
9335
+ display: grid;
9336
+ gap: 8px;
9337
+ margin-bottom: 10px;
9338
+ }
9339
+
9340
+ .butex-widget .debug-json-import textarea {
9341
+ min-height: 150px;
9342
+ resize: vertical;
9343
+ border: 1px solid var(--butex-dev-inset-border);
9344
+ border-radius: 8px;
9345
+ padding: 8px 10px;
9346
+ direction: ltr;
9347
+ text-align: left;
9348
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
9349
+ font-size: 11px;
9350
+ background: var(--butex-dev-inset-bg);
9351
+ color: var(--butex-dev-inset-fg);
9352
+ }
9353
+
9354
+ .butex-widget .debug-json-error {
9355
+ margin: 0;
9356
+ border: 1px solid rgba(220, 38, 38, 0.45);
9357
+ background: rgba(254, 226, 226, 0.9);
9358
+ color: #991b1b;
9359
+ border-radius: 8px;
9360
+ padding: 8px 10px;
9361
+ white-space: pre-wrap;
9362
+ }
9225
9363
  `.trim();
9226
9364
  var CHROME_STYLE_ID = "butex-widget-chrome-css";
9227
9365
  function injectWidgetChromeCss(doc) {
@@ -9240,6 +9378,32 @@ function uiLocaleDirection(locale) {
9240
9378
  return locale === "ar" ? "rtl" : "ltr";
9241
9379
  }
9242
9380
 
9381
+ // src/debugJson.ts
9382
+ function lineColumnForPosition(text, position) {
9383
+ const before = text.slice(0, Math.max(0, position));
9384
+ const lines = before.split("\n");
9385
+ return {
9386
+ line: lines.length,
9387
+ column: (lines[lines.length - 1]?.length ?? 0) + 1
9388
+ };
9389
+ }
9390
+ function parseJsonDebugInput(text) {
9391
+ try {
9392
+ return { ok: true, value: JSON.parse(text) };
9393
+ } catch (error) {
9394
+ const message = error instanceof Error ? error.message : String(error);
9395
+ const positionMatch = message.match(/position\s+(\d+)/i);
9396
+ if (positionMatch) {
9397
+ const position = Number(positionMatch[1]);
9398
+ if (Number.isFinite(position)) {
9399
+ const location = lineColumnForPosition(text, position);
9400
+ return { ok: false, error: `Invalid JSON at line ${String(location.line)}, column ${String(location.column)}: ${message}` };
9401
+ }
9402
+ }
9403
+ return { ok: false, error: `Invalid JSON: ${message}` };
9404
+ }
9405
+ }
9406
+
9243
9407
  // src/react/ButexEditor.tsx
9244
9408
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9245
9409
  var DIGIT_FORM_OPTIONS = [
@@ -9355,6 +9519,7 @@ ${latex || messages.empty}`;
9355
9519
  var ButexEditor = forwardRef(
9356
9520
  function ButexEditor2({ className, debug = false, defaultSide, uiLocale = "ar", initialSession, onSessionChange }, ref) {
9357
9521
  const messages = equationEditorMessages(uiLocale);
9522
+ const debugEnabled = debug || typeof window !== "undefined" && new URLSearchParams(window.location.search).get("debug") === "1";
9358
9523
  const wrapperRef = useRef(null);
9359
9524
  const surfaceRef = useRef(null);
9360
9525
  const inputRef = useRef(null);
@@ -9372,7 +9537,7 @@ var ButexEditor = forwardRef(
9372
9537
  const digitFormBtnRef = useRef(null);
9373
9538
  const runtimeRef = useRef(null);
9374
9539
  const debugRef = useRef(debug);
9375
- debugRef.current = debug;
9540
+ debugRef.current = debugEnabled;
9376
9541
  const uiLocaleRef = useRef(uiLocale);
9377
9542
  uiLocaleRef.current = uiLocale;
9378
9543
  const onSessionChangeRef = useRef(onSessionChange);
@@ -9380,6 +9545,9 @@ var ButexEditor = forwardRef(
9380
9545
  const debugBodyHiddenRef = useRef(false);
9381
9546
  const [debugBodyHidden, setDebugBodyHidden] = useState(false);
9382
9547
  debugBodyHiddenRef.current = debugBodyHidden;
9548
+ const [debugImportOpen, setDebugImportOpen] = useState(false);
9549
+ const [debugImportText, setDebugImportText] = useState("");
9550
+ const [debugImportError, setDebugImportError] = useState("");
9383
9551
  const [digitMenuOpen, setDigitMenuOpen] = useState(false);
9384
9552
  const [characterFontMenuOpen, setCharacterFontMenuOpen] = useState(false);
9385
9553
  const [delimiterMenuOpen, setDelimiterMenuOpen] = useState(false);
@@ -9460,6 +9628,54 @@ ${arabic || currentMessages.empty}`;
9460
9628
  }, []);
9461
9629
  const updatePreviewRef = useRef(updatePreview);
9462
9630
  updatePreviewRef.current = updatePreview;
9631
+ async function copyDebugText(text) {
9632
+ try {
9633
+ await navigator.clipboard.writeText(text);
9634
+ } catch {
9635
+ }
9636
+ }
9637
+ function currentMathObjectJsonText() {
9638
+ const session = runtimeRef.current?.getSession();
9639
+ if (!session) {
9640
+ return "";
9641
+ }
9642
+ const math = mathNodeFromEditorSession(session);
9643
+ if (!math.ok) {
9644
+ return "";
9645
+ }
9646
+ return JSON.stringify(toMathObjectJson(math.math), null, 2);
9647
+ }
9648
+ function openDebugMathObjectImport() {
9649
+ setDebugImportText(currentMathObjectJsonText());
9650
+ setDebugImportError("");
9651
+ setDebugImportOpen(true);
9652
+ }
9653
+ function applyDebugMathObjectImport() {
9654
+ const parsed = parseJsonDebugInput(debugImportText);
9655
+ if (!parsed.ok) {
9656
+ setDebugImportError(parsed.error);
9657
+ return;
9658
+ }
9659
+ try {
9660
+ const math = fromMathObjectJson(parsed.value, activeEditorSide);
9661
+ const result = mathObjectToEditorSession(math);
9662
+ if (!result.editable) {
9663
+ setDebugImportError(result.reason);
9664
+ return;
9665
+ }
9666
+ const next = { ...result.session, activeSide: activeEditorSide };
9667
+ runtimeRef.current?.setSession(next);
9668
+ setSelectedDigitForm(next.currentDigitForm ?? "western");
9669
+ setSelectedCharacterFont(next.currentCharacterFont ?? "default");
9670
+ setActiveEditorSide(next.activeSide);
9671
+ onSessionChangeRef.current?.(next);
9672
+ setDebugImportError("");
9673
+ setDebugImportOpen(false);
9674
+ void updatePreviewRef.current(next);
9675
+ } catch (importError) {
9676
+ setDebugImportError(importError instanceof Error ? importError.message : String(importError));
9677
+ }
9678
+ }
9463
9679
  useEffect(() => {
9464
9680
  injectWidgetChromeCss();
9465
9681
  injectBuTeXEditorStyles(typeof document !== "undefined" ? document : void 0);
@@ -10620,7 +10836,7 @@ ${arabic || currentMessages.empty}`;
10620
10836
  /* @__PURE__ */ jsx("div", { ref: renderErrorRef, className: "error", hidden: true })
10621
10837
  ] }),
10622
10838
  /* @__PURE__ */ jsx("section", { className: "panel panel-preview", "aria-label": messages.renderPreview, children: /* @__PURE__ */ jsx("div", { ref: mathOutputRef, className: "preview-box" }) }),
10623
- debug ? /* @__PURE__ */ jsxs("section", { className: "panel", children: [
10839
+ debugEnabled ? /* @__PURE__ */ jsxs("section", { className: "panel", children: [
10624
10840
  /* @__PURE__ */ jsxs("div", { className: "dev-strip", children: [
10625
10841
  /* @__PURE__ */ jsx("span", { className: "dev-badge", children: messages.development }),
10626
10842
  /* @__PURE__ */ jsx("div", { ref: latexLinesRef, className: "ascii" }),
@@ -10628,6 +10844,8 @@ ${arabic || currentMessages.empty}`;
10628
10844
  ] }),
10629
10845
  /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
10630
10846
  /* @__PURE__ */ jsxs("div", { className: "debug-head", children: [
10847
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void copyDebugText(currentMathObjectJsonText()), children: "Copy MathObject JSON" }),
10848
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-expanded": debugImportOpen, onClick: openDebugMathObjectImport, children: "Import MathObject JSON" }),
10631
10849
  /* @__PURE__ */ jsx(
10632
10850
  "button",
10633
10851
  {
@@ -10663,6 +10881,35 @@ ${arabic || currentMessages.empty}`;
10663
10881
  }
10664
10882
  )
10665
10883
  ] }),
10884
+ debugImportOpen ? /* @__PURE__ */ jsxs("div", { className: "debug-json-import", children: [
10885
+ /* @__PURE__ */ jsx(
10886
+ "textarea",
10887
+ {
10888
+ value: debugImportText,
10889
+ "aria-label": "Import MathObject JSON",
10890
+ spellCheck: false,
10891
+ onChange: (event) => {
10892
+ setDebugImportText(event.currentTarget.value);
10893
+ setDebugImportError("");
10894
+ }
10895
+ }
10896
+ ),
10897
+ /* @__PURE__ */ jsxs("div", { className: "debug-head", children: [
10898
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: applyDebugMathObjectImport, children: "Apply" }),
10899
+ /* @__PURE__ */ jsx(
10900
+ "button",
10901
+ {
10902
+ type: "button",
10903
+ onClick: () => {
10904
+ setDebugImportOpen(false);
10905
+ setDebugImportError("");
10906
+ },
10907
+ children: "Cancel"
10908
+ }
10909
+ )
10910
+ ] }),
10911
+ debugImportError ? /* @__PURE__ */ jsx("p", { className: "debug-json-error", children: debugImportError }) : null
10912
+ ] }) : null,
10666
10913
  /* @__PURE__ */ jsx("div", { ref: debugLogRef, className: "debug-log" })
10667
10914
  ] })
10668
10915
  ] }) : null