@yurikilian/lex4 1.2.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -515,7 +515,17 @@ const DEFAULT_TRANSLATIONS = {
515
515
  noVariablesFound: "No variables found",
516
516
  insertVariable: "Insert variable {{key}}",
517
517
  openPanel: "Open Variables",
518
- closePanel: "Close Variables"
518
+ closePanel: "Close Variables",
519
+ newVariable: "New variable",
520
+ createVariableTitle: "Create variable",
521
+ createVariableLabel: "Label",
522
+ createVariableKey: "Key",
523
+ createVariableGroup: "Group",
524
+ createVariableType: "Type",
525
+ createVariableSave: "Add variable",
526
+ createVariableCancel: "Cancel",
527
+ createVariableMissingFields: "Label and key are required.",
528
+ createVariableDuplicateKey: "Variable {{key}} already exists."
519
529
  },
520
530
  header: {
521
531
  placeholder: "Header"
@@ -540,7 +550,20 @@ const DEFAULT_TRANSLATIONS = {
540
550
  page: "Page {{page}}"
541
551
  },
542
552
  headerFooter: {
543
- label: "Headers & Footers"
553
+ label: "Headers & Footers",
554
+ settingsLabel: "Header and footer settings",
555
+ pageCounter: "Page counter",
556
+ pageCounterModes: {
557
+ none: "None",
558
+ header: "Header",
559
+ footer: "Footer",
560
+ both: "Both"
561
+ },
562
+ headerSection: "Header",
563
+ footerSection: "Footer",
564
+ copyToAllPages: "Copy to all pages",
565
+ clearThisPage: "Clear this page",
566
+ clearAll: "Clear all"
544
567
  },
545
568
  sidebar: {
546
569
  close: "Close sidebar"
@@ -623,7 +646,17 @@ const PT_BR_TRANSLATIONS = {
623
646
  noVariablesFound: "Nenhuma variável encontrada",
624
647
  insertVariable: "Inserir variável {{key}}",
625
648
  openPanel: "Abrir Variáveis",
626
- closePanel: "Fechar Variáveis"
649
+ closePanel: "Fechar Variáveis",
650
+ newVariable: "Nova variável",
651
+ createVariableTitle: "Criar variável",
652
+ createVariableLabel: "Rótulo",
653
+ createVariableKey: "Chave",
654
+ createVariableGroup: "Grupo",
655
+ createVariableType: "Tipo",
656
+ createVariableSave: "Adicionar variável",
657
+ createVariableCancel: "Cancelar",
658
+ createVariableMissingFields: "Rótulo e chave são obrigatórios.",
659
+ createVariableDuplicateKey: "A variável {{key}} já existe."
627
660
  },
628
661
  header: {
629
662
  placeholder: "Cabeçalho"
@@ -648,7 +681,20 @@ const PT_BR_TRANSLATIONS = {
648
681
  page: "Página {{page}}"
649
682
  },
650
683
  headerFooter: {
651
- label: "Cabeçalhos e Rodapés"
684
+ label: "Cabeçalhos e Rodapés",
685
+ settingsLabel: "Configurações de cabeçalho e rodapé",
686
+ pageCounter: "Contador de páginas",
687
+ pageCounterModes: {
688
+ none: "Nenhum",
689
+ header: "Cabeçalho",
690
+ footer: "Rodapé",
691
+ both: "Ambos"
692
+ },
693
+ headerSection: "Cabeçalho",
694
+ footerSection: "Rodapé",
695
+ copyToAllPages: "Copiar para todas as páginas",
696
+ clearThisPage: "Limpar esta página",
697
+ clearAll: "Limpar tudo"
652
698
  },
653
699
  sidebar: {
654
700
  close: "Fechar barra lateral"
@@ -934,7 +980,7 @@ const DocumentProvider = ({
934
980
  );
935
981
  const activePageIdRef = React.useRef(((_b = initialSnapshot.pages[0]) == null ? void 0 : _b.id) ?? null);
936
982
  const [globalSelectionActive, setGlobalSelectionActive] = React.useState(false);
937
- const [historySidebarOpen, setHistorySidebarOpen] = React.useState(true);
983
+ const [historySidebarOpen, setHistorySidebarOpen] = React.useState(false);
938
984
  const [focusAtEndVersion, setFocusAtEndVersion] = React.useState(0);
939
985
  const t = useTranslations();
940
986
  const activeEditorRef = React.useRef(null);
@@ -1389,33 +1435,33 @@ const createLucideIcon = (iconName, iconNode) => {
1389
1435
  * This source code is licensed under the ISC license.
1390
1436
  * See the LICENSE file in the root directory of this source tree.
1391
1437
  */
1392
- const __iconNode$q = [
1438
+ const __iconNode$p = [
1393
1439
  ["path", { d: "m15 16 2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16", key: "xik6mr" }],
1394
1440
  ["path", { d: "M15.697 14h5.606", key: "1stdlc" }],
1395
1441
  ["path", { d: "m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16", key: "d5nyq2" }],
1396
1442
  ["path", { d: "M3.304 13h6.392", key: "1q3zxz" }]
1397
1443
  ];
1398
- const ALargeSmall = createLucideIcon("a-large-small", __iconNode$q);
1444
+ const ALargeSmall = createLucideIcon("a-large-small", __iconNode$p);
1399
1445
  /**
1400
1446
  * @license lucide-react v1.8.0 - ISC
1401
1447
  *
1402
1448
  * This source code is licensed under the ISC license.
1403
1449
  * See the LICENSE file in the root directory of this source tree.
1404
1450
  */
1405
- const __iconNode$p = [
1451
+ const __iconNode$o = [
1406
1452
  [
1407
1453
  "path",
1408
1454
  { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }
1409
1455
  ]
1410
1456
  ];
1411
- const Bold = createLucideIcon("bold", __iconNode$p);
1457
+ const Bold = createLucideIcon("bold", __iconNode$o);
1412
1458
  /**
1413
1459
  * @license lucide-react v1.8.0 - ISC
1414
1460
  *
1415
1461
  * This source code is licensed under the ISC license.
1416
1462
  * See the LICENSE file in the root directory of this source tree.
1417
1463
  */
1418
- const __iconNode$o = [
1464
+ const __iconNode$n = [
1419
1465
  [
1420
1466
  "path",
1421
1467
  { d: "M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1", key: "ezmyqa" }
@@ -1428,15 +1474,7 @@ const __iconNode$o = [
1428
1474
  }
1429
1475
  ]
1430
1476
  ];
1431
- const Braces = createLucideIcon("braces", __iconNode$o);
1432
- /**
1433
- * @license lucide-react v1.8.0 - ISC
1434
- *
1435
- * This source code is licensed under the ISC license.
1436
- * See the LICENSE file in the root directory of this source tree.
1437
- */
1438
- const __iconNode$n = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
1439
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$n);
1477
+ const Braces = createLucideIcon("braces", __iconNode$n);
1440
1478
  /**
1441
1479
  * @license lucide-react v1.8.0 - ISC
1442
1480
  *
@@ -1474,44 +1512,76 @@ const Eraser = createLucideIcon("eraser", __iconNode$l);
1474
1512
  * See the LICENSE file in the root directory of this source tree.
1475
1513
  */
1476
1514
  const __iconNode$k = [
1515
+ [
1516
+ "path",
1517
+ {
1518
+ d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
1519
+ key: "1oefj6"
1520
+ }
1521
+ ],
1522
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5", key: "wfsgrz" }],
1523
+ ["path", { d: "M10 9H8", key: "b1mrlr" }],
1524
+ ["path", { d: "M16 13H8", key: "t4e002" }],
1525
+ ["path", { d: "M16 17H8", key: "z1uh3a" }]
1526
+ ];
1527
+ const FileText = createLucideIcon("file-text", __iconNode$k);
1528
+ /**
1529
+ * @license lucide-react v1.8.0 - ISC
1530
+ *
1531
+ * This source code is licensed under the ISC license.
1532
+ * See the LICENSE file in the root directory of this source tree.
1533
+ */
1534
+ const __iconNode$j = [
1535
+ ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "1357e3" }],
1536
+ ["path", { d: "M3 3v5h5", key: "1xhq8a" }],
1537
+ ["path", { d: "M12 7v5l4 2", key: "1fdv2h" }]
1538
+ ];
1539
+ const History = createLucideIcon("history", __iconNode$j);
1540
+ /**
1541
+ * @license lucide-react v1.8.0 - ISC
1542
+ *
1543
+ * This source code is licensed under the ISC license.
1544
+ * See the LICENSE file in the root directory of this source tree.
1545
+ */
1546
+ const __iconNode$i = [
1477
1547
  ["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }],
1478
1548
  ["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }],
1479
1549
  ["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }]
1480
1550
  ];
1481
- const Italic = createLucideIcon("italic", __iconNode$k);
1551
+ const Italic = createLucideIcon("italic", __iconNode$i);
1482
1552
  /**
1483
1553
  * @license lucide-react v1.8.0 - ISC
1484
1554
  *
1485
1555
  * This source code is licensed under the ISC license.
1486
1556
  * See the LICENSE file in the root directory of this source tree.
1487
1557
  */
1488
- const __iconNode$j = [
1558
+ const __iconNode$h = [
1489
1559
  ["path", { d: "M21 5H11", key: "us1j55" }],
1490
1560
  ["path", { d: "M21 12H11", key: "wd7e0v" }],
1491
1561
  ["path", { d: "M21 19H11", key: "saa85w" }],
1492
1562
  ["path", { d: "m7 8-4 4 4 4", key: "o5hrat" }]
1493
1563
  ];
1494
- const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$j);
1564
+ const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$h);
1495
1565
  /**
1496
1566
  * @license lucide-react v1.8.0 - ISC
1497
1567
  *
1498
1568
  * This source code is licensed under the ISC license.
1499
1569
  * See the LICENSE file in the root directory of this source tree.
1500
1570
  */
1501
- const __iconNode$i = [
1571
+ const __iconNode$g = [
1502
1572
  ["path", { d: "M21 5H11", key: "us1j55" }],
1503
1573
  ["path", { d: "M21 12H11", key: "wd7e0v" }],
1504
1574
  ["path", { d: "M21 19H11", key: "saa85w" }],
1505
1575
  ["path", { d: "m3 8 4 4-4 4", key: "1a3j6y" }]
1506
1576
  ];
1507
- const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$i);
1577
+ const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$g);
1508
1578
  /**
1509
1579
  * @license lucide-react v1.8.0 - ISC
1510
1580
  *
1511
1581
  * This source code is licensed under the ISC license.
1512
1582
  * See the LICENSE file in the root directory of this source tree.
1513
1583
  */
1514
- const __iconNode$h = [
1584
+ const __iconNode$f = [
1515
1585
  ["path", { d: "M11 5h10", key: "1cz7ny" }],
1516
1586
  ["path", { d: "M11 12h10", key: "1438ji" }],
1517
1587
  ["path", { d: "M11 19h10", key: "11t30w" }],
@@ -1519,14 +1589,14 @@ const __iconNode$h = [
1519
1589
  ["path", { d: "M4 9h2", key: "r1h2o0" }],
1520
1590
  ["path", { d: "M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02", key: "xtkcd5" }]
1521
1591
  ];
1522
- const ListOrdered = createLucideIcon("list-ordered", __iconNode$h);
1592
+ const ListOrdered = createLucideIcon("list-ordered", __iconNode$f);
1523
1593
  /**
1524
1594
  * @license lucide-react v1.8.0 - ISC
1525
1595
  *
1526
1596
  * This source code is licensed under the ISC license.
1527
1597
  * See the LICENSE file in the root directory of this source tree.
1528
1598
  */
1529
- const __iconNode$g = [
1599
+ const __iconNode$e = [
1530
1600
  ["path", { d: "M3 5h.01", key: "18ugdj" }],
1531
1601
  ["path", { d: "M3 12h.01", key: "nlz23k" }],
1532
1602
  ["path", { d: "M3 19h.01", key: "noohij" }],
@@ -1534,54 +1604,31 @@ const __iconNode$g = [
1534
1604
  ["path", { d: "M8 12h13", key: "1za7za" }],
1535
1605
  ["path", { d: "M8 19h13", key: "m83p4d" }]
1536
1606
  ];
1537
- const List = createLucideIcon("list", __iconNode$g);
1607
+ const List = createLucideIcon("list", __iconNode$e);
1538
1608
  /**
1539
1609
  * @license lucide-react v1.8.0 - ISC
1540
1610
  *
1541
1611
  * This source code is licensed under the ISC license.
1542
1612
  * See the LICENSE file in the root directory of this source tree.
1543
1613
  */
1544
- const __iconNode$f = [
1545
- ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
1546
- ["path", { d: "M15 3v18", key: "14nvp0" }]
1547
- ];
1548
- const PanelRight = createLucideIcon("panel-right", __iconNode$f);
1549
- /**
1550
- * @license lucide-react v1.8.0 - ISC
1551
- *
1552
- * This source code is licensed under the ISC license.
1553
- * See the LICENSE file in the root directory of this source tree.
1554
- */
1555
- const __iconNode$e = [
1614
+ const __iconNode$d = [
1556
1615
  ["path", { d: "m15 14 5-5-5-5", key: "12vg1m" }],
1557
1616
  ["path", { d: "M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13", key: "6uklza" }]
1558
1617
  ];
1559
- const Redo2 = createLucideIcon("redo-2", __iconNode$e);
1618
+ const Redo2 = createLucideIcon("redo-2", __iconNode$d);
1560
1619
  /**
1561
1620
  * @license lucide-react v1.8.0 - ISC
1562
1621
  *
1563
1622
  * This source code is licensed under the ISC license.
1564
1623
  * See the LICENSE file in the root directory of this source tree.
1565
1624
  */
1566
- const __iconNode$d = [
1625
+ const __iconNode$c = [
1567
1626
  ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
1568
1627
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
1569
1628
  ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
1570
1629
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
1571
1630
  ];
1572
- const RefreshCw = createLucideIcon("refresh-cw", __iconNode$d);
1573
- /**
1574
- * @license lucide-react v1.8.0 - ISC
1575
- *
1576
- * This source code is licensed under the ISC license.
1577
- * See the LICENSE file in the root directory of this source tree.
1578
- */
1579
- const __iconNode$c = [
1580
- ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
1581
- ["path", { d: "M21 9H3", key: "1338ky" }],
1582
- ["path", { d: "M21 15H3", key: "9uk58r" }]
1583
- ];
1584
- const Rows3 = createLucideIcon("rows-3", __iconNode$c);
1631
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$c);
1585
1632
  /**
1586
1633
  * @license lucide-react v1.8.0 - ISC
1587
1634
  *
@@ -1600,16 +1647,12 @@ const Search = createLucideIcon("search", __iconNode$b);
1600
1647
  * See the LICENSE file in the root directory of this source tree.
1601
1648
  */
1602
1649
  const __iconNode$a = [
1603
- [
1604
- "path",
1605
- {
1606
- d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",
1607
- key: "1i5ecw"
1608
- }
1609
- ],
1610
- ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
1650
+ ["path", { d: "M14 17H5", key: "gfn3mx" }],
1651
+ ["path", { d: "M19 7h-9", key: "6i9tg" }],
1652
+ ["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
1653
+ ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
1611
1654
  ];
1612
- const Settings = createLucideIcon("settings", __iconNode$a);
1655
+ const Settings2 = createLucideIcon("settings-2", __iconNode$a);
1613
1656
  /**
1614
1657
  * @license lucide-react v1.8.0 - ISC
1615
1658
  *
@@ -1833,11 +1876,15 @@ const HistorySidebar = () => {
1833
1876
  "data-history-current": isCurrent ? "true" : "false",
1834
1877
  onClick: () => jumpToHistoryEntry2(actualIndex),
1835
1878
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-history-entry-row", children: [
1879
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-history-entry-dot" }),
1836
1880
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-history-entry-content", children: [
1837
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `lex4-history-entry-label${isCurrent ? " current" : ""}`, children: entry.label }),
1838
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-history-entry-source", children: t.regions[entry.source] ?? entry.source })
1839
- ] }),
1840
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-history-entry-time", children: formatTimestamp(entry.timestamp) })
1881
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-history-entry-label", children: entry.label }),
1882
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-history-entry-meta", children: [
1883
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-history-entry-source", children: t.regions[entry.source] ?? entry.source }),
1884
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-history-entry-meta-dot", children: "·" }),
1885
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-history-entry-time", children: formatTimestamp(entry.timestamp) })
1886
+ ] })
1887
+ ] })
1841
1888
  ] })
1842
1889
  }
1843
1890
  ) }, entry.id);
@@ -1845,201 +1892,12 @@ const HistorySidebar = () => {
1845
1892
  }
1846
1893
  );
1847
1894
  };
1848
- const HeaderFooterToggle = ({
1849
- enabled,
1850
- onToggle
1851
- }) => {
1852
- const t = useTranslations();
1853
- return /* @__PURE__ */ jsxRuntime.jsxs(
1854
- "label",
1855
- {
1856
- className: "lex4-hf-toggle",
1857
- "data-testid": "header-footer-toggle",
1858
- children: [
1859
- /* @__PURE__ */ jsxRuntime.jsx(Rows3, { size: 14, className: "lex4-hf-toggle-icon" }),
1860
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
1861
- /* @__PURE__ */ jsxRuntime.jsx(
1862
- "button",
1863
- {
1864
- type: "button",
1865
- role: "switch",
1866
- "aria-checked": enabled,
1867
- onMouseDown: (e) => e.preventDefault(),
1868
- onClick: () => onToggle(!enabled),
1869
- className: "lex4-hf-switch",
1870
- style: { backgroundColor: enabled ? "var(--lex4-color-primary)" : "var(--lex4-color-text-disabled)" },
1871
- "data-testid": "header-footer-switch",
1872
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-switch-knob" })
1873
- }
1874
- )
1875
- ]
1876
- }
1877
- );
1878
- };
1879
- const PAGE_COUNTER_OPTIONS = [
1880
- { value: "none", label: "None" },
1881
- { value: "header", label: "Header" },
1882
- { value: "footer", label: "Footer" },
1883
- { value: "both", label: "Both" }
1884
- ];
1885
- const HeaderFooterActions = ({
1886
- activePageId,
1887
- pageCounterMode,
1888
- onPageCounterModeChange,
1889
- onCopyHeaderToAll,
1890
- onCopyFooterToAll,
1891
- onClearHeader,
1892
- onClearFooter,
1893
- onClearAllHeaders,
1894
- onClearAllFooters
1895
- }) => {
1896
- const [open, setOpen] = React.useState(false);
1897
- const containerRef = React.useRef(null);
1898
- const hasActivePage = activePageId !== null;
1899
- const close = React.useCallback(() => setOpen(false), []);
1900
- React.useEffect(() => {
1901
- if (!open) return;
1902
- const handleClickOutside = (e) => {
1903
- if (containerRef.current && !containerRef.current.contains(e.target)) {
1904
- close();
1905
- }
1906
- };
1907
- document.addEventListener("mousedown", handleClickOutside);
1908
- return () => document.removeEventListener("mousedown", handleClickOutside);
1909
- }, [open, close]);
1910
- const handleAction = (action) => {
1911
- action();
1912
- close();
1913
- };
1914
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: { position: "relative" }, "data-testid": "header-footer-actions", children: [
1915
- /* @__PURE__ */ jsxRuntime.jsxs(
1916
- "button",
1917
- {
1918
- type: "button",
1919
- onMouseDown: (e) => e.preventDefault(),
1920
- onClick: () => setOpen(!open),
1921
- className: "lex4-settings-trigger",
1922
- "data-testid": "header-footer-menu-trigger",
1923
- "aria-expanded": open,
1924
- "aria-haspopup": "true",
1925
- children: [
1926
- /* @__PURE__ */ jsxRuntime.jsx(Settings, { size: 14 }),
1927
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 12 })
1928
- ]
1929
- }
1930
- ),
1931
- open && /* @__PURE__ */ jsxRuntime.jsxs(
1932
- "div",
1933
- {
1934
- className: "lex4-settings-menu",
1935
- role: "menu",
1936
- "data-testid": "header-footer-menu",
1937
- children: [
1938
- /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "Page counter" }),
1939
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-counter-grid", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsxRuntime.jsx(
1940
- "button",
1941
- {
1942
- type: "button",
1943
- role: "menuitemradio",
1944
- "aria-checked": pageCounterMode === value,
1945
- onMouseDown: (e) => e.preventDefault(),
1946
- onClick: () => onPageCounterModeChange(value),
1947
- className: "lex4-settings-counter-btn",
1948
- "data-testid": `page-counter-${value}`,
1949
- children: label
1950
- },
1951
- value
1952
- )) }),
1953
- /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
1954
- /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "Header" }),
1955
- /* @__PURE__ */ jsxRuntime.jsx(
1956
- MenuItem,
1957
- {
1958
- icon: /* @__PURE__ */ jsxRuntime.jsx(CopyPlus, { size: 14 }),
1959
- label: "Copy to all pages",
1960
- onClick: () => handleAction(onCopyHeaderToAll),
1961
- disabled: !hasActivePage,
1962
- testId: "copy-header-all"
1963
- }
1964
- ),
1965
- /* @__PURE__ */ jsxRuntime.jsx(
1966
- MenuItem,
1967
- {
1968
- icon: /* @__PURE__ */ jsxRuntime.jsx(Eraser, { size: 14 }),
1969
- label: "Clear this page",
1970
- onClick: () => handleAction(onClearHeader),
1971
- disabled: !hasActivePage,
1972
- testId: "clear-header"
1973
- }
1974
- ),
1975
- /* @__PURE__ */ jsxRuntime.jsx(
1976
- MenuItem,
1977
- {
1978
- icon: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { size: 14 }),
1979
- label: "Clear all",
1980
- onClick: () => handleAction(onClearAllHeaders),
1981
- testId: "clear-all-headers"
1982
- }
1983
- ),
1984
- /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
1985
- /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "Footer" }),
1986
- /* @__PURE__ */ jsxRuntime.jsx(
1987
- MenuItem,
1988
- {
1989
- icon: /* @__PURE__ */ jsxRuntime.jsx(CopyPlus, { size: 14 }),
1990
- label: "Copy to all pages",
1991
- onClick: () => handleAction(onCopyFooterToAll),
1992
- disabled: !hasActivePage,
1993
- testId: "copy-footer-all"
1994
- }
1995
- ),
1996
- /* @__PURE__ */ jsxRuntime.jsx(
1997
- MenuItem,
1998
- {
1999
- icon: /* @__PURE__ */ jsxRuntime.jsx(Eraser, { size: 14 }),
2000
- label: "Clear this page",
2001
- onClick: () => handleAction(onClearFooter),
2002
- disabled: !hasActivePage,
2003
- testId: "clear-footer"
2004
- }
2005
- ),
2006
- /* @__PURE__ */ jsxRuntime.jsx(
2007
- MenuItem,
2008
- {
2009
- icon: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { size: 14 }),
2010
- label: "Clear all",
2011
- onClick: () => handleAction(onClearAllFooters),
2012
- testId: "clear-all-footers"
2013
- }
2014
- )
2015
- ]
2016
- }
2017
- )
2018
- ] });
2019
- };
2020
- const SectionLabel = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-label", children });
2021
- const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */ jsxRuntime.jsxs(
2022
- "button",
2023
- {
2024
- type: "button",
2025
- role: "menuitem",
2026
- onMouseDown: (e) => e.preventDefault(),
2027
- onClick,
2028
- disabled,
2029
- className: "lex4-settings-item",
2030
- "data-testid": testId,
2031
- children: [
2032
- icon,
2033
- label
2034
- ]
2035
- }
2036
- );
2037
- const MenuDivider = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-divider" });
2038
1895
  function resolveExtensions(extensions) {
2039
1896
  const resolved = {
2040
1897
  nodes: [],
2041
1898
  bodyPlugins: [],
2042
1899
  toolbarItems: [],
1900
+ toolbarEndItems: [],
2043
1901
  sidePanels: [],
2044
1902
  providers: [],
2045
1903
  themeOverrides: {},
@@ -2051,6 +1909,7 @@ function resolveExtensions(extensions) {
2051
1909
  if (ext.nodes) resolved.nodes.push(...ext.nodes);
2052
1910
  if (ext.bodyPlugins) resolved.bodyPlugins.push(...ext.bodyPlugins);
2053
1911
  if (ext.toolbarItems) resolved.toolbarItems.push(...ext.toolbarItems);
1912
+ if (ext.toolbarEndItems) resolved.toolbarEndItems.push(...ext.toolbarEndItems);
2054
1913
  if (ext.sidePanel) resolved.sidePanels.push(ext.sidePanel);
2055
1914
  if (ext.provider) resolved.providers.push(ext.provider);
2056
1915
  if (ext.themeOverrides) {
@@ -2068,6 +1927,7 @@ const EMPTY_RESOLVED = {
2068
1927
  nodes: [],
2069
1928
  bodyPlugins: [],
2070
1929
  toolbarItems: [],
1930
+ toolbarEndItems: [],
2071
1931
  sidePanels: [],
2072
1932
  providers: [],
2073
1933
  themeOverrides: {},
@@ -2214,11 +2074,285 @@ function indentContent(editor) {
2214
2074
  function outdentContent(editor) {
2215
2075
  editor.dispatchCommand(lexical.OUTDENT_CONTENT_COMMAND, void 0);
2216
2076
  }
2077
+ const HeaderFooterToggle = ({
2078
+ enabled,
2079
+ onToggle
2080
+ }) => {
2081
+ const t = useTranslations();
2082
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2083
+ "label",
2084
+ {
2085
+ className: "lex4-hf-toggle",
2086
+ "data-testid": "header-footer-toggle",
2087
+ children: [
2088
+ /* @__PURE__ */ jsxRuntime.jsx(FileText, { size: 14, className: "lex4-hf-toggle-icon" }),
2089
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
2090
+ /* @__PURE__ */ jsxRuntime.jsx(
2091
+ "button",
2092
+ {
2093
+ type: "button",
2094
+ role: "switch",
2095
+ "aria-checked": enabled,
2096
+ onMouseDown: (e) => e.preventDefault(),
2097
+ onClick: () => onToggle(!enabled),
2098
+ className: "lex4-hf-switch",
2099
+ style: { backgroundColor: enabled ? "var(--color-primary)" : "var(--color-muted)" },
2100
+ "data-testid": "header-footer-switch",
2101
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-switch-knob" })
2102
+ }
2103
+ )
2104
+ ]
2105
+ }
2106
+ );
2107
+ };
2108
+ const PAGE_COUNTER_OPTIONS = ["none", "header", "footer", "both"];
2109
+ const HeaderFooterActions = ({
2110
+ activePageId,
2111
+ pageCounterMode,
2112
+ onPageCounterModeChange,
2113
+ onCopyHeaderToAll,
2114
+ onCopyFooterToAll,
2115
+ onClearHeader,
2116
+ onClearFooter,
2117
+ onClearAllHeaders,
2118
+ onClearAllFooters
2119
+ }) => {
2120
+ const [open, setOpen] = React.useState(false);
2121
+ const containerRef = React.useRef(null);
2122
+ const hasActivePage = activePageId !== null;
2123
+ const t = useTranslations();
2124
+ const close = React.useCallback(() => setOpen(false), []);
2125
+ React.useEffect(() => {
2126
+ if (!open) return;
2127
+ const handleClickOutside = (e) => {
2128
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
2129
+ close();
2130
+ }
2131
+ };
2132
+ document.addEventListener("mousedown", handleClickOutside);
2133
+ return () => document.removeEventListener("mousedown", handleClickOutside);
2134
+ }, [open, close]);
2135
+ const handleAction = (action) => {
2136
+ action();
2137
+ close();
2138
+ };
2139
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: { position: "relative" }, "data-testid": "header-footer-actions", children: [
2140
+ /* @__PURE__ */ jsxRuntime.jsx(
2141
+ "button",
2142
+ {
2143
+ type: "button",
2144
+ onMouseDown: (e) => e.preventDefault(),
2145
+ onClick: () => setOpen(!open),
2146
+ className: "lex4-settings-trigger lex4-settings-trigger-icon",
2147
+ "data-testid": "header-footer-menu-trigger",
2148
+ "aria-expanded": open,
2149
+ "aria-haspopup": "true",
2150
+ "aria-label": t.headerFooter.settingsLabel,
2151
+ title: t.headerFooter.settingsLabel,
2152
+ children: /* @__PURE__ */ jsxRuntime.jsx(Settings2, { size: 14 })
2153
+ }
2154
+ ),
2155
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
2156
+ "div",
2157
+ {
2158
+ className: "lex4-settings-menu",
2159
+ role: "menu",
2160
+ "data-testid": "header-footer-menu",
2161
+ children: [
2162
+ /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: t.headerFooter.pageCounter }),
2163
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-counter-grid", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map((value) => /* @__PURE__ */ jsxRuntime.jsx(
2164
+ "button",
2165
+ {
2166
+ type: "button",
2167
+ role: "menuitemradio",
2168
+ "aria-checked": pageCounterMode === value,
2169
+ onMouseDown: (e) => e.preventDefault(),
2170
+ onClick: () => onPageCounterModeChange(value),
2171
+ className: "lex4-settings-counter-btn",
2172
+ "data-testid": `page-counter-${value}`,
2173
+ children: t.headerFooter.pageCounterModes[value]
2174
+ },
2175
+ value
2176
+ )) }),
2177
+ /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
2178
+ /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: t.headerFooter.headerSection }),
2179
+ /* @__PURE__ */ jsxRuntime.jsx(
2180
+ MenuItem,
2181
+ {
2182
+ icon: /* @__PURE__ */ jsxRuntime.jsx(CopyPlus, { size: 14 }),
2183
+ label: t.headerFooter.copyToAllPages,
2184
+ onClick: () => handleAction(onCopyHeaderToAll),
2185
+ disabled: !hasActivePage,
2186
+ testId: "copy-header-all"
2187
+ }
2188
+ ),
2189
+ /* @__PURE__ */ jsxRuntime.jsx(
2190
+ MenuItem,
2191
+ {
2192
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Eraser, { size: 14 }),
2193
+ label: t.headerFooter.clearThisPage,
2194
+ onClick: () => handleAction(onClearHeader),
2195
+ disabled: !hasActivePage,
2196
+ testId: "clear-header"
2197
+ }
2198
+ ),
2199
+ /* @__PURE__ */ jsxRuntime.jsx(
2200
+ MenuItem,
2201
+ {
2202
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { size: 14 }),
2203
+ label: t.headerFooter.clearAll,
2204
+ onClick: () => handleAction(onClearAllHeaders),
2205
+ testId: "clear-all-headers"
2206
+ }
2207
+ ),
2208
+ /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
2209
+ /* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: t.headerFooter.footerSection }),
2210
+ /* @__PURE__ */ jsxRuntime.jsx(
2211
+ MenuItem,
2212
+ {
2213
+ icon: /* @__PURE__ */ jsxRuntime.jsx(CopyPlus, { size: 14 }),
2214
+ label: t.headerFooter.copyToAllPages,
2215
+ onClick: () => handleAction(onCopyFooterToAll),
2216
+ disabled: !hasActivePage,
2217
+ testId: "copy-footer-all"
2218
+ }
2219
+ ),
2220
+ /* @__PURE__ */ jsxRuntime.jsx(
2221
+ MenuItem,
2222
+ {
2223
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Eraser, { size: 14 }),
2224
+ label: t.headerFooter.clearThisPage,
2225
+ onClick: () => handleAction(onClearFooter),
2226
+ disabled: !hasActivePage,
2227
+ testId: "clear-footer"
2228
+ }
2229
+ ),
2230
+ /* @__PURE__ */ jsxRuntime.jsx(
2231
+ MenuItem,
2232
+ {
2233
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Trash2, { size: 14 }),
2234
+ label: t.headerFooter.clearAll,
2235
+ onClick: () => handleAction(onClearAllFooters),
2236
+ testId: "clear-all-footers"
2237
+ }
2238
+ )
2239
+ ]
2240
+ }
2241
+ )
2242
+ ] });
2243
+ };
2244
+ const SectionLabel = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-label", children });
2245
+ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */ jsxRuntime.jsxs(
2246
+ "button",
2247
+ {
2248
+ type: "button",
2249
+ role: "menuitem",
2250
+ onMouseDown: (e) => e.preventDefault(),
2251
+ onClick,
2252
+ disabled,
2253
+ className: "lex4-settings-item",
2254
+ "data-testid": testId,
2255
+ children: [
2256
+ icon,
2257
+ label
2258
+ ]
2259
+ }
2260
+ );
2261
+ const MenuDivider = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-divider" });
2262
+ const CanvasControls = () => {
2263
+ const { document: document2, dispatch, activePageId, runHistoryAction } = useDocument();
2264
+ const t = useTranslations();
2265
+ const runCanvasAction = React.useCallback((label, callback) => {
2266
+ runHistoryAction(
2267
+ {
2268
+ label,
2269
+ source: "toolbar",
2270
+ region: "document"
2271
+ },
2272
+ callback
2273
+ );
2274
+ }, [runHistoryAction]);
2275
+ const handleToggle = React.useCallback((enabled) => {
2276
+ runCanvasAction(
2277
+ enabled ? t.history.actions.enabledHeadersFooters : t.history.actions.disabledHeadersFooters,
2278
+ () => {
2279
+ dispatch({ type: "SET_HEADER_FOOTER_ENABLED", enabled });
2280
+ }
2281
+ );
2282
+ }, [dispatch, runCanvasAction, t.history.actions.disabledHeadersFooters, t.history.actions.enabledHeadersFooters]);
2283
+ const handleCopyHeaderToAll = React.useCallback(() => {
2284
+ if (!activePageId) {
2285
+ return;
2286
+ }
2287
+ runCanvasAction(t.history.actions.copiedHeaderToAll, () => {
2288
+ dispatch({ type: "COPY_HEADER_TO_ALL", sourcePageId: activePageId });
2289
+ });
2290
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.copiedHeaderToAll]);
2291
+ const handleCopyFooterToAll = React.useCallback(() => {
2292
+ if (!activePageId) {
2293
+ return;
2294
+ }
2295
+ runCanvasAction(t.history.actions.copiedFooterToAll, () => {
2296
+ dispatch({ type: "COPY_FOOTER_TO_ALL", sourcePageId: activePageId });
2297
+ });
2298
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.copiedFooterToAll]);
2299
+ const handleClearHeader = React.useCallback(() => {
2300
+ if (!activePageId) {
2301
+ return;
2302
+ }
2303
+ runCanvasAction(t.history.actions.clearedHeader, () => {
2304
+ dispatch({ type: "CLEAR_HEADER", pageId: activePageId });
2305
+ });
2306
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.clearedHeader]);
2307
+ const handleClearFooter = React.useCallback(() => {
2308
+ if (!activePageId) {
2309
+ return;
2310
+ }
2311
+ runCanvasAction(t.history.actions.clearedFooter, () => {
2312
+ dispatch({ type: "CLEAR_FOOTER", pageId: activePageId });
2313
+ });
2314
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.clearedFooter]);
2315
+ const handleClearAllHeaders = React.useCallback(() => {
2316
+ runCanvasAction(t.history.actions.clearedAllHeaders, () => {
2317
+ dispatch({ type: "CLEAR_ALL_HEADERS" });
2318
+ });
2319
+ }, [dispatch, runCanvasAction, t.history.actions.clearedAllHeaders]);
2320
+ const handleClearAllFooters = React.useCallback(() => {
2321
+ runCanvasAction(t.history.actions.clearedAllFooters, () => {
2322
+ dispatch({ type: "CLEAR_ALL_FOOTERS" });
2323
+ });
2324
+ }, [dispatch, runCanvasAction, t.history.actions.clearedAllFooters]);
2325
+ const handlePageCounterModeChange = React.useCallback((mode) => {
2326
+ runCanvasAction(interpolate(t.history.actions.pageCounterSet, { value: mode }), () => {
2327
+ dispatch({ type: "SET_PAGE_COUNTER_MODE", mode });
2328
+ });
2329
+ }, [dispatch, runCanvasAction, t.history.actions.pageCounterSet]);
2330
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group lex4-toolbar-group-gap", "data-testid": "header-footer-controls", children: [
2331
+ /* @__PURE__ */ jsxRuntime.jsx(
2332
+ HeaderFooterToggle,
2333
+ {
2334
+ enabled: document2.headerFooterEnabled,
2335
+ onToggle: handleToggle
2336
+ }
2337
+ ),
2338
+ document2.headerFooterEnabled && /* @__PURE__ */ jsxRuntime.jsx(
2339
+ HeaderFooterActions,
2340
+ {
2341
+ activePageId,
2342
+ pageCounterMode: document2.pageCounterMode,
2343
+ onPageCounterModeChange: handlePageCounterModeChange,
2344
+ onCopyHeaderToAll: handleCopyHeaderToAll,
2345
+ onCopyFooterToAll: handleCopyFooterToAll,
2346
+ onClearHeader: handleClearHeader,
2347
+ onClearFooter: handleClearFooter,
2348
+ onClearAllHeaders: handleClearAllHeaders,
2349
+ onClearAllFooters: handleClearAllFooters
2350
+ }
2351
+ )
2352
+ ] });
2353
+ };
2217
2354
  const Toolbar = () => {
2218
2355
  const {
2219
- document: document2,
2220
- dispatch,
2221
- activePageId,
2222
2356
  activeEditor,
2223
2357
  canRedo,
2224
2358
  canUndo,
@@ -2230,7 +2364,7 @@ const Toolbar = () => {
2230
2364
  setHistorySidebarOpen,
2231
2365
  undo
2232
2366
  } = useDocument();
2233
- const { toolbarItems } = useExtensions();
2367
+ const { toolbarItems, toolbarEndItems } = useExtensions();
2234
2368
  const t = useTranslations();
2235
2369
  const withBodySelection = React.useCallback(
2236
2370
  (editor, action) => {
@@ -2267,53 +2401,6 @@ const Toolbar = () => {
2267
2401
  },
2268
2402
  [runHistoryAction]
2269
2403
  );
2270
- const handleToggle = (enabled) => {
2271
- runToolbarAction(
2272
- enabled ? t.history.actions.enabledHeadersFooters : t.history.actions.disabledHeadersFooters,
2273
- () => {
2274
- dispatch({ type: "SET_HEADER_FOOTER_ENABLED", enabled });
2275
- }
2276
- );
2277
- };
2278
- const handleCopyHeaderToAll = () => {
2279
- if (activePageId) {
2280
- runToolbarAction(t.history.actions.copiedHeaderToAll, () => {
2281
- dispatch({ type: "COPY_HEADER_TO_ALL", sourcePageId: activePageId });
2282
- });
2283
- }
2284
- };
2285
- const handleCopyFooterToAll = () => {
2286
- if (activePageId) {
2287
- runToolbarAction(t.history.actions.copiedFooterToAll, () => {
2288
- dispatch({ type: "COPY_FOOTER_TO_ALL", sourcePageId: activePageId });
2289
- });
2290
- }
2291
- };
2292
- const handleClearHeader = () => {
2293
- if (activePageId) {
2294
- runToolbarAction(t.history.actions.clearedHeader, () => {
2295
- dispatch({ type: "CLEAR_HEADER", pageId: activePageId });
2296
- });
2297
- }
2298
- };
2299
- const handleClearFooter = () => {
2300
- if (activePageId) {
2301
- runToolbarAction(t.history.actions.clearedFooter, () => {
2302
- dispatch({ type: "CLEAR_FOOTER", pageId: activePageId });
2303
- });
2304
- }
2305
- };
2306
- const handleClearAllHeaders = () => runToolbarAction(t.history.actions.clearedAllHeaders, () => {
2307
- dispatch({ type: "CLEAR_ALL_HEADERS" });
2308
- });
2309
- const handleClearAllFooters = () => runToolbarAction(t.history.actions.clearedAllFooters, () => {
2310
- dispatch({ type: "CLEAR_ALL_FOOTERS" });
2311
- });
2312
- const handlePageCounterModeChange = React.useCallback((mode) => {
2313
- runToolbarAction(interpolate(t.history.actions.pageCounterSet, { value: mode }), () => {
2314
- dispatch({ type: "SET_PAGE_COUNTER_MODE", mode });
2315
- });
2316
- }, [dispatch, runToolbarAction, t.history.actions.pageCounterSet]);
2317
2404
  const handleBold = React.useCallback(() => {
2318
2405
  debug("toolbar", `bold (globalSelection=${globalSelectionActive}, editors=${editorRegistry.all().length}, hasEditor=${!!activeEditor})`);
2319
2406
  runToolbarAction(t.history.actions.boldApplied, () => {
@@ -2395,122 +2482,111 @@ const Toolbar = () => {
2395
2482
  },
2396
2483
  [applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
2397
2484
  );
2398
- return /* @__PURE__ */ jsxRuntime.jsxs(
2485
+ const handleToggleHistory = React.useCallback(() => {
2486
+ setHistorySidebarOpen(!historySidebarOpen);
2487
+ }, [historySidebarOpen, setHistorySidebarOpen]);
2488
+ return /* @__PURE__ */ jsxRuntime.jsx(
2399
2489
  "div",
2400
2490
  {
2401
2491
  className: "lex4-toolbar",
2402
2492
  "data-testid": "toolbar",
2403
- children: [
2404
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-row", children: [
2405
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "history-controls", children: [
2406
- /* @__PURE__ */ jsxRuntime.jsx(
2407
- ToolbarIconButton,
2408
- {
2409
- title: t.toolbar.undo,
2410
- testId: "btn-undo",
2411
- disabled: !canUndo,
2412
- onClick: undo,
2413
- children: /* @__PURE__ */ jsxRuntime.jsx(Undo2, { size: 15 })
2414
- }
2415
- ),
2416
- /* @__PURE__ */ jsxRuntime.jsx(
2417
- ToolbarIconButton,
2418
- {
2419
- title: t.toolbar.redo,
2420
- testId: "btn-redo",
2421
- disabled: !canRedo,
2422
- onClick: redo,
2423
- children: /* @__PURE__ */ jsxRuntime.jsx(Redo2, { size: 15 })
2424
- }
2425
- )
2426
- ] }),
2427
- /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2428
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2429
- /* @__PURE__ */ jsxRuntime.jsx(Type, { size: 14, className: "lex4-toolbar-icon" }),
2430
- /* @__PURE__ */ jsxRuntime.jsx(
2431
- "select",
2432
- {
2433
- className: "lex4-toolbar-select",
2434
- "data-testid": "font-selector",
2435
- defaultValue: "Calibri",
2436
- onChange: handleFontChange,
2437
- children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
2438
- }
2439
- )
2440
- ] }),
2441
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2442
- /* @__PURE__ */ jsxRuntime.jsx(ALargeSmall, { size: 14, className: "lex4-toolbar-icon" }),
2443
- /* @__PURE__ */ jsxRuntime.jsx(
2444
- "select",
2445
- {
2446
- className: "lex4-toolbar-select lex4-toolbar-select-narrow",
2447
- "data-testid": "font-size-selector",
2448
- defaultValue: "12",
2449
- onChange: handleFontSizeChange,
2450
- children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: size, children: size }, size))
2451
- }
2452
- )
2453
- ] }),
2454
- /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2455
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "format-group", children: [
2456
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsxRuntime.jsx(Bold, { size: 15 }) }),
2457
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsxRuntime.jsx(Italic, { size: 15 }) }),
2458
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsxRuntime.jsx(Underline, { size: 15 }) }),
2459
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsxRuntime.jsx(Strikethrough, { size: 15 }) })
2460
- ] }),
2461
- /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2462
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "align-group", children: [
2463
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignStart, { size: 15 }) }),
2464
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignCenter, { size: 15 }) }),
2465
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignEnd, { size: 15 }) }),
2466
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignJustify, { size: 15 }) })
2467
- ] }),
2468
- /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2469
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "list-group", children: [
2470
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
2471
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
2472
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentIncrease, { size: 15 }) }),
2473
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentDecrease, { size: 15 }) })
2474
- ] }),
2475
- toolbarItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2476
- /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2477
- toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(ToolbarItem, {}, idx))
2478
- ] }),
2479
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-toolbar-end", children: /* @__PURE__ */ jsxRuntime.jsx(
2493
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-row", children: [
2494
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "history-controls", children: [
2495
+ /* @__PURE__ */ jsxRuntime.jsx(
2480
2496
  ToolbarIconButton,
2481
2497
  {
2482
- title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2483
- testId: "toggle-history-sidebar",
2484
- active: historySidebarOpen,
2485
- onClick: () => setHistorySidebarOpen(!historySidebarOpen),
2486
- children: /* @__PURE__ */ jsxRuntime.jsx(PanelRight, { size: 15 })
2498
+ title: t.toolbar.undo,
2499
+ testId: "btn-undo",
2500
+ disabled: !canUndo,
2501
+ onClick: undo,
2502
+ children: /* @__PURE__ */ jsxRuntime.jsx(Undo2, { size: 15 })
2503
+ }
2504
+ ),
2505
+ /* @__PURE__ */ jsxRuntime.jsx(
2506
+ ToolbarIconButton,
2507
+ {
2508
+ title: t.toolbar.redo,
2509
+ testId: "btn-redo",
2510
+ disabled: !canRedo,
2511
+ onClick: redo,
2512
+ children: /* @__PURE__ */ jsxRuntime.jsx(Redo2, { size: 15 })
2487
2513
  }
2488
- ) })
2514
+ )
2489
2515
  ] }),
2490
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-hf-row", children: [
2516
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2517
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2518
+ /* @__PURE__ */ jsxRuntime.jsx(Type, { size: 14, className: "lex4-toolbar-icon" }),
2491
2519
  /* @__PURE__ */ jsxRuntime.jsx(
2492
- HeaderFooterToggle,
2520
+ "select",
2493
2521
  {
2494
- enabled: document2.headerFooterEnabled,
2495
- onToggle: handleToggle
2522
+ className: "lex4-toolbar-select",
2523
+ "data-testid": "font-selector",
2524
+ defaultValue: "Calibri",
2525
+ onChange: handleFontChange,
2526
+ children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
2496
2527
  }
2497
- ),
2498
- document2.headerFooterEnabled && /* @__PURE__ */ jsxRuntime.jsx(
2499
- HeaderFooterActions,
2528
+ )
2529
+ ] }),
2530
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2531
+ /* @__PURE__ */ jsxRuntime.jsx(ALargeSmall, { size: 14, className: "lex4-toolbar-icon" }),
2532
+ /* @__PURE__ */ jsxRuntime.jsx(
2533
+ "select",
2534
+ {
2535
+ className: "lex4-toolbar-select lex4-toolbar-select-narrow",
2536
+ "data-testid": "font-size-selector",
2537
+ defaultValue: "12",
2538
+ onChange: handleFontSizeChange,
2539
+ children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: size, children: size }, size))
2540
+ }
2541
+ )
2542
+ ] }),
2543
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2544
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "format-group", children: [
2545
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsxRuntime.jsx(Bold, { size: 15 }) }),
2546
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsxRuntime.jsx(Italic, { size: 15 }) }),
2547
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsxRuntime.jsx(Underline, { size: 15 }) }),
2548
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsxRuntime.jsx(Strikethrough, { size: 15 }) })
2549
+ ] }),
2550
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2551
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "align-group", children: [
2552
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignStart, { size: 15 }) }),
2553
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignCenter, { size: 15 }) }),
2554
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignEnd, { size: 15 }) }),
2555
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignJustify, { size: 15 }) })
2556
+ ] }),
2557
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2558
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "list-group", children: [
2559
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
2560
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
2561
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentIncrease, { size: 15 }) }),
2562
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentDecrease, { size: 15 }) })
2563
+ ] }),
2564
+ toolbarItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2565
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2566
+ toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(ToolbarItem, {}, idx))
2567
+ ] }),
2568
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2569
+ /* @__PURE__ */ jsxRuntime.jsx(CanvasControls, {}),
2570
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-end", children: [
2571
+ toolbarEndItems.map((EndItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(EndItem, {}, idx)),
2572
+ /* @__PURE__ */ jsxRuntime.jsxs(
2573
+ "button",
2500
2574
  {
2501
- activePageId,
2502
- pageCounterMode: document2.pageCounterMode,
2503
- onPageCounterModeChange: handlePageCounterModeChange,
2504
- onCopyHeaderToAll: handleCopyHeaderToAll,
2505
- onCopyFooterToAll: handleCopyFooterToAll,
2506
- onClearHeader: handleClearHeader,
2507
- onClearFooter: handleClearFooter,
2508
- onClearAllHeaders: handleClearAllHeaders,
2509
- onClearAllFooters: handleClearAllFooters
2575
+ type: "button",
2576
+ className: `lex4-toolbar-toggle-btn${historySidebarOpen ? " active" : ""}`,
2577
+ onMouseDown: (e) => e.preventDefault(),
2578
+ onClick: handleToggleHistory,
2579
+ "data-testid": "toggle-history-sidebar",
2580
+ title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2581
+ "aria-label": historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2582
+ children: [
2583
+ /* @__PURE__ */ jsxRuntime.jsx(History, { size: 14 }),
2584
+ "History"
2585
+ ]
2510
2586
  }
2511
2587
  )
2512
2588
  ] })
2513
- ]
2589
+ ] })
2514
2590
  }
2515
2591
  );
2516
2592
  };
@@ -4630,8 +4706,8 @@ function selectEntireDocument(rootElement, selectionBuffer) {
4630
4706
  selectionBuffer.focus();
4631
4707
  selectionBuffer.select();
4632
4708
  }
4633
- const GLOBAL_SELECTION_BACKGROUND = "rgb(191, 219, 254)";
4634
- const GLOBAL_SELECTION_FOREGROUND = "rgb(30, 64, 175)";
4709
+ const GLOBAL_SELECTION_BACKGROUND = "hsl(214 95% 87%)";
4710
+ const GLOBAL_SELECTION_FOREGROUND = "hsl(224 71% 25%)";
4635
4711
  function isFormFieldTarget(target) {
4636
4712
  const element = target;
4637
4713
  const tagName = element == null ? void 0 : element.tagName;
@@ -4747,8 +4823,8 @@ const EditorChrome = ({
4747
4823
  )) ?? [];
4748
4824
  const root = rootRef.current;
4749
4825
  const styles = root ? getComputedStyle(root) : null;
4750
- const selBg = (styles == null ? void 0 : styles.getPropertyValue("--lex4-color-selection-bg").trim()) || GLOBAL_SELECTION_BACKGROUND;
4751
- const selFg = (styles == null ? void 0 : styles.getPropertyValue("--lex4-color-selection-text").trim()) || GLOBAL_SELECTION_FOREGROUND;
4826
+ const selBg = (styles == null ? void 0 : styles.getPropertyValue("--color-selection-bg").trim()) || GLOBAL_SELECTION_BACKGROUND;
4827
+ const selFg = (styles == null ? void 0 : styles.getPropertyValue("--color-selection-text").trim()) || GLOBAL_SELECTION_FOREGROUND;
4752
4828
  editableRoots.forEach((editableRoot) => {
4753
4829
  editableRoot.style.backgroundColor = globalSelectionActive ? selBg : "";
4754
4830
  editableRoot.style.color = globalSelectionActive ? selFg : "";
@@ -4819,19 +4895,31 @@ const EditorChrome = ({
4819
4895
  );
4820
4896
  };
4821
4897
  const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, onSave, className }, ref) => {
4822
- const { document: doc, activeEditor } = useDocument();
4898
+ const {
4899
+ document: doc,
4900
+ activeEditor,
4901
+ historySidebarOpen,
4902
+ setHistorySidebarOpen
4903
+ } = useDocument();
4823
4904
  const { handleFactories } = useExtensions();
4824
4905
  const getDocument = React.useCallback(() => doc, [doc]);
4825
4906
  const getActiveEditor = React.useCallback(() => activeEditor, [activeEditor]);
4826
4907
  const extensionCtx = useExtensionContext(getDocument, getActiveEditor);
4827
4908
  React.useImperativeHandle(ref, () => {
4828
- const handle = {};
4909
+ const handle = {
4910
+ setHistorySidebarOpen: (open) => {
4911
+ setHistorySidebarOpen(open);
4912
+ },
4913
+ toggleHistorySidebar: () => {
4914
+ setHistorySidebarOpen(!historySidebarOpen);
4915
+ }
4916
+ };
4829
4917
  for (const factory of handleFactories) {
4830
4918
  const methods = factory(extensionCtx);
4831
4919
  Object.assign(handle, methods);
4832
4920
  }
4833
4921
  return handle;
4834
- }, [extensionCtx, handleFactories]);
4922
+ }, [extensionCtx, handleFactories, historySidebarOpen, setHistorySidebarOpen]);
4835
4923
  return /* @__PURE__ */ jsxRuntime.jsx(
4836
4924
  EditorChrome,
4837
4925
  {
@@ -5053,14 +5141,16 @@ class VariableNode extends lexical.DecoratorNode {
5053
5141
  }
5054
5142
  }
5055
5143
  function VariableChip({ variableKey }) {
5056
- var _a;
5057
5144
  const { getDefinition } = useVariables();
5058
- const label = ((_a = getDefinition(variableKey)) == null ? void 0 : _a.label) ?? variableKey;
5145
+ const def = getDefinition(variableKey);
5146
+ const label = (def == null ? void 0 : def.label) ?? variableKey;
5147
+ const group = def == null ? void 0 : def.group;
5059
5148
  return /* @__PURE__ */ jsxRuntime.jsx(
5060
5149
  "span",
5061
5150
  {
5062
5151
  className: "lex4-variable-chip",
5063
5152
  "data-testid": `variable-chip-${variableKey}`,
5153
+ "data-variable-group": group,
5064
5154
  title: variableKey,
5065
5155
  children: label
5066
5156
  }
@@ -5092,106 +5182,19 @@ const VariablePlugin = () => {
5092
5182
  }, [editor]);
5093
5183
  return null;
5094
5184
  };
5095
- const VariablePicker = ({ onInsert, disabled = false }) => {
5096
- const { definitions } = useVariables();
5097
- const [open, setOpen] = React.useState(false);
5098
- const [filter, setFilter] = React.useState("");
5099
- const containerRef = React.useRef(null);
5100
- const filtered = React.useMemo(() => {
5101
- if (!filter) return definitions;
5102
- const lower = filter.toLowerCase();
5103
- return definitions.filter(
5104
- (d) => d.key.toLowerCase().includes(lower) || d.label.toLowerCase().includes(lower)
5105
- );
5106
- }, [definitions, filter]);
5107
- const grouped = React.useMemo(() => {
5108
- const groups = {};
5109
- for (const def of filtered) {
5110
- const g = def.group ?? "Other";
5111
- if (!groups[g]) groups[g] = [];
5112
- groups[g].push(def);
5113
- }
5114
- return groups;
5115
- }, [filtered]);
5116
- const handleInsert = React.useCallback(
5117
- (key) => {
5118
- onInsert(key);
5119
- setOpen(false);
5120
- setFilter("");
5121
- },
5122
- [onInsert]
5123
- );
5124
- React.useEffect(() => {
5125
- if (!open) return;
5126
- const handler = (e) => {
5127
- if (containerRef.current && !containerRef.current.contains(e.target)) {
5128
- setOpen(false);
5129
- setFilter("");
5130
- }
5131
- };
5132
- document.addEventListener("mousedown", handler);
5133
- return () => document.removeEventListener("mousedown", handler);
5134
- }, [open]);
5135
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: "lex4-variable-picker", children: [
5136
- /* @__PURE__ */ jsxRuntime.jsx(
5137
- "button",
5138
- {
5139
- type: "button",
5140
- className: "lex4-variable-picker-btn",
5141
- "data-testid": "variable-picker-button",
5142
- disabled: disabled || definitions.length === 0,
5143
- onClick: () => setOpen(!open),
5144
- title: "Insert variable",
5145
- children: "{x}"
5146
- }
5147
- ),
5148
- open && /* @__PURE__ */ jsxRuntime.jsxs(
5149
- "div",
5150
- {
5151
- className: "lex4-variable-picker-dropdown",
5152
- "data-testid": "variable-picker-dropdown",
5153
- children: [
5154
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-picker-search", children: /* @__PURE__ */ jsxRuntime.jsx(
5155
- "input",
5156
- {
5157
- type: "text",
5158
- placeholder: "Search variables...",
5159
- "data-testid": "variable-picker-search",
5160
- value: filter,
5161
- onChange: (e) => setFilter(e.target.value),
5162
- autoFocus: true
5163
- }
5164
- ) }),
5165
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-picker-list", children: [
5166
- Object.keys(grouped).length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-picker-empty", children: "No variables found" }),
5167
- Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5168
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-picker-group-label", children: group }),
5169
- defs.map((def) => /* @__PURE__ */ jsxRuntime.jsxs(
5170
- "button",
5171
- {
5172
- type: "button",
5173
- className: "lex4-variable-picker-option",
5174
- "data-testid": `variable-option-${def.key}`,
5175
- onClick: () => handleInsert(def.key),
5176
- children: [
5177
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-picker-key", children: `{{${def.key}}}` }),
5178
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-picker-label", children: def.label })
5179
- ]
5180
- },
5181
- def.key
5182
- ))
5183
- ] }, group))
5184
- ] })
5185
- ]
5186
- }
5187
- )
5188
- ] });
5189
- };
5190
5185
  const VariablePanel = ({ open, onClose }) => {
5191
- const { definitions } = useVariables();
5186
+ const { definitions, refreshDefinitions } = useVariables();
5192
5187
  const { activeEditor, runHistoryAction } = useDocument();
5193
5188
  const t = useTranslations();
5194
5189
  const [filter, setFilter] = React.useState("");
5190
+ const [creating, setCreating] = React.useState(false);
5191
+ const [createError, setCreateError] = React.useState(null);
5192
+ const [draft, setDraft] = React.useState({
5193
+ label: "",
5194
+ key: "",
5195
+ group: "",
5196
+ valueType: "string"
5197
+ });
5195
5198
  const filtered = React.useMemo(() => {
5196
5199
  if (!filter) return definitions;
5197
5200
  const lower = filter.toLowerCase();
@@ -5220,8 +5223,51 @@ const VariablePanel = ({ open, onClose }) => {
5220
5223
  },
5221
5224
  [activeEditor, runHistoryAction]
5222
5225
  );
5223
- if (definitions.length === 0) return null;
5224
- return /* @__PURE__ */ jsxRuntime.jsxs(
5226
+ const resetCreateState = React.useCallback(() => {
5227
+ setDraft({
5228
+ label: "",
5229
+ key: "",
5230
+ group: "",
5231
+ valueType: "string"
5232
+ });
5233
+ setCreateError(null);
5234
+ setCreating(false);
5235
+ }, []);
5236
+ const handleCreateVariable = React.useCallback((event) => {
5237
+ event.preventDefault();
5238
+ const label = draft.label.trim();
5239
+ const key = draft.key.trim();
5240
+ const group = draft.group.trim();
5241
+ if (!label || !key) {
5242
+ setCreateError(t.variables.createVariableMissingFields);
5243
+ return;
5244
+ }
5245
+ if (definitions.some((def) => def.key === key)) {
5246
+ setCreateError(interpolate(t.variables.createVariableDuplicateKey, { key }));
5247
+ return;
5248
+ }
5249
+ refreshDefinitions([
5250
+ ...definitions,
5251
+ {
5252
+ key,
5253
+ label,
5254
+ group: group || void 0,
5255
+ valueType: draft.valueType
5256
+ }
5257
+ ]);
5258
+ resetCreateState();
5259
+ }, [
5260
+ definitions,
5261
+ draft.group,
5262
+ draft.key,
5263
+ draft.label,
5264
+ draft.valueType,
5265
+ refreshDefinitions,
5266
+ resetCreateState,
5267
+ t.variables.createVariableDuplicateKey,
5268
+ t.variables.createVariableMissingFields
5269
+ ]);
5270
+ return /* @__PURE__ */ jsxRuntime.jsx(
5225
5271
  EditorSidebar,
5226
5272
  {
5227
5273
  title: t.variables.title,
@@ -5239,7 +5285,7 @@ const VariablePanel = ({ open, onClose }) => {
5239
5285
  children: /* @__PURE__ */ jsxRuntime.jsx(RefreshCw, { size: 12 })
5240
5286
  }
5241
5287
  ),
5242
- children: [
5288
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-panel-content", children: [
5243
5289
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-search-container", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-search-wrapper", children: [
5244
5290
  /* @__PURE__ */ jsxRuntime.jsx(Search, { size: 14, className: "lex4-variable-search-icon" }),
5245
5291
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5256,23 +5302,109 @@ const VariablePanel = ({ open, onClose }) => {
5256
5302
  ] }) }),
5257
5303
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-list", children: [
5258
5304
  Object.keys(grouped).length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-list-empty", children: t.variables.noVariablesFound }),
5259
- Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group", children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsxs(
5260
- "button",
5261
- {
5262
- type: "button",
5263
- className: "lex4-variable-list-item",
5264
- "data-testid": `variable-panel-${def.key}`,
5265
- onClick: () => handleInsert(def.key),
5266
- disabled: !activeEditor,
5267
- children: [
5268
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-badge", children: def.label }),
5269
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-variable-group-label", children: group })
5270
- ]
5271
- },
5272
- def.key
5273
- )) }) }, group))
5274
- ] })
5275
- ]
5305
+ Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-group", "data-variable-group": group, children: [
5306
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-label", children: group }),
5307
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-items", children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(
5308
+ "button",
5309
+ {
5310
+ type: "button",
5311
+ className: "lex4-variable-list-item",
5312
+ "data-testid": `variable-panel-${def.key}`,
5313
+ "data-variable-group": group,
5314
+ onClick: () => handleInsert(def.key),
5315
+ disabled: !activeEditor,
5316
+ title: def.key,
5317
+ children: def.label
5318
+ },
5319
+ def.key
5320
+ )) })
5321
+ ] }, group))
5322
+ ] }),
5323
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create", children: creating ? /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "lex4-variable-create-form", onSubmit: handleCreateVariable, children: [
5324
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create-title", children: t.variables.createVariableTitle }),
5325
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5326
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableLabel }),
5327
+ /* @__PURE__ */ jsxRuntime.jsx(
5328
+ "input",
5329
+ {
5330
+ value: draft.label,
5331
+ onChange: (e) => {
5332
+ setDraft((current) => ({ ...current, label: e.target.value }));
5333
+ if (createError) {
5334
+ setCreateError(null);
5335
+ }
5336
+ }
5337
+ }
5338
+ )
5339
+ ] }),
5340
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5341
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableKey }),
5342
+ /* @__PURE__ */ jsxRuntime.jsx(
5343
+ "input",
5344
+ {
5345
+ value: draft.key,
5346
+ onChange: (e) => {
5347
+ setDraft((current) => ({ ...current, key: e.target.value }));
5348
+ if (createError) {
5349
+ setCreateError(null);
5350
+ }
5351
+ }
5352
+ }
5353
+ )
5354
+ ] }),
5355
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-create-row", children: [
5356
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5357
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableGroup }),
5358
+ /* @__PURE__ */ jsxRuntime.jsx(
5359
+ "input",
5360
+ {
5361
+ value: draft.group,
5362
+ onChange: (e) => {
5363
+ setDraft((current) => ({ ...current, group: e.target.value }));
5364
+ }
5365
+ }
5366
+ )
5367
+ ] }),
5368
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5369
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableType }),
5370
+ /* @__PURE__ */ jsxRuntime.jsxs(
5371
+ "select",
5372
+ {
5373
+ value: draft.valueType,
5374
+ onChange: (e) => {
5375
+ setDraft((current) => ({
5376
+ ...current,
5377
+ valueType: e.target.value
5378
+ }));
5379
+ },
5380
+ children: [
5381
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "string", children: "string" }),
5382
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "number" }),
5383
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "date", children: "date" }),
5384
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "boolean" })
5385
+ ]
5386
+ }
5387
+ )
5388
+ ] })
5389
+ ] }),
5390
+ createError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create-error", children: createError }),
5391
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-create-actions", children: [
5392
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lex4-variable-create-cancel", onClick: resetCreateState, children: t.variables.createVariableCancel }),
5393
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "lex4-variable-create-submit", children: t.variables.createVariableSave })
5394
+ ] })
5395
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
5396
+ "button",
5397
+ {
5398
+ type: "button",
5399
+ className: "lex4-variable-create-toggle",
5400
+ onClick: () => setCreating(true),
5401
+ children: [
5402
+ "+ ",
5403
+ t.variables.newVariable
5404
+ ]
5405
+ }
5406
+ ) })
5407
+ ] })
5276
5408
  }
5277
5409
  );
5278
5410
  };
@@ -5288,41 +5420,23 @@ const VariablePanelStateProvider = ({ children }) => {
5288
5420
  const [panelOpen, setPanelOpen] = React.useState(false);
5289
5421
  return /* @__PURE__ */ jsxRuntime.jsx(VariablePanelContext.Provider, { value: { panelOpen, setPanelOpen }, children });
5290
5422
  };
5291
- const VariableToolbarItem = () => {
5292
- const { activeEditor, runHistoryAction } = useDocument();
5293
- const t = useTranslations();
5294
- const handleInsert = React.useCallback((variableKey) => {
5295
- runHistoryAction(
5296
- { label: interpolate(t.variables.insertVariable, { key: variableKey }), source: "toolbar", region: "document" },
5297
- () => {
5298
- if (activeEditor) {
5299
- activeEditor.dispatchCommand(INSERT_VARIABLE_COMMAND, variableKey);
5300
- }
5301
- }
5302
- );
5303
- }, [activeEditor, runHistoryAction]);
5304
- return /* @__PURE__ */ jsxRuntime.jsx(
5305
- VariablePicker,
5306
- {
5307
- onInsert: handleInsert,
5308
- disabled: !activeEditor
5309
- }
5310
- );
5311
- };
5312
- const VariablePanelToggle = () => {
5423
+ const VariableToolbarToggle = () => {
5313
5424
  const { panelOpen, setPanelOpen } = useVariablePanelState();
5314
5425
  const t = useTranslations();
5315
- return /* @__PURE__ */ jsxRuntime.jsx(
5426
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5316
5427
  "button",
5317
5428
  {
5318
5429
  type: "button",
5319
- title: panelOpen ? t.variables.closePanel : t.variables.openPanel,
5320
- "aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
5430
+ className: `lex4-toolbar-toggle-btn${panelOpen ? " active" : ""}`,
5321
5431
  onMouseDown: (e) => e.preventDefault(),
5322
5432
  onClick: () => setPanelOpen(!panelOpen),
5323
- className: `lex4-toolbar-btn${panelOpen ? " active" : ""}`,
5324
5433
  "data-testid": "toggle-variable-panel",
5325
- children: /* @__PURE__ */ jsxRuntime.jsx(Braces, { size: 15 })
5434
+ title: panelOpen ? t.variables.closePanel : t.variables.openPanel,
5435
+ "aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
5436
+ children: [
5437
+ /* @__PURE__ */ jsxRuntime.jsx(Braces, { size: 14 }),
5438
+ t.variables.title
5439
+ ]
5326
5440
  }
5327
5441
  );
5328
5442
  };
@@ -5330,6 +5444,15 @@ const VariablePanelWithState = () => {
5330
5444
  const { panelOpen, setPanelOpen } = useVariablePanelState();
5331
5445
  return /* @__PURE__ */ jsxRuntime.jsx(VariablePanel, { open: panelOpen, onClose: () => setPanelOpen(false) });
5332
5446
  };
5447
+ const VariablePanelStateSync = ({ children }) => {
5448
+ const { panelOpen, setPanelOpen } = useVariablePanelState();
5449
+ const extState = useExtensionState();
5450
+ React.useEffect(() => {
5451
+ extState.set("variablePanelOpen", panelOpen);
5452
+ extState.set("setVariablePanelOpen", setPanelOpen);
5453
+ }, [extState, panelOpen, setPanelOpen]);
5454
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
5455
+ };
5333
5456
  const VariableDefinitionsSync = ({ children }) => {
5334
5457
  const { definitions } = useVariables();
5335
5458
  const extState = useExtensionState();
@@ -5340,13 +5463,13 @@ const VariableDefinitionsSync = ({ children }) => {
5340
5463
  };
5341
5464
  function variablesExtension(definitions = []) {
5342
5465
  const ProviderWrapper = ({ children }) => {
5343
- return /* @__PURE__ */ jsxRuntime.jsx(VariablePanelStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(VariableProvider, { initialDefinitions: definitions, children: /* @__PURE__ */ jsxRuntime.jsx(VariableDefinitionsSync, { children }) }) });
5466
+ return /* @__PURE__ */ jsxRuntime.jsx(VariablePanelStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(VariableProvider, { initialDefinitions: definitions, children: /* @__PURE__ */ jsxRuntime.jsx(VariablePanelStateSync, { children: /* @__PURE__ */ jsxRuntime.jsx(VariableDefinitionsSync, { children }) }) }) });
5344
5467
  };
5345
5468
  return {
5346
5469
  name: "variables",
5347
5470
  nodes: [VariableNode],
5348
5471
  bodyPlugins: [VariablePlugin],
5349
- toolbarItems: [VariableToolbarItem, VariablePanelToggle],
5472
+ toolbarEndItems: [VariableToolbarToggle],
5350
5473
  sidePanel: VariablePanelWithState,
5351
5474
  provider: ProviderWrapper,
5352
5475
  handleMethods: (ctx) => ({
@@ -5358,6 +5481,15 @@ function variablesExtension(definitions = []) {
5358
5481
  },
5359
5482
  refreshVariables: (newDefs) => {
5360
5483
  ctx.setExtensionState("variableDefinitions", newDefs);
5484
+ },
5485
+ setVariablePanelOpen: (open) => {
5486
+ const setter = ctx.getExtensionState("setVariablePanelOpen");
5487
+ setter == null ? void 0 : setter(open);
5488
+ },
5489
+ toggleVariablePanel: () => {
5490
+ const current = ctx.getExtensionState("variablePanelOpen") ?? false;
5491
+ const setter = ctx.getExtensionState("setVariablePanelOpen");
5492
+ setter == null ? void 0 : setter(!current);
5361
5493
  }
5362
5494
  })
5363
5495
  };