@yurikilian/lex4 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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"
@@ -623,7 +633,17 @@ const PT_BR_TRANSLATIONS = {
623
633
  noVariablesFound: "Nenhuma variável encontrada",
624
634
  insertVariable: "Inserir variável {{key}}",
625
635
  openPanel: "Abrir Variáveis",
626
- closePanel: "Fechar Variáveis"
636
+ closePanel: "Fechar Variáveis",
637
+ newVariable: "Nova variável",
638
+ createVariableTitle: "Criar variável",
639
+ createVariableLabel: "Rótulo",
640
+ createVariableKey: "Chave",
641
+ createVariableGroup: "Grupo",
642
+ createVariableType: "Tipo",
643
+ createVariableSave: "Adicionar variável",
644
+ createVariableCancel: "Cancelar",
645
+ createVariableMissingFields: "Rótulo e chave são obrigatórios.",
646
+ createVariableDuplicateKey: "A variável {{key}} já existe."
627
647
  },
628
648
  header: {
629
649
  placeholder: "Cabeçalho"
@@ -934,7 +954,7 @@ const DocumentProvider = ({
934
954
  );
935
955
  const activePageIdRef = React.useRef(((_b = initialSnapshot.pages[0]) == null ? void 0 : _b.id) ?? null);
936
956
  const [globalSelectionActive, setGlobalSelectionActive] = React.useState(false);
937
- const [historySidebarOpen, setHistorySidebarOpen] = React.useState(true);
957
+ const [historySidebarOpen, setHistorySidebarOpen] = React.useState(false);
938
958
  const [focusAtEndVersion, setFocusAtEndVersion] = React.useState(0);
939
959
  const t = useTranslations();
940
960
  const activeEditorRef = React.useRef(null);
@@ -1389,33 +1409,33 @@ const createLucideIcon = (iconName, iconNode) => {
1389
1409
  * This source code is licensed under the ISC license.
1390
1410
  * See the LICENSE file in the root directory of this source tree.
1391
1411
  */
1392
- const __iconNode$q = [
1412
+ const __iconNode$p = [
1393
1413
  ["path", { d: "m15 16 2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16", key: "xik6mr" }],
1394
1414
  ["path", { d: "M15.697 14h5.606", key: "1stdlc" }],
1395
1415
  ["path", { d: "m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16", key: "d5nyq2" }],
1396
1416
  ["path", { d: "M3.304 13h6.392", key: "1q3zxz" }]
1397
1417
  ];
1398
- const ALargeSmall = createLucideIcon("a-large-small", __iconNode$q);
1418
+ const ALargeSmall = createLucideIcon("a-large-small", __iconNode$p);
1399
1419
  /**
1400
1420
  * @license lucide-react v1.8.0 - ISC
1401
1421
  *
1402
1422
  * This source code is licensed under the ISC license.
1403
1423
  * See the LICENSE file in the root directory of this source tree.
1404
1424
  */
1405
- const __iconNode$p = [
1425
+ const __iconNode$o = [
1406
1426
  [
1407
1427
  "path",
1408
1428
  { 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
1429
  ]
1410
1430
  ];
1411
- const Bold = createLucideIcon("bold", __iconNode$p);
1431
+ const Bold = createLucideIcon("bold", __iconNode$o);
1412
1432
  /**
1413
1433
  * @license lucide-react v1.8.0 - ISC
1414
1434
  *
1415
1435
  * This source code is licensed under the ISC license.
1416
1436
  * See the LICENSE file in the root directory of this source tree.
1417
1437
  */
1418
- const __iconNode$o = [
1438
+ const __iconNode$n = [
1419
1439
  [
1420
1440
  "path",
1421
1441
  { 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 +1448,7 @@ const __iconNode$o = [
1428
1448
  }
1429
1449
  ]
1430
1450
  ];
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);
1451
+ const Braces = createLucideIcon("braces", __iconNode$n);
1440
1452
  /**
1441
1453
  * @license lucide-react v1.8.0 - ISC
1442
1454
  *
@@ -1474,44 +1486,76 @@ const Eraser = createLucideIcon("eraser", __iconNode$l);
1474
1486
  * See the LICENSE file in the root directory of this source tree.
1475
1487
  */
1476
1488
  const __iconNode$k = [
1489
+ [
1490
+ "path",
1491
+ {
1492
+ 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",
1493
+ key: "1oefj6"
1494
+ }
1495
+ ],
1496
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5", key: "wfsgrz" }],
1497
+ ["path", { d: "M10 9H8", key: "b1mrlr" }],
1498
+ ["path", { d: "M16 13H8", key: "t4e002" }],
1499
+ ["path", { d: "M16 17H8", key: "z1uh3a" }]
1500
+ ];
1501
+ const FileText = createLucideIcon("file-text", __iconNode$k);
1502
+ /**
1503
+ * @license lucide-react v1.8.0 - ISC
1504
+ *
1505
+ * This source code is licensed under the ISC license.
1506
+ * See the LICENSE file in the root directory of this source tree.
1507
+ */
1508
+ const __iconNode$j = [
1509
+ ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "1357e3" }],
1510
+ ["path", { d: "M3 3v5h5", key: "1xhq8a" }],
1511
+ ["path", { d: "M12 7v5l4 2", key: "1fdv2h" }]
1512
+ ];
1513
+ const History = createLucideIcon("history", __iconNode$j);
1514
+ /**
1515
+ * @license lucide-react v1.8.0 - ISC
1516
+ *
1517
+ * This source code is licensed under the ISC license.
1518
+ * See the LICENSE file in the root directory of this source tree.
1519
+ */
1520
+ const __iconNode$i = [
1477
1521
  ["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }],
1478
1522
  ["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }],
1479
1523
  ["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }]
1480
1524
  ];
1481
- const Italic = createLucideIcon("italic", __iconNode$k);
1525
+ const Italic = createLucideIcon("italic", __iconNode$i);
1482
1526
  /**
1483
1527
  * @license lucide-react v1.8.0 - ISC
1484
1528
  *
1485
1529
  * This source code is licensed under the ISC license.
1486
1530
  * See the LICENSE file in the root directory of this source tree.
1487
1531
  */
1488
- const __iconNode$j = [
1532
+ const __iconNode$h = [
1489
1533
  ["path", { d: "M21 5H11", key: "us1j55" }],
1490
1534
  ["path", { d: "M21 12H11", key: "wd7e0v" }],
1491
1535
  ["path", { d: "M21 19H11", key: "saa85w" }],
1492
1536
  ["path", { d: "m7 8-4 4 4 4", key: "o5hrat" }]
1493
1537
  ];
1494
- const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$j);
1538
+ const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$h);
1495
1539
  /**
1496
1540
  * @license lucide-react v1.8.0 - ISC
1497
1541
  *
1498
1542
  * This source code is licensed under the ISC license.
1499
1543
  * See the LICENSE file in the root directory of this source tree.
1500
1544
  */
1501
- const __iconNode$i = [
1545
+ const __iconNode$g = [
1502
1546
  ["path", { d: "M21 5H11", key: "us1j55" }],
1503
1547
  ["path", { d: "M21 12H11", key: "wd7e0v" }],
1504
1548
  ["path", { d: "M21 19H11", key: "saa85w" }],
1505
1549
  ["path", { d: "m3 8 4 4-4 4", key: "1a3j6y" }]
1506
1550
  ];
1507
- const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$i);
1551
+ const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$g);
1508
1552
  /**
1509
1553
  * @license lucide-react v1.8.0 - ISC
1510
1554
  *
1511
1555
  * This source code is licensed under the ISC license.
1512
1556
  * See the LICENSE file in the root directory of this source tree.
1513
1557
  */
1514
- const __iconNode$h = [
1558
+ const __iconNode$f = [
1515
1559
  ["path", { d: "M11 5h10", key: "1cz7ny" }],
1516
1560
  ["path", { d: "M11 12h10", key: "1438ji" }],
1517
1561
  ["path", { d: "M11 19h10", key: "11t30w" }],
@@ -1519,14 +1563,14 @@ const __iconNode$h = [
1519
1563
  ["path", { d: "M4 9h2", key: "r1h2o0" }],
1520
1564
  ["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
1565
  ];
1522
- const ListOrdered = createLucideIcon("list-ordered", __iconNode$h);
1566
+ const ListOrdered = createLucideIcon("list-ordered", __iconNode$f);
1523
1567
  /**
1524
1568
  * @license lucide-react v1.8.0 - ISC
1525
1569
  *
1526
1570
  * This source code is licensed under the ISC license.
1527
1571
  * See the LICENSE file in the root directory of this source tree.
1528
1572
  */
1529
- const __iconNode$g = [
1573
+ const __iconNode$e = [
1530
1574
  ["path", { d: "M3 5h.01", key: "18ugdj" }],
1531
1575
  ["path", { d: "M3 12h.01", key: "nlz23k" }],
1532
1576
  ["path", { d: "M3 19h.01", key: "noohij" }],
@@ -1534,54 +1578,31 @@ const __iconNode$g = [
1534
1578
  ["path", { d: "M8 12h13", key: "1za7za" }],
1535
1579
  ["path", { d: "M8 19h13", key: "m83p4d" }]
1536
1580
  ];
1537
- const List = createLucideIcon("list", __iconNode$g);
1581
+ const List = createLucideIcon("list", __iconNode$e);
1538
1582
  /**
1539
1583
  * @license lucide-react v1.8.0 - ISC
1540
1584
  *
1541
1585
  * This source code is licensed under the ISC license.
1542
1586
  * See the LICENSE file in the root directory of this source tree.
1543
1587
  */
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 = [
1588
+ const __iconNode$d = [
1556
1589
  ["path", { d: "m15 14 5-5-5-5", key: "12vg1m" }],
1557
1590
  ["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
1591
  ];
1559
- const Redo2 = createLucideIcon("redo-2", __iconNode$e);
1592
+ const Redo2 = createLucideIcon("redo-2", __iconNode$d);
1560
1593
  /**
1561
1594
  * @license lucide-react v1.8.0 - ISC
1562
1595
  *
1563
1596
  * This source code is licensed under the ISC license.
1564
1597
  * See the LICENSE file in the root directory of this source tree.
1565
1598
  */
1566
- const __iconNode$d = [
1599
+ const __iconNode$c = [
1567
1600
  ["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
1601
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
1569
1602
  ["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
1603
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
1571
1604
  ];
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);
1605
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$c);
1585
1606
  /**
1586
1607
  * @license lucide-react v1.8.0 - ISC
1587
1608
  *
@@ -1600,16 +1621,12 @@ const Search = createLucideIcon("search", __iconNode$b);
1600
1621
  * See the LICENSE file in the root directory of this source tree.
1601
1622
  */
1602
1623
  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" }]
1624
+ ["path", { d: "M14 17H5", key: "gfn3mx" }],
1625
+ ["path", { d: "M19 7h-9", key: "6i9tg" }],
1626
+ ["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
1627
+ ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
1611
1628
  ];
1612
- const Settings = createLucideIcon("settings", __iconNode$a);
1629
+ const Settings2 = createLucideIcon("settings-2", __iconNode$a);
1613
1630
  /**
1614
1631
  * @license lucide-react v1.8.0 - ISC
1615
1632
  *
@@ -1833,11 +1850,15 @@ const HistorySidebar = () => {
1833
1850
  "data-history-current": isCurrent ? "true" : "false",
1834
1851
  onClick: () => jumpToHistoryEntry2(actualIndex),
1835
1852
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-history-entry-row", children: [
1853
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-history-entry-dot" }),
1836
1854
  /* @__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) })
1855
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-history-entry-label", children: entry.label }),
1856
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-history-entry-meta", children: [
1857
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-history-entry-source", children: t.regions[entry.source] ?? entry.source }),
1858
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-history-entry-meta-dot", children: "·" }),
1859
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-history-entry-time", children: formatTimestamp(entry.timestamp) })
1860
+ ] })
1861
+ ] })
1841
1862
  ] })
1842
1863
  }
1843
1864
  ) }, entry.id);
@@ -1845,6 +1866,188 @@ const HistorySidebar = () => {
1845
1866
  }
1846
1867
  );
1847
1868
  };
1869
+ function resolveExtensions(extensions) {
1870
+ const resolved = {
1871
+ nodes: [],
1872
+ bodyPlugins: [],
1873
+ toolbarItems: [],
1874
+ toolbarEndItems: [],
1875
+ sidePanels: [],
1876
+ providers: [],
1877
+ themeOverrides: {},
1878
+ cssVariables: {},
1879
+ rootClassNames: [],
1880
+ handleFactories: []
1881
+ };
1882
+ for (const ext of extensions) {
1883
+ if (ext.nodes) resolved.nodes.push(...ext.nodes);
1884
+ if (ext.bodyPlugins) resolved.bodyPlugins.push(...ext.bodyPlugins);
1885
+ if (ext.toolbarItems) resolved.toolbarItems.push(...ext.toolbarItems);
1886
+ if (ext.toolbarEndItems) resolved.toolbarEndItems.push(...ext.toolbarEndItems);
1887
+ if (ext.sidePanel) resolved.sidePanels.push(ext.sidePanel);
1888
+ if (ext.provider) resolved.providers.push(ext.provider);
1889
+ if (ext.themeOverrides) {
1890
+ resolved.themeOverrides = { ...resolved.themeOverrides, ...ext.themeOverrides };
1891
+ }
1892
+ if (ext.cssVariables) {
1893
+ Object.assign(resolved.cssVariables, ext.cssVariables);
1894
+ }
1895
+ if (ext.rootClassName) resolved.rootClassNames.push(ext.rootClassName);
1896
+ if (ext.handleMethods) resolved.handleFactories.push(ext.handleMethods);
1897
+ }
1898
+ return resolved;
1899
+ }
1900
+ const EMPTY_RESOLVED = {
1901
+ nodes: [],
1902
+ bodyPlugins: [],
1903
+ toolbarItems: [],
1904
+ toolbarEndItems: [],
1905
+ sidePanels: [],
1906
+ providers: [],
1907
+ themeOverrides: {},
1908
+ cssVariables: {},
1909
+ rootClassNames: [],
1910
+ handleFactories: []
1911
+ };
1912
+ const ExtensionResolvedContext = React.createContext(EMPTY_RESOLVED);
1913
+ function arraysEqual(a, b) {
1914
+ if (a.length !== b.length) return false;
1915
+ for (let i = 0; i < a.length; i++) {
1916
+ if (a[i] !== b[i]) return false;
1917
+ }
1918
+ return true;
1919
+ }
1920
+ const ExtensionProvider = ({ extensions, children }) => {
1921
+ const prevNamesRef = React.useRef([]);
1922
+ const resolvedRef = React.useRef(EMPTY_RESOLVED);
1923
+ const currentNames = (extensions ?? []).map((e) => e.name);
1924
+ if (!arraysEqual(currentNames, prevNamesRef.current)) {
1925
+ resolvedRef.current = extensions && extensions.length > 0 ? resolveExtensions(extensions) : EMPTY_RESOLVED;
1926
+ prevNamesRef.current = currentNames;
1927
+ }
1928
+ const resolved = resolvedRef.current;
1929
+ let wrapped = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1930
+ for (let i = resolved.providers.length - 1; i >= 0; i--) {
1931
+ const Provider = resolved.providers[i];
1932
+ wrapped = /* @__PURE__ */ jsxRuntime.jsx(Provider, { children: wrapped });
1933
+ }
1934
+ return /* @__PURE__ */ jsxRuntime.jsx(ExtensionResolvedContext.Provider, { value: resolved, children: wrapped });
1935
+ };
1936
+ function useExtensions() {
1937
+ return React.useContext(ExtensionResolvedContext);
1938
+ }
1939
+ const ExtensionStateContext = React.createContext(null);
1940
+ const ExtensionStateProvider = ({ children }) => {
1941
+ const stateRef = React.useRef({});
1942
+ return /* @__PURE__ */ jsxRuntime.jsx(ExtensionStateContext.Provider, { value: stateRef, children });
1943
+ };
1944
+ function useExtensionState() {
1945
+ const stateRef = React.useContext(ExtensionStateContext);
1946
+ return {
1947
+ get: (key) => stateRef == null ? void 0 : stateRef.current[key],
1948
+ set: (key, value) => {
1949
+ if (stateRef) stateRef.current[key] = value;
1950
+ }
1951
+ };
1952
+ }
1953
+ function useExtensionContext(getDocument, getActiveEditor) {
1954
+ const stateRef = React.useContext(ExtensionStateContext);
1955
+ return React.useMemo(() => ({
1956
+ getDocument,
1957
+ getActiveEditor,
1958
+ getExtensionState: (key) => stateRef == null ? void 0 : stateRef.current[key],
1959
+ setExtensionState: (key, value) => {
1960
+ if (stateRef) stateRef.current[key] = value;
1961
+ }
1962
+ }), [getDocument, getActiveEditor, stateRef]);
1963
+ }
1964
+ const SUPPORTED_FONTS = [
1965
+ "Calibri",
1966
+ "Inter",
1967
+ "Times New Roman",
1968
+ "Arial",
1969
+ "Georgia",
1970
+ "Courier New"
1971
+ ];
1972
+ function applyFontFamily(editor, fontFamily) {
1973
+ editor.update(() => {
1974
+ const selection = lexical.$getSelection();
1975
+ if (!lexical.$isRangeSelection(selection)) return;
1976
+ const nodes = selection.getNodes();
1977
+ for (const node of nodes) {
1978
+ if (lexical.$isTextNode(node)) {
1979
+ node.setStyle(`font-family: ${fontFamily}`);
1980
+ }
1981
+ }
1982
+ });
1983
+ }
1984
+ const SUPPORTED_FONT_SIZES = [
1985
+ 8,
1986
+ 9,
1987
+ 10,
1988
+ 11,
1989
+ 12,
1990
+ 14,
1991
+ 16,
1992
+ 18,
1993
+ 20,
1994
+ 24,
1995
+ 28,
1996
+ 32,
1997
+ 36,
1998
+ 48,
1999
+ 72
2000
+ ];
2001
+ function applyFontSize(editor, size) {
2002
+ editor.update(() => {
2003
+ const selection = lexical.$getSelection();
2004
+ if (!lexical.$isRangeSelection(selection)) return;
2005
+ const nodes = selection.getNodes();
2006
+ for (const node of nodes) {
2007
+ if (lexical.$isTextNode(node)) {
2008
+ const existing = node.getStyle();
2009
+ const updated = mergeFontSize(existing, size);
2010
+ node.setStyle(updated);
2011
+ }
2012
+ }
2013
+ });
2014
+ }
2015
+ function mergeFontSize(existingStyle, size) {
2016
+ const stripped = existingStyle.replace(/font-size:\s*[^;]+;?\s*/g, "").trim();
2017
+ const sizeDecl = `font-size: ${size}pt`;
2018
+ return stripped ? `${stripped}; ${sizeDecl}` : sizeDecl;
2019
+ }
2020
+ function toggleFormat(editor, format) {
2021
+ editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, format);
2022
+ }
2023
+ function toggleBold(editor) {
2024
+ toggleFormat(editor, "bold");
2025
+ }
2026
+ function toggleItalic(editor) {
2027
+ toggleFormat(editor, "italic");
2028
+ }
2029
+ function toggleUnderline(editor) {
2030
+ toggleFormat(editor, "underline");
2031
+ }
2032
+ function toggleStrikethrough(editor) {
2033
+ toggleFormat(editor, "strikethrough");
2034
+ }
2035
+ function setAlignment(editor, alignment) {
2036
+ editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, alignment);
2037
+ }
2038
+ function insertList(editor, type) {
2039
+ if (type === "number") {
2040
+ editor.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND, void 0);
2041
+ } else {
2042
+ editor.dispatchCommand(list.INSERT_UNORDERED_LIST_COMMAND, void 0);
2043
+ }
2044
+ }
2045
+ function indentContent(editor) {
2046
+ editor.dispatchCommand(lexical.INDENT_CONTENT_COMMAND, void 0);
2047
+ }
2048
+ function outdentContent(editor) {
2049
+ editor.dispatchCommand(lexical.OUTDENT_CONTENT_COMMAND, void 0);
2050
+ }
1848
2051
  const HeaderFooterToggle = ({
1849
2052
  enabled,
1850
2053
  onToggle
@@ -1856,7 +2059,7 @@ const HeaderFooterToggle = ({
1856
2059
  className: "lex4-hf-toggle",
1857
2060
  "data-testid": "header-footer-toggle",
1858
2061
  children: [
1859
- /* @__PURE__ */ jsxRuntime.jsx(Rows3, { size: 14, className: "lex4-hf-toggle-icon" }),
2062
+ /* @__PURE__ */ jsxRuntime.jsx(FileText, { size: 14, className: "lex4-hf-toggle-icon" }),
1860
2063
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
1861
2064
  /* @__PURE__ */ jsxRuntime.jsx(
1862
2065
  "button",
@@ -1867,7 +2070,7 @@ const HeaderFooterToggle = ({
1867
2070
  onMouseDown: (e) => e.preventDefault(),
1868
2071
  onClick: () => onToggle(!enabled),
1869
2072
  className: "lex4-hf-switch",
1870
- style: { backgroundColor: enabled ? "var(--lex4-color-primary)" : "var(--lex4-color-text-disabled)" },
2073
+ style: { backgroundColor: enabled ? "var(--color-primary)" : "var(--color-muted)" },
1871
2074
  "data-testid": "header-footer-switch",
1872
2075
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "lex4-hf-switch-knob" })
1873
2076
  }
@@ -1912,20 +2115,19 @@ const HeaderFooterActions = ({
1912
2115
  close();
1913
2116
  };
1914
2117
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: { position: "relative" }, "data-testid": "header-footer-actions", children: [
1915
- /* @__PURE__ */ jsxRuntime.jsxs(
2118
+ /* @__PURE__ */ jsxRuntime.jsx(
1916
2119
  "button",
1917
2120
  {
1918
2121
  type: "button",
1919
2122
  onMouseDown: (e) => e.preventDefault(),
1920
2123
  onClick: () => setOpen(!open),
1921
- className: "lex4-settings-trigger",
2124
+ className: "lex4-settings-trigger lex4-settings-trigger-icon",
1922
2125
  "data-testid": "header-footer-menu-trigger",
1923
2126
  "aria-expanded": open,
1924
2127
  "aria-haspopup": "true",
1925
- children: [
1926
- /* @__PURE__ */ jsxRuntime.jsx(Settings, { size: 14 }),
1927
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 12 })
1928
- ]
2128
+ "aria-label": "Header and footer settings",
2129
+ title: "Header and footer settings",
2130
+ children: /* @__PURE__ */ jsxRuntime.jsx(Settings2, { size: 14 })
1929
2131
  }
1930
2132
  ),
1931
2133
  open && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2035,190 +2237,100 @@ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */
2035
2237
  }
2036
2238
  );
2037
2239
  const MenuDivider = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-settings-divider" });
2038
- function resolveExtensions(extensions) {
2039
- const resolved = {
2040
- nodes: [],
2041
- bodyPlugins: [],
2042
- toolbarItems: [],
2043
- sidePanels: [],
2044
- providers: [],
2045
- themeOverrides: {},
2046
- cssVariables: {},
2047
- rootClassNames: [],
2048
- handleFactories: []
2049
- };
2050
- for (const ext of extensions) {
2051
- if (ext.nodes) resolved.nodes.push(...ext.nodes);
2052
- if (ext.bodyPlugins) resolved.bodyPlugins.push(...ext.bodyPlugins);
2053
- if (ext.toolbarItems) resolved.toolbarItems.push(...ext.toolbarItems);
2054
- if (ext.sidePanel) resolved.sidePanels.push(ext.sidePanel);
2055
- if (ext.provider) resolved.providers.push(ext.provider);
2056
- if (ext.themeOverrides) {
2057
- resolved.themeOverrides = { ...resolved.themeOverrides, ...ext.themeOverrides };
2058
- }
2059
- if (ext.cssVariables) {
2060
- Object.assign(resolved.cssVariables, ext.cssVariables);
2061
- }
2062
- if (ext.rootClassName) resolved.rootClassNames.push(ext.rootClassName);
2063
- if (ext.handleMethods) resolved.handleFactories.push(ext.handleMethods);
2064
- }
2065
- return resolved;
2066
- }
2067
- const EMPTY_RESOLVED = {
2068
- nodes: [],
2069
- bodyPlugins: [],
2070
- toolbarItems: [],
2071
- sidePanels: [],
2072
- providers: [],
2073
- themeOverrides: {},
2074
- cssVariables: {},
2075
- rootClassNames: [],
2076
- handleFactories: []
2077
- };
2078
- const ExtensionResolvedContext = React.createContext(EMPTY_RESOLVED);
2079
- function arraysEqual(a, b) {
2080
- if (a.length !== b.length) return false;
2081
- for (let i = 0; i < a.length; i++) {
2082
- if (a[i] !== b[i]) return false;
2083
- }
2084
- return true;
2085
- }
2086
- const ExtensionProvider = ({ extensions, children }) => {
2087
- const prevNamesRef = React.useRef([]);
2088
- const resolvedRef = React.useRef(EMPTY_RESOLVED);
2089
- const currentNames = (extensions ?? []).map((e) => e.name);
2090
- if (!arraysEqual(currentNames, prevNamesRef.current)) {
2091
- resolvedRef.current = extensions && extensions.length > 0 ? resolveExtensions(extensions) : EMPTY_RESOLVED;
2092
- prevNamesRef.current = currentNames;
2093
- }
2094
- const resolved = resolvedRef.current;
2095
- let wrapped = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
2096
- for (let i = resolved.providers.length - 1; i >= 0; i--) {
2097
- const Provider = resolved.providers[i];
2098
- wrapped = /* @__PURE__ */ jsxRuntime.jsx(Provider, { children: wrapped });
2099
- }
2100
- return /* @__PURE__ */ jsxRuntime.jsx(ExtensionResolvedContext.Provider, { value: resolved, children: wrapped });
2101
- };
2102
- function useExtensions() {
2103
- return React.useContext(ExtensionResolvedContext);
2104
- }
2105
- const ExtensionStateContext = React.createContext(null);
2106
- const ExtensionStateProvider = ({ children }) => {
2107
- const stateRef = React.useRef({});
2108
- return /* @__PURE__ */ jsxRuntime.jsx(ExtensionStateContext.Provider, { value: stateRef, children });
2109
- };
2110
- function useExtensionState() {
2111
- const stateRef = React.useContext(ExtensionStateContext);
2112
- return {
2113
- get: (key) => stateRef == null ? void 0 : stateRef.current[key],
2114
- set: (key, value) => {
2115
- if (stateRef) stateRef.current[key] = value;
2116
- }
2117
- };
2118
- }
2119
- function useExtensionContext(getDocument, getActiveEditor) {
2120
- const stateRef = React.useContext(ExtensionStateContext);
2121
- return React.useMemo(() => ({
2122
- getDocument,
2123
- getActiveEditor,
2124
- getExtensionState: (key) => stateRef == null ? void 0 : stateRef.current[key],
2125
- setExtensionState: (key, value) => {
2126
- if (stateRef) stateRef.current[key] = value;
2127
- }
2128
- }), [getDocument, getActiveEditor, stateRef]);
2129
- }
2130
- const SUPPORTED_FONTS = [
2131
- "Calibri",
2132
- "Inter",
2133
- "Times New Roman",
2134
- "Arial",
2135
- "Georgia",
2136
- "Courier New"
2137
- ];
2138
- function applyFontFamily(editor, fontFamily) {
2139
- editor.update(() => {
2140
- const selection = lexical.$getSelection();
2141
- if (!lexical.$isRangeSelection(selection)) return;
2142
- const nodes = selection.getNodes();
2143
- for (const node of nodes) {
2144
- if (lexical.$isTextNode(node)) {
2145
- node.setStyle(`font-family: ${fontFamily}`);
2146
- }
2147
- }
2148
- });
2149
- }
2150
- const SUPPORTED_FONT_SIZES = [
2151
- 8,
2152
- 9,
2153
- 10,
2154
- 11,
2155
- 12,
2156
- 14,
2157
- 16,
2158
- 18,
2159
- 20,
2160
- 24,
2161
- 28,
2162
- 32,
2163
- 36,
2164
- 48,
2165
- 72
2166
- ];
2167
- function applyFontSize(editor, size) {
2168
- editor.update(() => {
2169
- const selection = lexical.$getSelection();
2170
- if (!lexical.$isRangeSelection(selection)) return;
2171
- const nodes = selection.getNodes();
2172
- for (const node of nodes) {
2173
- if (lexical.$isTextNode(node)) {
2174
- const existing = node.getStyle();
2175
- const updated = mergeFontSize(existing, size);
2176
- node.setStyle(updated);
2240
+ const CanvasControls = () => {
2241
+ const { document: document2, dispatch, activePageId, runHistoryAction } = useDocument();
2242
+ const t = useTranslations();
2243
+ const runCanvasAction = React.useCallback((label, callback) => {
2244
+ runHistoryAction(
2245
+ {
2246
+ label,
2247
+ source: "toolbar",
2248
+ region: "document"
2249
+ },
2250
+ callback
2251
+ );
2252
+ }, [runHistoryAction]);
2253
+ const handleToggle = React.useCallback((enabled) => {
2254
+ runCanvasAction(
2255
+ enabled ? t.history.actions.enabledHeadersFooters : t.history.actions.disabledHeadersFooters,
2256
+ () => {
2257
+ dispatch({ type: "SET_HEADER_FOOTER_ENABLED", enabled });
2177
2258
  }
2178
- }
2179
- });
2180
- }
2181
- function mergeFontSize(existingStyle, size) {
2182
- const stripped = existingStyle.replace(/font-size:\s*[^;]+;?\s*/g, "").trim();
2183
- const sizeDecl = `font-size: ${size}pt`;
2184
- return stripped ? `${stripped}; ${sizeDecl}` : sizeDecl;
2185
- }
2186
- function toggleFormat(editor, format) {
2187
- editor.dispatchCommand(lexical.FORMAT_TEXT_COMMAND, format);
2188
- }
2189
- function toggleBold(editor) {
2190
- toggleFormat(editor, "bold");
2191
- }
2192
- function toggleItalic(editor) {
2193
- toggleFormat(editor, "italic");
2194
- }
2195
- function toggleUnderline(editor) {
2196
- toggleFormat(editor, "underline");
2197
- }
2198
- function toggleStrikethrough(editor) {
2199
- toggleFormat(editor, "strikethrough");
2200
- }
2201
- function setAlignment(editor, alignment) {
2202
- editor.dispatchCommand(lexical.FORMAT_ELEMENT_COMMAND, alignment);
2203
- }
2204
- function insertList(editor, type) {
2205
- if (type === "number") {
2206
- editor.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND, void 0);
2207
- } else {
2208
- editor.dispatchCommand(list.INSERT_UNORDERED_LIST_COMMAND, void 0);
2209
- }
2210
- }
2211
- function indentContent(editor) {
2212
- editor.dispatchCommand(lexical.INDENT_CONTENT_COMMAND, void 0);
2213
- }
2214
- function outdentContent(editor) {
2215
- editor.dispatchCommand(lexical.OUTDENT_CONTENT_COMMAND, void 0);
2216
- }
2259
+ );
2260
+ }, [dispatch, runCanvasAction, t.history.actions.disabledHeadersFooters, t.history.actions.enabledHeadersFooters]);
2261
+ const handleCopyHeaderToAll = React.useCallback(() => {
2262
+ if (!activePageId) {
2263
+ return;
2264
+ }
2265
+ runCanvasAction(t.history.actions.copiedHeaderToAll, () => {
2266
+ dispatch({ type: "COPY_HEADER_TO_ALL", sourcePageId: activePageId });
2267
+ });
2268
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.copiedHeaderToAll]);
2269
+ const handleCopyFooterToAll = React.useCallback(() => {
2270
+ if (!activePageId) {
2271
+ return;
2272
+ }
2273
+ runCanvasAction(t.history.actions.copiedFooterToAll, () => {
2274
+ dispatch({ type: "COPY_FOOTER_TO_ALL", sourcePageId: activePageId });
2275
+ });
2276
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.copiedFooterToAll]);
2277
+ const handleClearHeader = React.useCallback(() => {
2278
+ if (!activePageId) {
2279
+ return;
2280
+ }
2281
+ runCanvasAction(t.history.actions.clearedHeader, () => {
2282
+ dispatch({ type: "CLEAR_HEADER", pageId: activePageId });
2283
+ });
2284
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.clearedHeader]);
2285
+ const handleClearFooter = React.useCallback(() => {
2286
+ if (!activePageId) {
2287
+ return;
2288
+ }
2289
+ runCanvasAction(t.history.actions.clearedFooter, () => {
2290
+ dispatch({ type: "CLEAR_FOOTER", pageId: activePageId });
2291
+ });
2292
+ }, [activePageId, dispatch, runCanvasAction, t.history.actions.clearedFooter]);
2293
+ const handleClearAllHeaders = React.useCallback(() => {
2294
+ runCanvasAction(t.history.actions.clearedAllHeaders, () => {
2295
+ dispatch({ type: "CLEAR_ALL_HEADERS" });
2296
+ });
2297
+ }, [dispatch, runCanvasAction, t.history.actions.clearedAllHeaders]);
2298
+ const handleClearAllFooters = React.useCallback(() => {
2299
+ runCanvasAction(t.history.actions.clearedAllFooters, () => {
2300
+ dispatch({ type: "CLEAR_ALL_FOOTERS" });
2301
+ });
2302
+ }, [dispatch, runCanvasAction, t.history.actions.clearedAllFooters]);
2303
+ const handlePageCounterModeChange = React.useCallback((mode) => {
2304
+ runCanvasAction(interpolate(t.history.actions.pageCounterSet, { value: mode }), () => {
2305
+ dispatch({ type: "SET_PAGE_COUNTER_MODE", mode });
2306
+ });
2307
+ }, [dispatch, runCanvasAction, t.history.actions.pageCounterSet]);
2308
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group lex4-toolbar-group-gap", "data-testid": "header-footer-controls", children: [
2309
+ /* @__PURE__ */ jsxRuntime.jsx(
2310
+ HeaderFooterToggle,
2311
+ {
2312
+ enabled: document2.headerFooterEnabled,
2313
+ onToggle: handleToggle
2314
+ }
2315
+ ),
2316
+ document2.headerFooterEnabled && /* @__PURE__ */ jsxRuntime.jsx(
2317
+ HeaderFooterActions,
2318
+ {
2319
+ activePageId,
2320
+ pageCounterMode: document2.pageCounterMode,
2321
+ onPageCounterModeChange: handlePageCounterModeChange,
2322
+ onCopyHeaderToAll: handleCopyHeaderToAll,
2323
+ onCopyFooterToAll: handleCopyFooterToAll,
2324
+ onClearHeader: handleClearHeader,
2325
+ onClearFooter: handleClearFooter,
2326
+ onClearAllHeaders: handleClearAllHeaders,
2327
+ onClearAllFooters: handleClearAllFooters
2328
+ }
2329
+ )
2330
+ ] });
2331
+ };
2217
2332
  const Toolbar = () => {
2218
2333
  const {
2219
- document: document2,
2220
- dispatch,
2221
- activePageId,
2222
2334
  activeEditor,
2223
2335
  canRedo,
2224
2336
  canUndo,
@@ -2230,7 +2342,7 @@ const Toolbar = () => {
2230
2342
  setHistorySidebarOpen,
2231
2343
  undo
2232
2344
  } = useDocument();
2233
- const { toolbarItems } = useExtensions();
2345
+ const { toolbarItems, toolbarEndItems } = useExtensions();
2234
2346
  const t = useTranslations();
2235
2347
  const withBodySelection = React.useCallback(
2236
2348
  (editor, action) => {
@@ -2267,53 +2379,6 @@ const Toolbar = () => {
2267
2379
  },
2268
2380
  [runHistoryAction]
2269
2381
  );
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
2382
  const handleBold = React.useCallback(() => {
2318
2383
  debug("toolbar", `bold (globalSelection=${globalSelectionActive}, editors=${editorRegistry.all().length}, hasEditor=${!!activeEditor})`);
2319
2384
  runToolbarAction(t.history.actions.boldApplied, () => {
@@ -2395,122 +2460,111 @@ const Toolbar = () => {
2395
2460
  },
2396
2461
  [applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
2397
2462
  );
2398
- return /* @__PURE__ */ jsxRuntime.jsxs(
2463
+ const handleToggleHistory = React.useCallback(() => {
2464
+ setHistorySidebarOpen(!historySidebarOpen);
2465
+ }, [historySidebarOpen, setHistorySidebarOpen]);
2466
+ return /* @__PURE__ */ jsxRuntime.jsx(
2399
2467
  "div",
2400
2468
  {
2401
2469
  className: "lex4-toolbar",
2402
2470
  "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(
2471
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-row", children: [
2472
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "history-controls", children: [
2473
+ /* @__PURE__ */ jsxRuntime.jsx(
2480
2474
  ToolbarIconButton,
2481
2475
  {
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 })
2476
+ title: t.toolbar.undo,
2477
+ testId: "btn-undo",
2478
+ disabled: !canUndo,
2479
+ onClick: undo,
2480
+ children: /* @__PURE__ */ jsxRuntime.jsx(Undo2, { size: 15 })
2481
+ }
2482
+ ),
2483
+ /* @__PURE__ */ jsxRuntime.jsx(
2484
+ ToolbarIconButton,
2485
+ {
2486
+ title: t.toolbar.redo,
2487
+ testId: "btn-redo",
2488
+ disabled: !canRedo,
2489
+ onClick: redo,
2490
+ children: /* @__PURE__ */ jsxRuntime.jsx(Redo2, { size: 15 })
2487
2491
  }
2488
- ) })
2492
+ )
2489
2493
  ] }),
2490
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-hf-row", children: [
2494
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2495
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2496
+ /* @__PURE__ */ jsxRuntime.jsx(Type, { size: 14, className: "lex4-toolbar-icon" }),
2491
2497
  /* @__PURE__ */ jsxRuntime.jsx(
2492
- HeaderFooterToggle,
2498
+ "select",
2493
2499
  {
2494
- enabled: document2.headerFooterEnabled,
2495
- onToggle: handleToggle
2500
+ className: "lex4-toolbar-select",
2501
+ "data-testid": "font-selector",
2502
+ defaultValue: "Calibri",
2503
+ onChange: handleFontChange,
2504
+ children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
2496
2505
  }
2497
- ),
2498
- document2.headerFooterEnabled && /* @__PURE__ */ jsxRuntime.jsx(
2499
- HeaderFooterActions,
2506
+ )
2507
+ ] }),
2508
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2509
+ /* @__PURE__ */ jsxRuntime.jsx(ALargeSmall, { size: 14, className: "lex4-toolbar-icon" }),
2510
+ /* @__PURE__ */ jsxRuntime.jsx(
2511
+ "select",
2512
+ {
2513
+ className: "lex4-toolbar-select lex4-toolbar-select-narrow",
2514
+ "data-testid": "font-size-selector",
2515
+ defaultValue: "12",
2516
+ onChange: handleFontSizeChange,
2517
+ children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: size, children: size }, size))
2518
+ }
2519
+ )
2520
+ ] }),
2521
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2522
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "format-group", children: [
2523
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsxRuntime.jsx(Bold, { size: 15 }) }),
2524
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsxRuntime.jsx(Italic, { size: 15 }) }),
2525
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsxRuntime.jsx(Underline, { size: 15 }) }),
2526
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsxRuntime.jsx(Strikethrough, { size: 15 }) })
2527
+ ] }),
2528
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2529
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "align-group", children: [
2530
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignStart, { size: 15 }) }),
2531
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignCenter, { size: 15 }) }),
2532
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignEnd, { size: 15 }) }),
2533
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsxRuntime.jsx(TextAlignJustify, { size: 15 }) })
2534
+ ] }),
2535
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2536
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-group", "data-testid": "list-group", children: [
2537
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsxRuntime.jsx(ListOrdered, { size: 15 }) }),
2538
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsxRuntime.jsx(List, { size: 15 }) }),
2539
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentIncrease, { size: 15 }) }),
2540
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsxRuntime.jsx(ListIndentDecrease, { size: 15 }) })
2541
+ ] }),
2542
+ toolbarItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2543
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2544
+ toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(ToolbarItem, {}, idx))
2545
+ ] }),
2546
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, {}),
2547
+ /* @__PURE__ */ jsxRuntime.jsx(CanvasControls, {}),
2548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-toolbar-end", children: [
2549
+ toolbarEndItems.map((EndItem, idx) => /* @__PURE__ */ jsxRuntime.jsx(EndItem, {}, idx)),
2550
+ /* @__PURE__ */ jsxRuntime.jsxs(
2551
+ "button",
2500
2552
  {
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
2553
+ type: "button",
2554
+ className: `lex4-toolbar-toggle-btn${historySidebarOpen ? " active" : ""}`,
2555
+ onMouseDown: (e) => e.preventDefault(),
2556
+ onClick: handleToggleHistory,
2557
+ "data-testid": "toggle-history-sidebar",
2558
+ title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2559
+ "aria-label": historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2560
+ children: [
2561
+ /* @__PURE__ */ jsxRuntime.jsx(History, { size: 14 }),
2562
+ "History"
2563
+ ]
2510
2564
  }
2511
2565
  )
2512
2566
  ] })
2513
- ]
2567
+ ] })
2514
2568
  }
2515
2569
  );
2516
2570
  };
@@ -4630,8 +4684,8 @@ function selectEntireDocument(rootElement, selectionBuffer) {
4630
4684
  selectionBuffer.focus();
4631
4685
  selectionBuffer.select();
4632
4686
  }
4633
- const GLOBAL_SELECTION_BACKGROUND = "rgb(191, 219, 254)";
4634
- const GLOBAL_SELECTION_FOREGROUND = "rgb(30, 64, 175)";
4687
+ const GLOBAL_SELECTION_BACKGROUND = "hsl(214 95% 87%)";
4688
+ const GLOBAL_SELECTION_FOREGROUND = "hsl(224 71% 25%)";
4635
4689
  function isFormFieldTarget(target) {
4636
4690
  const element = target;
4637
4691
  const tagName = element == null ? void 0 : element.tagName;
@@ -4747,8 +4801,8 @@ const EditorChrome = ({
4747
4801
  )) ?? [];
4748
4802
  const root = rootRef.current;
4749
4803
  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;
4804
+ const selBg = (styles == null ? void 0 : styles.getPropertyValue("--color-selection-bg").trim()) || GLOBAL_SELECTION_BACKGROUND;
4805
+ const selFg = (styles == null ? void 0 : styles.getPropertyValue("--color-selection-text").trim()) || GLOBAL_SELECTION_FOREGROUND;
4752
4806
  editableRoots.forEach((editableRoot) => {
4753
4807
  editableRoot.style.backgroundColor = globalSelectionActive ? selBg : "";
4754
4808
  editableRoot.style.color = globalSelectionActive ? selFg : "";
@@ -4819,19 +4873,31 @@ const EditorChrome = ({
4819
4873
  );
4820
4874
  };
4821
4875
  const EditorWithHandle = React.forwardRef(({ captureHistoryShortcutsOnWindow, onSave, className }, ref) => {
4822
- const { document: doc, activeEditor } = useDocument();
4876
+ const {
4877
+ document: doc,
4878
+ activeEditor,
4879
+ historySidebarOpen,
4880
+ setHistorySidebarOpen
4881
+ } = useDocument();
4823
4882
  const { handleFactories } = useExtensions();
4824
4883
  const getDocument = React.useCallback(() => doc, [doc]);
4825
4884
  const getActiveEditor = React.useCallback(() => activeEditor, [activeEditor]);
4826
4885
  const extensionCtx = useExtensionContext(getDocument, getActiveEditor);
4827
4886
  React.useImperativeHandle(ref, () => {
4828
- const handle = {};
4887
+ const handle = {
4888
+ setHistorySidebarOpen: (open) => {
4889
+ setHistorySidebarOpen(open);
4890
+ },
4891
+ toggleHistorySidebar: () => {
4892
+ setHistorySidebarOpen(!historySidebarOpen);
4893
+ }
4894
+ };
4829
4895
  for (const factory of handleFactories) {
4830
4896
  const methods = factory(extensionCtx);
4831
4897
  Object.assign(handle, methods);
4832
4898
  }
4833
4899
  return handle;
4834
- }, [extensionCtx, handleFactories]);
4900
+ }, [extensionCtx, handleFactories, historySidebarOpen, setHistorySidebarOpen]);
4835
4901
  return /* @__PURE__ */ jsxRuntime.jsx(
4836
4902
  EditorChrome,
4837
4903
  {
@@ -5053,14 +5119,16 @@ class VariableNode extends lexical.DecoratorNode {
5053
5119
  }
5054
5120
  }
5055
5121
  function VariableChip({ variableKey }) {
5056
- var _a;
5057
5122
  const { getDefinition } = useVariables();
5058
- const label = ((_a = getDefinition(variableKey)) == null ? void 0 : _a.label) ?? variableKey;
5123
+ const def = getDefinition(variableKey);
5124
+ const label = (def == null ? void 0 : def.label) ?? variableKey;
5125
+ const group = def == null ? void 0 : def.group;
5059
5126
  return /* @__PURE__ */ jsxRuntime.jsx(
5060
5127
  "span",
5061
5128
  {
5062
5129
  className: "lex4-variable-chip",
5063
5130
  "data-testid": `variable-chip-${variableKey}`,
5131
+ "data-variable-group": group,
5064
5132
  title: variableKey,
5065
5133
  children: label
5066
5134
  }
@@ -5092,106 +5160,19 @@ const VariablePlugin = () => {
5092
5160
  }, [editor]);
5093
5161
  return null;
5094
5162
  };
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
5163
  const VariablePanel = ({ open, onClose }) => {
5191
- const { definitions } = useVariables();
5164
+ const { definitions, refreshDefinitions } = useVariables();
5192
5165
  const { activeEditor, runHistoryAction } = useDocument();
5193
5166
  const t = useTranslations();
5194
5167
  const [filter, setFilter] = React.useState("");
5168
+ const [creating, setCreating] = React.useState(false);
5169
+ const [createError, setCreateError] = React.useState(null);
5170
+ const [draft, setDraft] = React.useState({
5171
+ label: "",
5172
+ key: "",
5173
+ group: "",
5174
+ valueType: "string"
5175
+ });
5195
5176
  const filtered = React.useMemo(() => {
5196
5177
  if (!filter) return definitions;
5197
5178
  const lower = filter.toLowerCase();
@@ -5220,8 +5201,51 @@ const VariablePanel = ({ open, onClose }) => {
5220
5201
  },
5221
5202
  [activeEditor, runHistoryAction]
5222
5203
  );
5223
- if (definitions.length === 0) return null;
5224
- return /* @__PURE__ */ jsxRuntime.jsxs(
5204
+ const resetCreateState = React.useCallback(() => {
5205
+ setDraft({
5206
+ label: "",
5207
+ key: "",
5208
+ group: "",
5209
+ valueType: "string"
5210
+ });
5211
+ setCreateError(null);
5212
+ setCreating(false);
5213
+ }, []);
5214
+ const handleCreateVariable = React.useCallback((event) => {
5215
+ event.preventDefault();
5216
+ const label = draft.label.trim();
5217
+ const key = draft.key.trim();
5218
+ const group = draft.group.trim();
5219
+ if (!label || !key) {
5220
+ setCreateError(t.variables.createVariableMissingFields);
5221
+ return;
5222
+ }
5223
+ if (definitions.some((def) => def.key === key)) {
5224
+ setCreateError(interpolate(t.variables.createVariableDuplicateKey, { key }));
5225
+ return;
5226
+ }
5227
+ refreshDefinitions([
5228
+ ...definitions,
5229
+ {
5230
+ key,
5231
+ label,
5232
+ group: group || void 0,
5233
+ valueType: draft.valueType
5234
+ }
5235
+ ]);
5236
+ resetCreateState();
5237
+ }, [
5238
+ definitions,
5239
+ draft.group,
5240
+ draft.key,
5241
+ draft.label,
5242
+ draft.valueType,
5243
+ refreshDefinitions,
5244
+ resetCreateState,
5245
+ t.variables.createVariableDuplicateKey,
5246
+ t.variables.createVariableMissingFields
5247
+ ]);
5248
+ return /* @__PURE__ */ jsxRuntime.jsx(
5225
5249
  EditorSidebar,
5226
5250
  {
5227
5251
  title: t.variables.title,
@@ -5239,7 +5263,7 @@ const VariablePanel = ({ open, onClose }) => {
5239
5263
  children: /* @__PURE__ */ jsxRuntime.jsx(RefreshCw, { size: 12 })
5240
5264
  }
5241
5265
  ),
5242
- children: [
5266
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-panel-content", children: [
5243
5267
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-search-container", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-search-wrapper", children: [
5244
5268
  /* @__PURE__ */ jsxRuntime.jsx(Search, { size: 14, className: "lex4-variable-search-icon" }),
5245
5269
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5256,23 +5280,109 @@ const VariablePanel = ({ open, onClose }) => {
5256
5280
  ] }) }),
5257
5281
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-list", children: [
5258
5282
  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
- ]
5283
+ Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-group", "data-variable-group": group, children: [
5284
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-label", children: group }),
5285
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-group-items", children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(
5286
+ "button",
5287
+ {
5288
+ type: "button",
5289
+ className: "lex4-variable-list-item",
5290
+ "data-testid": `variable-panel-${def.key}`,
5291
+ "data-variable-group": group,
5292
+ onClick: () => handleInsert(def.key),
5293
+ disabled: !activeEditor,
5294
+ title: def.key,
5295
+ children: def.label
5296
+ },
5297
+ def.key
5298
+ )) })
5299
+ ] }, group))
5300
+ ] }),
5301
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create", children: creating ? /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "lex4-variable-create-form", onSubmit: handleCreateVariable, children: [
5302
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create-title", children: t.variables.createVariableTitle }),
5303
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5304
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableLabel }),
5305
+ /* @__PURE__ */ jsxRuntime.jsx(
5306
+ "input",
5307
+ {
5308
+ value: draft.label,
5309
+ onChange: (e) => {
5310
+ setDraft((current) => ({ ...current, label: e.target.value }));
5311
+ if (createError) {
5312
+ setCreateError(null);
5313
+ }
5314
+ }
5315
+ }
5316
+ )
5317
+ ] }),
5318
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5319
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableKey }),
5320
+ /* @__PURE__ */ jsxRuntime.jsx(
5321
+ "input",
5322
+ {
5323
+ value: draft.key,
5324
+ onChange: (e) => {
5325
+ setDraft((current) => ({ ...current, key: e.target.value }));
5326
+ if (createError) {
5327
+ setCreateError(null);
5328
+ }
5329
+ }
5330
+ }
5331
+ )
5332
+ ] }),
5333
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-create-row", children: [
5334
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5335
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableGroup }),
5336
+ /* @__PURE__ */ jsxRuntime.jsx(
5337
+ "input",
5338
+ {
5339
+ value: draft.group,
5340
+ onChange: (e) => {
5341
+ setDraft((current) => ({ ...current, group: e.target.value }));
5342
+ }
5343
+ }
5344
+ )
5345
+ ] }),
5346
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "lex4-variable-create-field", children: [
5347
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.variables.createVariableType }),
5348
+ /* @__PURE__ */ jsxRuntime.jsxs(
5349
+ "select",
5350
+ {
5351
+ value: draft.valueType,
5352
+ onChange: (e) => {
5353
+ setDraft((current) => ({
5354
+ ...current,
5355
+ valueType: e.target.value
5356
+ }));
5357
+ },
5358
+ children: [
5359
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "string", children: "string" }),
5360
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "number" }),
5361
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "date", children: "date" }),
5362
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "boolean" })
5363
+ ]
5364
+ }
5365
+ )
5366
+ ] })
5367
+ ] }),
5368
+ createError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lex4-variable-create-error", children: createError }),
5369
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lex4-variable-create-actions", children: [
5370
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lex4-variable-create-cancel", onClick: resetCreateState, children: t.variables.createVariableCancel }),
5371
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "lex4-variable-create-submit", children: t.variables.createVariableSave })
5372
+ ] })
5373
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
5374
+ "button",
5375
+ {
5376
+ type: "button",
5377
+ className: "lex4-variable-create-toggle",
5378
+ onClick: () => setCreating(true),
5379
+ children: [
5380
+ "+ ",
5381
+ t.variables.newVariable
5382
+ ]
5383
+ }
5384
+ ) })
5385
+ ] })
5276
5386
  }
5277
5387
  );
5278
5388
  };
@@ -5288,41 +5398,23 @@ const VariablePanelStateProvider = ({ children }) => {
5288
5398
  const [panelOpen, setPanelOpen] = React.useState(false);
5289
5399
  return /* @__PURE__ */ jsxRuntime.jsx(VariablePanelContext.Provider, { value: { panelOpen, setPanelOpen }, children });
5290
5400
  };
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 = () => {
5401
+ const VariableToolbarToggle = () => {
5313
5402
  const { panelOpen, setPanelOpen } = useVariablePanelState();
5314
5403
  const t = useTranslations();
5315
- return /* @__PURE__ */ jsxRuntime.jsx(
5404
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5316
5405
  "button",
5317
5406
  {
5318
5407
  type: "button",
5319
- title: panelOpen ? t.variables.closePanel : t.variables.openPanel,
5320
- "aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
5408
+ className: `lex4-toolbar-toggle-btn${panelOpen ? " active" : ""}`,
5321
5409
  onMouseDown: (e) => e.preventDefault(),
5322
5410
  onClick: () => setPanelOpen(!panelOpen),
5323
- className: `lex4-toolbar-btn${panelOpen ? " active" : ""}`,
5324
5411
  "data-testid": "toggle-variable-panel",
5325
- children: /* @__PURE__ */ jsxRuntime.jsx(Braces, { size: 15 })
5412
+ title: panelOpen ? t.variables.closePanel : t.variables.openPanel,
5413
+ "aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
5414
+ children: [
5415
+ /* @__PURE__ */ jsxRuntime.jsx(Braces, { size: 14 }),
5416
+ "Variables"
5417
+ ]
5326
5418
  }
5327
5419
  );
5328
5420
  };
@@ -5330,6 +5422,15 @@ const VariablePanelWithState = () => {
5330
5422
  const { panelOpen, setPanelOpen } = useVariablePanelState();
5331
5423
  return /* @__PURE__ */ jsxRuntime.jsx(VariablePanel, { open: panelOpen, onClose: () => setPanelOpen(false) });
5332
5424
  };
5425
+ const VariablePanelStateSync = ({ children }) => {
5426
+ const { panelOpen, setPanelOpen } = useVariablePanelState();
5427
+ const extState = useExtensionState();
5428
+ React.useEffect(() => {
5429
+ extState.set("variablePanelOpen", panelOpen);
5430
+ extState.set("setVariablePanelOpen", setPanelOpen);
5431
+ }, [extState, panelOpen, setPanelOpen]);
5432
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
5433
+ };
5333
5434
  const VariableDefinitionsSync = ({ children }) => {
5334
5435
  const { definitions } = useVariables();
5335
5436
  const extState = useExtensionState();
@@ -5340,13 +5441,13 @@ const VariableDefinitionsSync = ({ children }) => {
5340
5441
  };
5341
5442
  function variablesExtension(definitions = []) {
5342
5443
  const ProviderWrapper = ({ children }) => {
5343
- return /* @__PURE__ */ jsxRuntime.jsx(VariablePanelStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(VariableProvider, { initialDefinitions: definitions, children: /* @__PURE__ */ jsxRuntime.jsx(VariableDefinitionsSync, { children }) }) });
5444
+ return /* @__PURE__ */ jsxRuntime.jsx(VariablePanelStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(VariableProvider, { initialDefinitions: definitions, children: /* @__PURE__ */ jsxRuntime.jsx(VariablePanelStateSync, { children: /* @__PURE__ */ jsxRuntime.jsx(VariableDefinitionsSync, { children }) }) }) });
5344
5445
  };
5345
5446
  return {
5346
5447
  name: "variables",
5347
5448
  nodes: [VariableNode],
5348
5449
  bodyPlugins: [VariablePlugin],
5349
- toolbarItems: [VariableToolbarItem, VariablePanelToggle],
5450
+ toolbarEndItems: [VariableToolbarToggle],
5350
5451
  sidePanel: VariablePanelWithState,
5351
5452
  provider: ProviderWrapper,
5352
5453
  handleMethods: (ctx) => ({
@@ -5358,6 +5459,15 @@ function variablesExtension(definitions = []) {
5358
5459
  },
5359
5460
  refreshVariables: (newDefs) => {
5360
5461
  ctx.setExtensionState("variableDefinitions", newDefs);
5462
+ },
5463
+ setVariablePanelOpen: (open) => {
5464
+ const setter = ctx.getExtensionState("setVariablePanelOpen");
5465
+ setter == null ? void 0 : setter(open);
5466
+ },
5467
+ toggleVariablePanel: () => {
5468
+ const current = ctx.getExtensionState("variablePanelOpen") ?? false;
5469
+ const setter = ctx.getExtensionState("setVariablePanelOpen");
5470
+ setter == null ? void 0 : setter(!current);
5361
5471
  }
5362
5472
  })
5363
5473
  };