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