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