@yurikilian/lex4 0.3.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +71 -4
  2. package/dist/ast/document-serializer.d.ts.map +1 -1
  3. package/dist/components/EditorSidebar.d.ts.map +1 -1
  4. package/dist/components/HeaderFooterActions.d.ts.map +1 -1
  5. package/dist/components/HeaderFooterToggle.d.ts.map +1 -1
  6. package/dist/components/HistorySidebar.d.ts.map +1 -1
  7. package/dist/components/Lex4Editor.d.ts.map +1 -1
  8. package/dist/components/PageBody.d.ts.map +1 -1
  9. package/dist/components/PageView.d.ts.map +1 -1
  10. package/dist/components/Toolbar.d.ts.map +1 -1
  11. package/dist/components/VariablePanel.d.ts.map +1 -1
  12. package/dist/components/VariablePicker.d.ts.map +1 -1
  13. package/dist/constants/dimensions.d.ts +6 -2
  14. package/dist/constants/dimensions.d.ts.map +1 -1
  15. package/dist/constants/index.d.ts +1 -1
  16. package/dist/constants/index.d.ts.map +1 -1
  17. package/dist/constants/page-layout.d.ts +0 -4
  18. package/dist/constants/page-layout.d.ts.map +1 -1
  19. package/dist/extensions/ast-extension.d.ts +9 -0
  20. package/dist/extensions/ast-extension.d.ts.map +1 -1
  21. package/dist/extensions/extension-context.d.ts +3 -1
  22. package/dist/extensions/extension-context.d.ts.map +1 -1
  23. package/dist/extensions/types.d.ts +12 -0
  24. package/dist/extensions/types.d.ts.map +1 -1
  25. package/dist/extensions/variables-extension.d.ts +6 -0
  26. package/dist/extensions/variables-extension.d.ts.map +1 -1
  27. package/dist/lex4-editor.cjs +402 -386
  28. package/dist/lex4-editor.cjs.map +1 -1
  29. package/dist/lex4-editor.js +402 -386
  30. package/dist/lex4-editor.js.map +1 -1
  31. package/dist/lexical/theme.d.ts +1 -1
  32. package/dist/style.css +789 -695
  33. package/dist/types/editor-handle.d.ts +12 -7
  34. package/dist/types/editor-handle.d.ts.map +1 -1
  35. package/dist/types/editor-props.d.ts +2 -1
  36. package/dist/types/editor-props.d.ts.map +1 -1
  37. package/dist/variables/variable-node.d.ts.map +1 -1
  38. package/package.json +1 -1
@@ -59,7 +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_PX = 40;
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);
63
66
  const MAX_HEADER_RATIO = 0.2;
64
67
  const MAX_FOOTER_RATIO = 0.2;
65
68
  const MAX_HEADER_HEIGHT_PX = Math.round(A4_HEIGHT_PX * MAX_HEADER_RATIO);
@@ -1740,21 +1743,21 @@ const EditorSidebar = ({
1740
1743
  return /* @__PURE__ */ jsxs(
1741
1744
  "aside",
1742
1745
  {
1743
- className: "flex h-full w-[320px] shrink-0 flex-col border-l border-gray-200 bg-white",
1746
+ className: "lex4-sidebar",
1744
1747
  "data-testid": testId,
1745
1748
  children: [
1746
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between border-b border-gray-200 px-3 py-2.5", children: [
1749
+ /* @__PURE__ */ jsxs("div", { className: "lex4-sidebar-header", children: [
1747
1750
  /* @__PURE__ */ jsxs("div", { children: [
1748
- /* @__PURE__ */ jsx("h2", { className: "text-xs font-semibold text-gray-900", children: title }),
1749
- subtitle && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-gray-500", children: subtitle })
1751
+ /* @__PURE__ */ jsx("h2", { className: "lex4-sidebar-title", children: title }),
1752
+ subtitle && /* @__PURE__ */ jsx("p", { className: "lex4-sidebar-subtitle", children: subtitle })
1750
1753
  ] }),
1751
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1754
+ /* @__PURE__ */ jsxs("div", { className: "lex4-sidebar-actions", children: [
1752
1755
  headerActions,
1753
1756
  onClose && /* @__PURE__ */ jsx(
1754
1757
  "button",
1755
1758
  {
1756
1759
  type: "button",
1757
- className: "flex h-6 w-6 items-center justify-center rounded text-gray-400\n transition-colors hover:bg-gray-100 hover:text-gray-600",
1760
+ className: "lex4-sidebar-action-btn",
1758
1761
  "data-testid": "close-editor-sidebar",
1759
1762
  onClick: onClose,
1760
1763
  "aria-label": t.sidebar.close,
@@ -1763,7 +1766,7 @@ const EditorSidebar = ({
1763
1766
  )
1764
1767
  ] })
1765
1768
  ] }),
1766
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto", children })
1769
+ /* @__PURE__ */ jsx("div", { className: "lex4-sidebar-body", children })
1767
1770
  ]
1768
1771
  }
1769
1772
  );
@@ -1795,7 +1798,7 @@ const HistorySidebar = () => {
1795
1798
  "aria-label": t.history.clearHistory,
1796
1799
  onMouseDown: (e) => e.preventDefault(),
1797
1800
  onClick: () => clearHistory("manual"),
1798
- className: "flex h-6 w-6 items-center justify-center rounded text-gray-400\n transition-colors hover:bg-gray-100 hover:text-gray-600",
1801
+ className: "lex4-sidebar-action-btn",
1799
1802
  "data-testid": "clear-history",
1800
1803
  children: /* @__PURE__ */ jsx(Trash2, { size: 13 })
1801
1804
  }
@@ -1812,27 +1815,27 @@ const HistorySidebar = () => {
1812
1815
  children: visibleEntries.length === 0 ? /* @__PURE__ */ jsx(
1813
1816
  "div",
1814
1817
  {
1815
- className: "px-3 py-4 text-xs text-gray-500",
1818
+ className: "lex4-history-empty",
1816
1819
  "data-testid": "history-empty",
1817
1820
  children: t.history.empty
1818
1821
  }
1819
- ) : /* @__PURE__ */ jsx("ol", { className: "divide-y divide-gray-100", "data-testid": "history-entry-list", children: visibleEntries.map((entry, reversedIndex) => {
1822
+ ) : /* @__PURE__ */ jsx("ol", { className: "lex4-history-list", "data-testid": "history-entry-list", children: visibleEntries.map((entry, reversedIndex) => {
1820
1823
  const actualIndex = historyEntries.length - reversedIndex - 1;
1821
1824
  const isCurrent = actualIndex === historyCursor - 1;
1822
1825
  return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
1823
1826
  "button",
1824
1827
  {
1825
1828
  type: "button",
1826
- className: `w-full px-3 py-2 text-left transition-colors ${isCurrent ? "bg-blue-50" : "bg-white hover:bg-gray-50"}`,
1829
+ className: `lex4-history-entry ${isCurrent ? "active" : ""}`,
1827
1830
  "data-testid": `history-entry-${actualIndex}`,
1828
1831
  "data-history-current": isCurrent ? "true" : "false",
1829
1832
  onClick: () => jumpToHistoryEntry2(actualIndex),
1830
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1831
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1832
- /* @__PURE__ */ jsx("div", { className: `text-xs ${isCurrent ? "font-semibold text-blue-700" : "text-gray-900"}`, children: entry.label }),
1833
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-gray-400", children: t.regions[entry.source] ?? entry.source })
1833
+ children: /* @__PURE__ */ jsxs("div", { className: "lex4-history-entry-row", children: [
1834
+ /* @__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 })
1834
1837
  ] }),
1835
- /* @__PURE__ */ jsx("div", { className: "shrink-0 text-xs text-gray-400", children: formatTimestamp(entry.timestamp) })
1838
+ /* @__PURE__ */ jsx("div", { className: "lex4-history-entry-time", children: formatTimestamp(entry.timestamp) })
1836
1839
  ] })
1837
1840
  }
1838
1841
  ) }, entry.id);
@@ -1848,11 +1851,11 @@ const HeaderFooterToggle = ({
1848
1851
  return /* @__PURE__ */ jsxs(
1849
1852
  "label",
1850
1853
  {
1851
- className: "flex items-center gap-1.5 cursor-pointer select-none",
1854
+ className: "lex4-hf-toggle",
1852
1855
  "data-testid": "header-footer-toggle",
1853
1856
  children: [
1854
- /* @__PURE__ */ jsx(Rows3, { size: 14, className: "text-gray-500" }),
1855
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-600", children: t.headerFooter.label }),
1857
+ /* @__PURE__ */ jsx(Rows3, { size: 14, className: "lex4-hf-toggle-icon" }),
1858
+ /* @__PURE__ */ jsx("span", { className: "lex4-hf-toggle-label", children: t.headerFooter.label }),
1856
1859
  /* @__PURE__ */ jsx(
1857
1860
  "button",
1858
1861
  {
@@ -1861,22 +1864,10 @@ const HeaderFooterToggle = ({
1861
1864
  "aria-checked": enabled,
1862
1865
  onMouseDown: (e) => e.preventDefault(),
1863
1866
  onClick: () => onToggle(!enabled),
1864
- className: `
1865
- relative inline-flex h-4 w-7 items-center rounded-full
1866
- transition-colors duration-200
1867
- ${enabled ? "bg-blue-500" : "bg-gray-300"}
1868
- `,
1867
+ className: "lex4-hf-switch",
1868
+ style: { backgroundColor: enabled ? "var(--lex4-color-primary)" : "var(--lex4-color-text-disabled)" },
1869
1869
  "data-testid": "header-footer-switch",
1870
- children: /* @__PURE__ */ jsx(
1871
- "span",
1872
- {
1873
- className: `
1874
- inline-block h-3 w-3 rounded-full bg-white shadow-sm
1875
- transition-transform duration-200
1876
- ${enabled ? "translate-x-3.5" : "translate-x-0.5"}
1877
- `
1878
- }
1879
- )
1870
+ children: /* @__PURE__ */ jsx("span", { className: "lex4-hf-switch-knob" })
1880
1871
  }
1881
1872
  )
1882
1873
  ]
@@ -1918,17 +1909,14 @@ const HeaderFooterActions = ({
1918
1909
  action();
1919
1910
  close();
1920
1911
  };
1921
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", "data-testid": "header-footer-actions", children: [
1912
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, style: { position: "relative" }, "data-testid": "header-footer-actions", children: [
1922
1913
  /* @__PURE__ */ jsxs(
1923
1914
  "button",
1924
1915
  {
1925
1916
  type: "button",
1926
1917
  onMouseDown: (e) => e.preventDefault(),
1927
1918
  onClick: () => setOpen(!open),
1928
- className: `
1929
- flex h-7 items-center gap-1 rounded px-1.5 text-xs transition-colors
1930
- ${open ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}
1931
- `,
1919
+ className: "lex4-settings-trigger",
1932
1920
  "data-testid": "header-footer-menu-trigger",
1933
1921
  "aria-expanded": open,
1934
1922
  "aria-haspopup": "true",
@@ -1941,12 +1929,12 @@ const HeaderFooterActions = ({
1941
1929
  open && /* @__PURE__ */ jsxs(
1942
1930
  "div",
1943
1931
  {
1944
- className: "absolute left-0 top-full mt-1 z-50 w-56 rounded-lg border border-gray-200 bg-white py-1 shadow-lg",
1932
+ className: "lex4-settings-menu",
1945
1933
  role: "menu",
1946
1934
  "data-testid": "header-footer-menu",
1947
1935
  children: [
1948
1936
  /* @__PURE__ */ jsx(SectionLabel, { children: "Page counter" }),
1949
- /* @__PURE__ */ jsx("div", { className: "px-3 pb-2 grid grid-cols-2 gap-1", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx(
1937
+ /* @__PURE__ */ jsx("div", { className: "lex4-settings-counter-grid", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx(
1950
1938
  "button",
1951
1939
  {
1952
1940
  type: "button",
@@ -1954,10 +1942,7 @@ const HeaderFooterActions = ({
1954
1942
  "aria-checked": pageCounterMode === value,
1955
1943
  onMouseDown: (e) => e.preventDefault(),
1956
1944
  onClick: () => onPageCounterModeChange(value),
1957
- className: `
1958
- rounded px-2 py-1 text-xs text-left transition-colors
1959
- ${pageCounterMode === value ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-600 hover:bg-gray-100"}
1960
- `,
1945
+ className: "lex4-settings-counter-btn",
1961
1946
  "data-testid": `page-counter-${value}`,
1962
1947
  children: label
1963
1948
  },
@@ -2030,7 +2015,7 @@ const HeaderFooterActions = ({
2030
2015
  )
2031
2016
  ] });
2032
2017
  };
2033
- const SectionLabel = ({ children }) => /* @__PURE__ */ jsx("div", { className: "px-3 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-gray-400", children });
2018
+ const SectionLabel = ({ children }) => /* @__PURE__ */ jsx("div", { className: "lex4-settings-label", children });
2034
2019
  const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */ jsxs(
2035
2020
  "button",
2036
2021
  {
@@ -2039,7 +2024,7 @@ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */
2039
2024
  onMouseDown: (e) => e.preventDefault(),
2040
2025
  onClick,
2041
2026
  disabled,
2042
- className: "flex w-full items-center gap-2 px-3 py-1.5 text-xs text-gray-700 transition-colors\n hover:bg-gray-50 disabled:cursor-not-allowed disabled:text-gray-300",
2027
+ className: "lex4-settings-item",
2043
2028
  "data-testid": testId,
2044
2029
  children: [
2045
2030
  icon,
@@ -2047,7 +2032,7 @@ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */
2047
2032
  ]
2048
2033
  }
2049
2034
  );
2050
- const MenuDivider = () => /* @__PURE__ */ jsx("div", { className: "my-1 h-px bg-gray-100" });
2035
+ const MenuDivider = () => /* @__PURE__ */ jsx("div", { className: "lex4-settings-divider" });
2051
2036
  function resolveExtensions(extensions) {
2052
2037
  const resolved = {
2053
2038
  nodes: [],
@@ -2056,6 +2041,8 @@ function resolveExtensions(extensions) {
2056
2041
  sidePanels: [],
2057
2042
  providers: [],
2058
2043
  themeOverrides: {},
2044
+ cssVariables: {},
2045
+ rootClassNames: [],
2059
2046
  handleFactories: []
2060
2047
  };
2061
2048
  for (const ext of extensions) {
@@ -2067,6 +2054,10 @@ function resolveExtensions(extensions) {
2067
2054
  if (ext.themeOverrides) {
2068
2055
  resolved.themeOverrides = { ...resolved.themeOverrides, ...ext.themeOverrides };
2069
2056
  }
2057
+ if (ext.cssVariables) {
2058
+ Object.assign(resolved.cssVariables, ext.cssVariables);
2059
+ }
2060
+ if (ext.rootClassName) resolved.rootClassNames.push(ext.rootClassName);
2070
2061
  if (ext.handleMethods) resolved.handleFactories.push(ext.handleMethods);
2071
2062
  }
2072
2063
  return resolved;
@@ -2078,14 +2069,27 @@ const EMPTY_RESOLVED = {
2078
2069
  sidePanels: [],
2079
2070
  providers: [],
2080
2071
  themeOverrides: {},
2072
+ cssVariables: {},
2073
+ rootClassNames: [],
2081
2074
  handleFactories: []
2082
2075
  };
2083
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
2084
  const ExtensionProvider = ({ extensions, children }) => {
2085
- const resolved = useMemo(
2086
- () => extensions && extensions.length > 0 ? resolveExtensions(extensions) : EMPTY_RESOLVED,
2087
- [extensions]
2088
- );
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;
2089
2093
  let wrapped = /* @__PURE__ */ jsx(Fragment, { children });
2090
2094
  for (let i = resolved.providers.length - 1; i >= 0; i--) {
2091
2095
  const Provider = resolved.providers[i];
@@ -2392,11 +2396,11 @@ const Toolbar = () => {
2392
2396
  return /* @__PURE__ */ jsxs(
2393
2397
  "div",
2394
2398
  {
2395
- className: "lex4-toolbar sticky top-0 z-10 bg-white border-b border-gray-200",
2399
+ className: "lex4-toolbar",
2396
2400
  "data-testid": "toolbar",
2397
2401
  children: [
2398
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5", children: [
2399
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "history-controls", children: [
2402
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-row", children: [
2403
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-group", "data-testid": "history-controls", children: [
2400
2404
  /* @__PURE__ */ jsx(
2401
2405
  ToolbarIconButton,
2402
2406
  {
@@ -2419,12 +2423,12 @@ const Toolbar = () => {
2419
2423
  )
2420
2424
  ] }),
2421
2425
  /* @__PURE__ */ jsx(Divider, {}),
2422
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2423
- /* @__PURE__ */ jsx(Type, { size: 14, className: "text-gray-500" }),
2426
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2427
+ /* @__PURE__ */ jsx(Type, { size: 14, className: "lex4-toolbar-icon" }),
2424
2428
  /* @__PURE__ */ jsx(
2425
2429
  "select",
2426
2430
  {
2427
- className: "h-7 rounded border border-gray-200 bg-white px-2 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
2431
+ className: "lex4-toolbar-select",
2428
2432
  "data-testid": "font-selector",
2429
2433
  defaultValue: "Calibri",
2430
2434
  onChange: handleFontChange,
@@ -2432,12 +2436,12 @@ const Toolbar = () => {
2432
2436
  }
2433
2437
  )
2434
2438
  ] }),
2435
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2436
- /* @__PURE__ */ jsx(ALargeSmall, { size: 14, className: "text-gray-500" }),
2439
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-group-gap", children: [
2440
+ /* @__PURE__ */ jsx(ALargeSmall, { size: 14, className: "lex4-toolbar-icon" }),
2437
2441
  /* @__PURE__ */ jsx(
2438
2442
  "select",
2439
2443
  {
2440
- className: "h-7 w-16 rounded border border-gray-200 bg-white px-1 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
2444
+ className: "lex4-toolbar-select lex4-toolbar-select-narrow",
2441
2445
  "data-testid": "font-size-selector",
2442
2446
  defaultValue: "12",
2443
2447
  onChange: handleFontSizeChange,
@@ -2446,21 +2450,21 @@ const Toolbar = () => {
2446
2450
  )
2447
2451
  ] }),
2448
2452
  /* @__PURE__ */ jsx(Divider, {}),
2449
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "format-group", children: [
2453
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-group", "data-testid": "format-group", children: [
2450
2454
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsx(Bold, { size: 15 }) }),
2451
2455
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsx(Italic, { size: 15 }) }),
2452
2456
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsx(Underline, { size: 15 }) }),
2453
2457
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsx(Strikethrough, { size: 15 }) })
2454
2458
  ] }),
2455
2459
  /* @__PURE__ */ jsx(Divider, {}),
2456
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "align-group", children: [
2460
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-group", "data-testid": "align-group", children: [
2457
2461
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsx(TextAlignStart, { size: 15 }) }),
2458
2462
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsx(TextAlignCenter, { size: 15 }) }),
2459
2463
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsx(TextAlignEnd, { size: 15 }) }),
2460
2464
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsx(TextAlignJustify, { size: 15 }) })
2461
2465
  ] }),
2462
2466
  /* @__PURE__ */ jsx(Divider, {}),
2463
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "list-group", children: [
2467
+ /* @__PURE__ */ jsxs("div", { className: "lex4-toolbar-group", "data-testid": "list-group", children: [
2464
2468
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsx(ListOrdered, { size: 15 }) }),
2465
2469
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsx(List, { size: 15 }) }),
2466
2470
  /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsx(ListIndentIncrease, { size: 15 }) }),
@@ -2470,7 +2474,7 @@ const Toolbar = () => {
2470
2474
  /* @__PURE__ */ jsx(Divider, {}),
2471
2475
  toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsx(ToolbarItem, {}, idx))
2472
2476
  ] }),
2473
- /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ jsx(
2477
+ /* @__PURE__ */ jsx("div", { className: "lex4-toolbar-end", children: /* @__PURE__ */ jsx(
2474
2478
  ToolbarIconButton,
2475
2479
  {
2476
2480
  title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
@@ -2481,7 +2485,7 @@ const Toolbar = () => {
2481
2485
  }
2482
2486
  ) })
2483
2487
  ] }),
2484
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1 border-t border-gray-100", children: [
2488
+ /* @__PURE__ */ jsxs("div", { className: "lex4-hf-row", children: [
2485
2489
  /* @__PURE__ */ jsx(
2486
2490
  HeaderFooterToggle,
2487
2491
  {
@@ -2524,17 +2528,14 @@ const ToolbarIconButton = ({
2524
2528
  disabled,
2525
2529
  onMouseDown: (e) => e.preventDefault(),
2526
2530
  onClick,
2527
- className: `
2528
- flex h-7 w-7 items-center justify-center rounded transition-colors
2529
- ${disabled ? "cursor-not-allowed text-gray-300" : active ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}
2530
- `,
2531
+ className: `lex4-toolbar-btn${active ? " active" : ""}`,
2531
2532
  "data-testid": testId,
2532
2533
  children
2533
2534
  }
2534
2535
  );
2535
- const Divider = () => /* @__PURE__ */ jsx("div", { className: "mx-0.5 h-5 w-px bg-gray-200" });
2536
+ const Divider = () => /* @__PURE__ */ jsx("div", { className: "lex4-toolbar-separator" });
2536
2537
  function computeBodyHeight(headerHeight, footerHeight) {
2537
- const verticalMargins = PAGE_MARGIN_PX * 2;
2538
+ const verticalMargins = PAGE_MARGIN_TOP_PX + PAGE_MARGIN_BOTTOM_PX;
2538
2539
  return A4_HEIGHT_PX - headerHeight - footerHeight - verticalMargins;
2539
2540
  }
2540
2541
  function getTopLevelNodes(state) {
@@ -2839,33 +2840,33 @@ function usePagination(document2, dispatch) {
2839
2840
  };
2840
2841
  }
2841
2842
  const lexicalTheme = {
2842
- root: "lex4-root outline-none",
2843
- paragraph: "lex4-paragraph text-justify",
2843
+ root: "lex4-root",
2844
+ paragraph: "lex4-paragraph",
2844
2845
  heading: {
2845
- h1: "text-3xl font-bold mb-2",
2846
- h2: "text-2xl font-bold mb-2",
2847
- h3: "text-xl font-semibold mb-1",
2848
- h4: "text-lg font-semibold mb-1",
2849
- h5: "text-base font-medium mb-1"
2846
+ h1: "lex4-heading lex4-heading-h1",
2847
+ h2: "lex4-heading lex4-heading-h2",
2848
+ h3: "lex4-heading lex4-heading-h3",
2849
+ h4: "lex4-heading lex4-heading-h4",
2850
+ h5: "lex4-heading lex4-heading-h5"
2850
2851
  },
2851
2852
  text: {
2852
- bold: "font-bold",
2853
- italic: "italic",
2854
- underline: "underline",
2855
- strikethrough: "line-through",
2856
- underlineStrikethrough: "underline line-through"
2853
+ bold: "lex4-text-bold",
2854
+ italic: "lex4-text-italic",
2855
+ underline: "lex4-text-underline",
2856
+ strikethrough: "lex4-text-strikethrough",
2857
+ underlineStrikethrough: "lex4-text-underline lex4-text-strikethrough"
2857
2858
  },
2858
2859
  list: {
2859
2860
  nested: {
2860
- listitem: "list-none"
2861
+ listitem: "lex4-listitem-nested"
2861
2862
  },
2862
- ol: "list-decimal ml-6",
2863
- ul: "list-disc ml-6",
2863
+ ol: "lex4-list lex4-list-ordered",
2864
+ ul: "lex4-list lex4-list-unordered",
2864
2865
  listitem: "lex4-listitem",
2865
2866
  listitemChecked: "lex4-listitem-checked",
2866
2867
  listitemUnchecked: "lex4-listitem-unchecked"
2867
2868
  },
2868
- quote: "border-l-4 border-gray-300 pl-4 italic text-gray-600"
2869
+ quote: "lex4-quote"
2869
2870
  };
2870
2871
  const DEFAULT_NODES = [HeadingNode, QuoteNode, ListNode, ListItemNode];
2871
2872
  function createEditorConfig(mode, pageId, extraNodes = [], themeOverrides = {}) {
@@ -3756,8 +3757,7 @@ const PageBody = ({
3756
3757
  return /* @__PURE__ */ jsx(
3757
3758
  "div",
3758
3759
  {
3759
- className: "lex4-page-body flex-1 min-h-0 relative",
3760
- style: { overflow: "hidden" },
3760
+ className: "lex4-page-body",
3761
3761
  "data-testid": `page-body-${pageId}`,
3762
3762
  onFocus,
3763
3763
  children: /* @__PURE__ */ jsxs(LexicalComposer, { initialConfig: config, children: [
@@ -3767,11 +3767,10 @@ const PageBody = ({
3767
3767
  contentEditable: /* @__PURE__ */ jsx(
3768
3768
  ContentEditable,
3769
3769
  {
3770
- className: "outline-none h-full p-0",
3771
- style: { overflow: "visible" }
3770
+ className: "lex4-page-body-editable"
3772
3771
  }
3773
3772
  ),
3774
- placeholder: /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 text-gray-400 pointer-events-none select-none", children: t.body.placeholder }),
3773
+ placeholder: /* @__PURE__ */ jsx("div", { className: "lex4-page-placeholder", children: t.body.placeholder }),
3775
3774
  ErrorBoundary: LexicalErrorBoundary
3776
3775
  }
3777
3776
  ),
@@ -3899,7 +3898,7 @@ const PageHeader = ({
3899
3898
  return /* @__PURE__ */ jsxs(
3900
3899
  "div",
3901
3900
  {
3902
- className: "lex4-page-header bg-blue-50/60 border-t-2 border-t-blue-200 border-b border-dashed border-blue-100 relative flex-shrink-0",
3901
+ className: "lex4-page-header",
3903
3902
  style: { maxHeight: MAX_HEADER_HEIGHT_PX, overflow: "clip" },
3904
3903
  "data-testid": `page-header-${pageId}`,
3905
3904
  children: [
@@ -3911,10 +3910,10 @@ const PageHeader = ({
3911
3910
  ContentEditable,
3912
3911
  {
3913
3912
  ref: contentRef,
3914
- className: `outline-none p-2 text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
3913
+ className: `lex4-page-hf-editable${hasPageCounter ? " lex4-page-hf-narrow" : ""}`
3915
3914
  }
3916
3915
  ),
3917
- placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 ${hasPageCounter ? "pr-24" : ""}`, children: t.header.placeholder }),
3916
+ placeholder: /* @__PURE__ */ jsx("div", { className: `lex4-page-hf-placeholder${hasPageCounter ? " lex4-page-hf-narrow" : ""}`, children: t.header.placeholder }),
3918
3917
  ErrorBoundary: LexicalErrorBoundary
3919
3918
  }
3920
3919
  ),
@@ -3926,7 +3925,7 @@ const PageHeader = ({
3926
3925
  pageCounterLabel && /* @__PURE__ */ jsx(
3927
3926
  "div",
3928
3927
  {
3929
- className: "pointer-events-none absolute right-2 top-2 select-none text-xs text-gray-500",
3928
+ className: "lex4-page-counter",
3930
3929
  "data-testid": `page-counter-header-${pageId}`,
3931
3930
  children: pageCounterLabel
3932
3931
  }
@@ -3979,7 +3978,7 @@ const PageFooter = ({
3979
3978
  return /* @__PURE__ */ jsxs(
3980
3979
  "div",
3981
3980
  {
3982
- className: "lex4-page-footer bg-blue-50/60 border-b-2 border-b-blue-200 border-t border-dashed border-blue-100 relative flex-shrink-0",
3981
+ className: "lex4-page-footer",
3983
3982
  style: { maxHeight: MAX_FOOTER_HEIGHT_PX, overflow: "clip" },
3984
3983
  "data-testid": `page-footer-${pageId}`,
3985
3984
  children: [
@@ -3991,10 +3990,10 @@ const PageFooter = ({
3991
3990
  ContentEditable,
3992
3991
  {
3993
3992
  ref: contentRef,
3994
- className: `outline-none p-2 text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
3993
+ className: `lex4-page-hf-editable${hasPageCounter ? " lex4-page-hf-narrow" : ""}`
3995
3994
  }
3996
3995
  ),
3997
- placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 ${hasPageCounter ? "pr-24" : ""}`, children: t.footer.placeholder }),
3996
+ placeholder: /* @__PURE__ */ jsx("div", { className: `lex4-page-hf-placeholder${hasPageCounter ? " lex4-page-hf-narrow" : ""}`, children: t.footer.placeholder }),
3998
3997
  ErrorBoundary: LexicalErrorBoundary
3999
3998
  }
4000
3999
  ),
@@ -4006,7 +4005,7 @@ const PageFooter = ({
4006
4005
  pageCounterLabel && /* @__PURE__ */ jsx(
4007
4006
  "div",
4008
4007
  {
4009
- className: "pointer-events-none absolute right-2 top-2 select-none text-xs text-gray-500",
4008
+ className: "lex4-page-counter",
4010
4009
  "data-testid": `page-counter-footer-${pageId}`,
4011
4010
  children: pageCounterLabel
4012
4011
  }
@@ -4064,11 +4063,14 @@ const PageView = React.memo(({
4064
4063
  return /* @__PURE__ */ jsxs(
4065
4064
  "div",
4066
4065
  {
4067
- className: "lex4-page bg-white shadow-xl flex flex-col",
4066
+ className: "lex4-page",
4068
4067
  style: {
4069
4068
  width: A4_WIDTH_PX,
4070
4069
  height: A4_HEIGHT_PX,
4071
- padding: PAGE_MARGIN_PX
4070
+ paddingTop: PAGE_MARGIN_TOP_PX,
4071
+ paddingBottom: PAGE_MARGIN_BOTTOM_PX,
4072
+ paddingLeft: PAGE_MARGIN_LEFT_PX,
4073
+ paddingRight: PAGE_MARGIN_RIGHT_PX
4072
4074
  },
4073
4075
  "data-testid": `page-${pageIndex}`,
4074
4076
  "data-page-id": pageId,
@@ -4349,7 +4351,7 @@ const DocumentView = () => {
4349
4351
  return /* @__PURE__ */ jsx(
4350
4352
  "div",
4351
4353
  {
4352
- className: "lex4-document-view flex flex-col items-center gap-8 py-8 min-h-full",
4354
+ className: "lex4-document-view",
4353
4355
  "data-testid": "document-view",
4354
4356
  tabIndex: -1,
4355
4357
  children: document2.pages.map((page, index) => /* @__PURE__ */ jsx(
@@ -4368,6 +4370,250 @@ const DocumentView = () => {
4368
4370
  }
4369
4371
  );
4370
4372
  };
4373
+ const AST_VERSION = "1.0.0";
4374
+ const IS_BOLD = 1;
4375
+ const IS_ITALIC = 2;
4376
+ const IS_STRIKETHROUGH = 4;
4377
+ const IS_UNDERLINE = 8;
4378
+ function decodeFormatBitmask(format) {
4379
+ const marks = {};
4380
+ if (format & IS_BOLD) marks.bold = true;
4381
+ if (format & IS_ITALIC) marks.italic = true;
4382
+ if (format & IS_UNDERLINE) marks.underline = true;
4383
+ if (format & IS_STRIKETHROUGH) marks.strikethrough = true;
4384
+ return marks;
4385
+ }
4386
+ function extractFontFamily(style) {
4387
+ const match = style.match(/font-family:\s*([^;]+)/);
4388
+ return match ? match[1].trim().replace(/['"]/g, "") : void 0;
4389
+ }
4390
+ function extractFontSizePt(style) {
4391
+ const match = style.match(/font-size:\s*(\d+(?:\.\d+)?)\s*pt/);
4392
+ return match ? parseFloat(match[1]) : void 0;
4393
+ }
4394
+ function buildTextMarks(format, style) {
4395
+ const formatMarks = decodeFormatBitmask(format);
4396
+ const fontFamily = style ? extractFontFamily(style) : void 0;
4397
+ const fontSize = style ? extractFontSizePt(style) : void 0;
4398
+ const marks = {
4399
+ ...formatMarks,
4400
+ ...fontFamily ? { fontFamily } : {},
4401
+ ...fontSize ? { fontSize } : {}
4402
+ };
4403
+ return Object.keys(marks).length > 0 ? marks : void 0;
4404
+ }
4405
+ function mapInlineNode(node) {
4406
+ switch (node.type) {
4407
+ case "text":
4408
+ return mapTextNode(node);
4409
+ case "variable-node":
4410
+ return mapVariableNode(node);
4411
+ case "linebreak":
4412
+ return mapLineBreak();
4413
+ default:
4414
+ return { type: "text", text: "" };
4415
+ }
4416
+ }
4417
+ function mapTextNode(node) {
4418
+ const marks = buildTextMarks(node.format, node.style);
4419
+ return {
4420
+ type: "text",
4421
+ text: node.text,
4422
+ ...marks ? { marks } : {}
4423
+ };
4424
+ }
4425
+ function mapVariableNode(node) {
4426
+ return {
4427
+ type: "variable",
4428
+ key: node.variableKey
4429
+ };
4430
+ }
4431
+ function mapLineBreak() {
4432
+ return { type: "linebreak" };
4433
+ }
4434
+ function mapInlineNodes(nodes) {
4435
+ return nodes.map(mapInlineNode);
4436
+ }
4437
+ const ALIGN_LEFT = 1;
4438
+ const ALIGN_CENTER = 2;
4439
+ const ALIGN_RIGHT = 3;
4440
+ const ALIGN_JUSTIFY = 4;
4441
+ function decodeAlignment(format) {
4442
+ if (typeof format === "string") {
4443
+ if (["left", "center", "right", "justify"].includes(format)) {
4444
+ return format;
4445
+ }
4446
+ return void 0;
4447
+ }
4448
+ if (typeof format !== "number" || format === 0) return void 0;
4449
+ switch (format) {
4450
+ case ALIGN_LEFT:
4451
+ return "left";
4452
+ case ALIGN_CENTER:
4453
+ return "center";
4454
+ case ALIGN_RIGHT:
4455
+ return "right";
4456
+ case ALIGN_JUSTIFY:
4457
+ return "justify";
4458
+ default:
4459
+ return void 0;
4460
+ }
4461
+ }
4462
+ function mapBlockNode(node) {
4463
+ switch (node.type) {
4464
+ case "paragraph":
4465
+ return mapParagraph(node);
4466
+ case "heading":
4467
+ return mapHeading(node);
4468
+ case "list":
4469
+ return mapList(node);
4470
+ case "quote":
4471
+ return mapBlockQuote(node);
4472
+ default:
4473
+ return {
4474
+ type: "paragraph",
4475
+ children: mapInlineChildren(node)
4476
+ };
4477
+ }
4478
+ }
4479
+ function mapParagraph(node) {
4480
+ const alignment = decodeAlignment(node.format);
4481
+ const indent = node.indent && node.indent > 0 ? node.indent : void 0;
4482
+ return {
4483
+ type: "paragraph",
4484
+ ...alignment ? { alignment } : {},
4485
+ ...indent ? { indent } : {},
4486
+ children: mapInlineChildren(node)
4487
+ };
4488
+ }
4489
+ function mapHeading(node) {
4490
+ var _a;
4491
+ const alignment = decodeAlignment(node.format);
4492
+ const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h(\d)$/);
4493
+ const level = tagMatch ? parseInt(tagMatch[1], 10) : 1;
4494
+ return {
4495
+ type: "heading",
4496
+ level,
4497
+ ...alignment ? { alignment } : {},
4498
+ children: mapInlineChildren(node)
4499
+ };
4500
+ }
4501
+ function mapList(node) {
4502
+ const listType = node.listType === "number" ? "ordered" : "unordered";
4503
+ const items = (node.children ?? []).filter((c) => c.type === "listitem").map(mapListItem);
4504
+ return {
4505
+ type: "list",
4506
+ listType,
4507
+ items
4508
+ };
4509
+ }
4510
+ function mapListItem(node) {
4511
+ const inlineChildren = [];
4512
+ let nestedList;
4513
+ for (const child of node.children ?? []) {
4514
+ if (child.type === "list") {
4515
+ nestedList = mapList(child);
4516
+ } else {
4517
+ const mapped = mapInlineNodes([child]);
4518
+ inlineChildren.push(...mapped);
4519
+ }
4520
+ }
4521
+ return {
4522
+ type: "list-item",
4523
+ children: inlineChildren,
4524
+ ...nestedList ? { nestedList } : {}
4525
+ };
4526
+ }
4527
+ function mapBlockQuote(node) {
4528
+ return {
4529
+ type: "blockquote",
4530
+ children: mapInlineChildren(node)
4531
+ };
4532
+ }
4533
+ function mapInlineChildren(node) {
4534
+ if (!node.children || node.children.length === 0) return [];
4535
+ return mapInlineNodes(node.children);
4536
+ }
4537
+ function mapBlockNodes(nodes) {
4538
+ return nodes.map(mapBlockNode);
4539
+ }
4540
+ function mapEditorStateToContent(state) {
4541
+ if (!state || !state.root || !state.root.children) {
4542
+ return null;
4543
+ }
4544
+ const blocks = mapBlockNodes(state.root.children);
4545
+ return { blocks };
4546
+ }
4547
+ function mapEditorStateToBlocks(state) {
4548
+ if (!state || !state.root || !state.root.children) {
4549
+ return [];
4550
+ }
4551
+ return mapBlockNodes(state.root.children);
4552
+ }
4553
+ const MARGIN_TOP_MM = Math.round(PAGE_MARGIN_TOP_PX / PX_PER_MM * 10) / 10;
4554
+ const MARGIN_BOTTOM_MM = Math.round(PAGE_MARGIN_BOTTOM_PX / PX_PER_MM * 10) / 10;
4555
+ const MARGIN_LEFT_MM = Math.round(PAGE_MARGIN_LEFT_PX / PX_PER_MM * 10) / 10;
4556
+ const MARGIN_RIGHT_MM = Math.round(PAGE_MARGIN_RIGHT_PX / PX_PER_MM * 10) / 10;
4557
+ function serializeDocument(document2, variableDefinitions = []) {
4558
+ const pages = document2.pages.map(
4559
+ (page, index) => serializePage(page, index)
4560
+ );
4561
+ const metadata = buildMetadata(variableDefinitions);
4562
+ return {
4563
+ version: AST_VERSION,
4564
+ page: {
4565
+ format: "A4",
4566
+ widthMm: 210,
4567
+ heightMm: 297,
4568
+ margins: {
4569
+ topMm: MARGIN_TOP_MM,
4570
+ rightMm: MARGIN_RIGHT_MM,
4571
+ bottomMm: MARGIN_BOTTOM_MM,
4572
+ leftMm: MARGIN_LEFT_MM
4573
+ }
4574
+ },
4575
+ headerFooter: {
4576
+ enabled: document2.headerFooterEnabled,
4577
+ pageCounterMode: document2.pageCounterMode,
4578
+ defaultHeader: document2.pages.length > 0 ? mapEditorStateToContent(document2.pages[0].headerState) : null,
4579
+ defaultFooter: document2.pages.length > 0 ? mapEditorStateToContent(document2.pages[0].footerState) : null
4580
+ },
4581
+ pages,
4582
+ metadata
4583
+ };
4584
+ }
4585
+ function serializePage(page, pageIndex) {
4586
+ return {
4587
+ pageIndex,
4588
+ body: mapEditorStateToBlocks(page.bodyState),
4589
+ header: mapEditorStateToContent(page.headerState),
4590
+ footer: mapEditorStateToContent(page.footerState)
4591
+ };
4592
+ }
4593
+ function buildMetadata(variableDefinitions) {
4594
+ const variables = {};
4595
+ for (const def of variableDefinitions) {
4596
+ variables[def.key] = {
4597
+ key: def.key,
4598
+ label: def.label,
4599
+ ...def.description ? { description: def.description } : {},
4600
+ ...def.valueType ? { valueType: def.valueType } : {},
4601
+ ...def.group ? { group: def.group } : {}
4602
+ };
4603
+ }
4604
+ return { variables };
4605
+ }
4606
+ function buildSavePayload(ast, options) {
4607
+ return {
4608
+ document: ast,
4609
+ ...(options == null ? void 0 : options.exportTarget) ? { exportTarget: options.exportTarget } : {},
4610
+ ...(options == null ? void 0 : options.documentId) ? { documentId: options.documentId } : {},
4611
+ ...(options == null ? void 0 : options.metadata) ? { metadata: options.metadata } : {}
4612
+ };
4613
+ }
4614
+ function serializeDocumentJson(ast) {
4615
+ return JSON.stringify(ast, null, 2);
4616
+ }
4371
4617
  function selectEntireDocument(rootElement, selectionBuffer) {
4372
4618
  if (!rootElement || !selectionBuffer) {
4373
4619
  return;
@@ -4391,6 +4637,7 @@ function isFormFieldTarget(target) {
4391
4637
  }
4392
4638
  const EditorChrome = ({
4393
4639
  captureHistoryShortcutsOnWindow,
4640
+ onSave,
4394
4641
  className
4395
4642
  }) => {
4396
4643
  const {
@@ -4401,7 +4648,7 @@ const EditorChrome = ({
4401
4648
  undo,
4402
4649
  redo
4403
4650
  } = useDocument();
4404
- const { sidePanels } = useExtensions();
4651
+ const { sidePanels, cssVariables, rootClassNames } = useExtensions();
4405
4652
  const rootRef = useRef(null);
4406
4653
  const selectionBufferRef = useRef(null);
4407
4654
  const clearGlobalSelection = useCallback(() => {
@@ -4463,12 +4710,22 @@ const EditorChrome = ({
4463
4710
  });
4464
4711
  return;
4465
4712
  }
4713
+ if (key === "s" && onSave) {
4714
+ event.preventDefault();
4715
+ event.stopPropagation();
4716
+ const ast = serializeDocument(document2);
4717
+ const json = serializeDocumentJson(ast);
4718
+ onSave({ document: document2, ast, json });
4719
+ return;
4720
+ }
4466
4721
  if (handleHistoryShortcut(event)) {
4467
4722
  return;
4468
4723
  }
4469
4724
  }, [
4725
+ document2,
4470
4726
  dispatch,
4471
4727
  handleHistoryShortcut,
4728
+ onSave,
4472
4729
  setGlobalSelectionActive
4473
4730
  ]);
4474
4731
  const handleMouseDownCapture = useCallback((event) => {
@@ -4486,9 +4743,13 @@ const EditorChrome = ({
4486
4743
  const editableRoots = ((_a = rootRef.current) == null ? void 0 : _a.querySelectorAll(
4487
4744
  '[data-testid^="page-body-"] [data-lexical-editor="true"]'
4488
4745
  )) ?? [];
4746
+ const root = rootRef.current;
4747
+ 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;
4489
4750
  editableRoots.forEach((editableRoot) => {
4490
- editableRoot.style.backgroundColor = globalSelectionActive ? GLOBAL_SELECTION_BACKGROUND : "";
4491
- editableRoot.style.color = globalSelectionActive ? GLOBAL_SELECTION_FOREGROUND : "";
4751
+ editableRoot.style.backgroundColor = globalSelectionActive ? selBg : "";
4752
+ editableRoot.style.color = globalSelectionActive ? selFg : "";
4492
4753
  editableRoot.style.caretColor = globalSelectionActive ? "transparent" : "";
4493
4754
  });
4494
4755
  }, [globalSelectionActive, document2.pages.length]);
@@ -4521,11 +4782,14 @@ const EditorChrome = ({
4521
4782
  window.removeEventListener("beforeinput", handleWindowBeforeInput, { capture: true });
4522
4783
  };
4523
4784
  }, [captureHistoryShortcutsOnWindow, clearGlobalSelection, handleHistoryShortcut, redo, undo]);
4785
+ const rootClassName = ["lex4-editor", ...rootClassNames, className].filter(Boolean).join(" ");
4786
+ const extensionStyle = Object.keys(cssVariables).length > 0 ? cssVariables : void 0;
4524
4787
  return /* @__PURE__ */ jsxs(
4525
4788
  "div",
4526
4789
  {
4527
4790
  ref: rootRef,
4528
- className: `lex4-editor flex flex-col h-full ${className ?? ""}`,
4791
+ className: rootClassName,
4792
+ style: extensionStyle,
4529
4793
  "data-testid": "lex4-editor",
4530
4794
  "data-global-selection-active": globalSelectionActive ? "true" : "false",
4531
4795
  onKeyDownCapture: handleKeyDownCapture,
@@ -4539,12 +4803,12 @@ const EditorChrome = ({
4539
4803
  "data-testid": "global-selection-buffer",
4540
4804
  readOnly: true,
4541
4805
  tabIndex: -1,
4542
- className: "pointer-events-none fixed -left-[9999px] top-0 h-0 w-0 opacity-0"
4806
+ className: "lex4-selection-buffer"
4543
4807
  }
4544
4808
  ),
4545
4809
  /* @__PURE__ */ jsx(Toolbar, {}),
4546
- /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-200", children: [
4547
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 overflow-auto", children: /* @__PURE__ */ jsx(DocumentView, {}) }),
4810
+ /* @__PURE__ */ jsxs("div", { className: "lex4-canvas", children: [
4811
+ /* @__PURE__ */ jsx("div", { className: "lex4-canvas-scroll", children: /* @__PURE__ */ jsx(DocumentView, {}) }),
4548
4812
  sidePanels.map((Panel, idx) => /* @__PURE__ */ jsx(Panel, {}, idx)),
4549
4813
  /* @__PURE__ */ jsx(HistorySidebar, {})
4550
4814
  ] })
@@ -4552,7 +4816,7 @@ const EditorChrome = ({
4552
4816
  }
4553
4817
  );
4554
4818
  };
4555
- const EditorWithHandle = forwardRef(({ captureHistoryShortcutsOnWindow, className }, ref) => {
4819
+ const EditorWithHandle = forwardRef(({ captureHistoryShortcutsOnWindow, onSave, className }, ref) => {
4556
4820
  const { document: doc, activeEditor } = useDocument();
4557
4821
  const { handleFactories } = useExtensions();
4558
4822
  const getDocument = useCallback(() => doc, [doc]);
@@ -4570,6 +4834,7 @@ const EditorWithHandle = forwardRef(({ captureHistoryShortcutsOnWindow, classNam
4570
4834
  EditorChrome,
4571
4835
  {
4572
4836
  captureHistoryShortcutsOnWindow,
4837
+ onSave,
4573
4838
  className
4574
4839
  }
4575
4840
  );
@@ -4579,6 +4844,7 @@ const Lex4Editor = forwardRef(({
4579
4844
  captureHistoryShortcutsOnWindow = true,
4580
4845
  initialDocument,
4581
4846
  onDocumentChange,
4847
+ onSave,
4582
4848
  extensions,
4583
4849
  translations,
4584
4850
  className
@@ -4593,6 +4859,7 @@ const Lex4Editor = forwardRef(({
4593
4859
  {
4594
4860
  ref,
4595
4861
  captureHistoryShortcutsOnWindow,
4862
+ onSave,
4596
4863
  className
4597
4864
  }
4598
4865
  )
@@ -4665,247 +4932,6 @@ function useHeaderFooter(maxHeight, onHeightChange) {
4665
4932
  }, []);
4666
4933
  return { attachRef };
4667
4934
  }
4668
- const AST_VERSION = "1.0.0";
4669
- const IS_BOLD = 1;
4670
- const IS_ITALIC = 2;
4671
- const IS_STRIKETHROUGH = 4;
4672
- const IS_UNDERLINE = 8;
4673
- function decodeFormatBitmask(format) {
4674
- const marks = {};
4675
- if (format & IS_BOLD) marks.bold = true;
4676
- if (format & IS_ITALIC) marks.italic = true;
4677
- if (format & IS_UNDERLINE) marks.underline = true;
4678
- if (format & IS_STRIKETHROUGH) marks.strikethrough = true;
4679
- return marks;
4680
- }
4681
- function extractFontFamily(style) {
4682
- const match = style.match(/font-family:\s*([^;]+)/);
4683
- return match ? match[1].trim().replace(/['"]/g, "") : void 0;
4684
- }
4685
- function extractFontSizePt(style) {
4686
- const match = style.match(/font-size:\s*(\d+(?:\.\d+)?)\s*pt/);
4687
- return match ? parseFloat(match[1]) : void 0;
4688
- }
4689
- function buildTextMarks(format, style) {
4690
- const formatMarks = decodeFormatBitmask(format);
4691
- const fontFamily = style ? extractFontFamily(style) : void 0;
4692
- const fontSize = style ? extractFontSizePt(style) : void 0;
4693
- const marks = {
4694
- ...formatMarks,
4695
- ...fontFamily ? { fontFamily } : {},
4696
- ...fontSize ? { fontSize } : {}
4697
- };
4698
- return Object.keys(marks).length > 0 ? marks : void 0;
4699
- }
4700
- function mapInlineNode(node) {
4701
- switch (node.type) {
4702
- case "text":
4703
- return mapTextNode(node);
4704
- case "variable-node":
4705
- return mapVariableNode(node);
4706
- case "linebreak":
4707
- return mapLineBreak();
4708
- default:
4709
- return { type: "text", text: "" };
4710
- }
4711
- }
4712
- function mapTextNode(node) {
4713
- const marks = buildTextMarks(node.format, node.style);
4714
- return {
4715
- type: "text",
4716
- text: node.text,
4717
- ...marks ? { marks } : {}
4718
- };
4719
- }
4720
- function mapVariableNode(node) {
4721
- return {
4722
- type: "variable",
4723
- key: node.variableKey
4724
- };
4725
- }
4726
- function mapLineBreak() {
4727
- return { type: "linebreak" };
4728
- }
4729
- function mapInlineNodes(nodes) {
4730
- return nodes.map(mapInlineNode);
4731
- }
4732
- const ALIGN_LEFT = 1;
4733
- const ALIGN_CENTER = 2;
4734
- const ALIGN_RIGHT = 3;
4735
- const ALIGN_JUSTIFY = 4;
4736
- function decodeAlignment(format) {
4737
- if (typeof format === "string") {
4738
- if (["left", "center", "right", "justify"].includes(format)) {
4739
- return format;
4740
- }
4741
- return void 0;
4742
- }
4743
- if (typeof format !== "number" || format === 0) return void 0;
4744
- switch (format) {
4745
- case ALIGN_LEFT:
4746
- return "left";
4747
- case ALIGN_CENTER:
4748
- return "center";
4749
- case ALIGN_RIGHT:
4750
- return "right";
4751
- case ALIGN_JUSTIFY:
4752
- return "justify";
4753
- default:
4754
- return void 0;
4755
- }
4756
- }
4757
- function mapBlockNode(node) {
4758
- switch (node.type) {
4759
- case "paragraph":
4760
- return mapParagraph(node);
4761
- case "heading":
4762
- return mapHeading(node);
4763
- case "list":
4764
- return mapList(node);
4765
- case "quote":
4766
- return mapBlockQuote(node);
4767
- default:
4768
- return {
4769
- type: "paragraph",
4770
- children: mapInlineChildren(node)
4771
- };
4772
- }
4773
- }
4774
- function mapParagraph(node) {
4775
- const alignment = decodeAlignment(node.format);
4776
- const indent = node.indent && node.indent > 0 ? node.indent : void 0;
4777
- return {
4778
- type: "paragraph",
4779
- ...alignment ? { alignment } : {},
4780
- ...indent ? { indent } : {},
4781
- children: mapInlineChildren(node)
4782
- };
4783
- }
4784
- function mapHeading(node) {
4785
- var _a;
4786
- const alignment = decodeAlignment(node.format);
4787
- const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h(\d)$/);
4788
- const level = tagMatch ? parseInt(tagMatch[1], 10) : 1;
4789
- return {
4790
- type: "heading",
4791
- level,
4792
- ...alignment ? { alignment } : {},
4793
- children: mapInlineChildren(node)
4794
- };
4795
- }
4796
- function mapList(node) {
4797
- const listType = node.listType === "number" ? "ordered" : "unordered";
4798
- const items = (node.children ?? []).filter((c) => c.type === "listitem").map(mapListItem);
4799
- return {
4800
- type: "list",
4801
- listType,
4802
- items
4803
- };
4804
- }
4805
- function mapListItem(node) {
4806
- const inlineChildren = [];
4807
- let nestedList;
4808
- for (const child of node.children ?? []) {
4809
- if (child.type === "list") {
4810
- nestedList = mapList(child);
4811
- } else {
4812
- const mapped = mapInlineNodes([child]);
4813
- inlineChildren.push(...mapped);
4814
- }
4815
- }
4816
- return {
4817
- type: "list-item",
4818
- children: inlineChildren,
4819
- ...nestedList ? { nestedList } : {}
4820
- };
4821
- }
4822
- function mapBlockQuote(node) {
4823
- return {
4824
- type: "blockquote",
4825
- children: mapInlineChildren(node)
4826
- };
4827
- }
4828
- function mapInlineChildren(node) {
4829
- if (!node.children || node.children.length === 0) return [];
4830
- return mapInlineNodes(node.children);
4831
- }
4832
- function mapBlockNodes(nodes) {
4833
- return nodes.map(mapBlockNode);
4834
- }
4835
- function mapEditorStateToContent(state) {
4836
- if (!state || !state.root || !state.root.children) {
4837
- return null;
4838
- }
4839
- const blocks = mapBlockNodes(state.root.children);
4840
- return { blocks };
4841
- }
4842
- function mapEditorStateToBlocks(state) {
4843
- if (!state || !state.root || !state.root.children) {
4844
- return [];
4845
- }
4846
- return mapBlockNodes(state.root.children);
4847
- }
4848
- const MARGIN_MM = Math.round(PAGE_MARGIN_PX / PX_PER_MM * 10) / 10;
4849
- function serializeDocument(document2, variableDefinitions = []) {
4850
- const pages = document2.pages.map(
4851
- (page, index) => serializePage(page, index)
4852
- );
4853
- const metadata = buildMetadata(variableDefinitions);
4854
- return {
4855
- version: AST_VERSION,
4856
- page: {
4857
- format: "A4",
4858
- widthMm: 210,
4859
- heightMm: 297,
4860
- margins: {
4861
- topMm: MARGIN_MM,
4862
- rightMm: MARGIN_MM,
4863
- bottomMm: MARGIN_MM,
4864
- leftMm: MARGIN_MM
4865
- }
4866
- },
4867
- headerFooter: {
4868
- enabled: document2.headerFooterEnabled,
4869
- pageCounterMode: document2.pageCounterMode,
4870
- defaultHeader: document2.pages.length > 0 ? mapEditorStateToContent(document2.pages[0].headerState) : null,
4871
- defaultFooter: document2.pages.length > 0 ? mapEditorStateToContent(document2.pages[0].footerState) : null
4872
- },
4873
- pages,
4874
- metadata
4875
- };
4876
- }
4877
- function serializePage(page, pageIndex) {
4878
- return {
4879
- pageIndex,
4880
- body: mapEditorStateToBlocks(page.bodyState),
4881
- header: mapEditorStateToContent(page.headerState),
4882
- footer: mapEditorStateToContent(page.footerState)
4883
- };
4884
- }
4885
- function buildMetadata(variableDefinitions) {
4886
- const variables = {};
4887
- for (const def of variableDefinitions) {
4888
- variables[def.key] = {
4889
- key: def.key,
4890
- label: def.label,
4891
- ...def.description ? { description: def.description } : {},
4892
- ...def.valueType ? { valueType: def.valueType } : {},
4893
- ...def.group ? { group: def.group } : {}
4894
- };
4895
- }
4896
- return { variables };
4897
- }
4898
- function buildSavePayload(ast, options) {
4899
- return {
4900
- document: ast,
4901
- ...(options == null ? void 0 : options.exportTarget) ? { exportTarget: options.exportTarget } : {},
4902
- ...(options == null ? void 0 : options.documentId) ? { documentId: options.documentId } : {},
4903
- ...(options == null ? void 0 : options.metadata) ? { metadata: options.metadata } : {}
4904
- };
4905
- }
4906
- function serializeDocumentJson(ast) {
4907
- return JSON.stringify(ast, null, 2);
4908
- }
4909
4935
  function astExtension() {
4910
4936
  return {
4911
4937
  name: "ast",
@@ -5031,7 +5057,7 @@ function VariableChip({ variableKey }) {
5031
5057
  return /* @__PURE__ */ jsx(
5032
5058
  "span",
5033
5059
  {
5034
- className: "lex4-variable-chip inline-flex items-center rounded-full border border-blue-300\n bg-white px-2 py-0.5 text-[11px] font-medium text-blue-700 select-none\n cursor-default whitespace-nowrap mx-0.5",
5060
+ className: "lex4-variable-chip",
5035
5061
  "data-testid": `variable-chip-${variableKey}`,
5036
5062
  title: variableKey,
5037
5063
  children: label
@@ -5104,12 +5130,12 @@ const VariablePicker = ({ onInsert, disabled = false }) => {
5104
5130
  document.addEventListener("mousedown", handler);
5105
5131
  return () => document.removeEventListener("mousedown", handler);
5106
5132
  }, [open]);
5107
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-block", children: [
5133
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "lex4-variable-picker", children: [
5108
5134
  /* @__PURE__ */ jsx(
5109
5135
  "button",
5110
5136
  {
5111
5137
  type: "button",
5112
- className: "h-7 rounded border border-gray-200 bg-white px-2 text-xs font-medium text-gray-700\n hover:bg-gray-50 focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400\n disabled:opacity-50 disabled:cursor-not-allowed",
5138
+ className: "lex4-variable-picker-btn",
5113
5139
  "data-testid": "variable-picker-button",
5114
5140
  disabled: disabled || definitions.length === 0,
5115
5141
  onClick: () => setOpen(!open),
@@ -5120,14 +5146,13 @@ const VariablePicker = ({ onInsert, disabled = false }) => {
5120
5146
  open && /* @__PURE__ */ jsxs(
5121
5147
  "div",
5122
5148
  {
5123
- className: "absolute left-0 top-full z-50 mt-1 w-64 rounded-md border border-gray-200\n bg-white shadow-lg",
5149
+ className: "lex4-variable-picker-dropdown",
5124
5150
  "data-testid": "variable-picker-dropdown",
5125
5151
  children: [
5126
- /* @__PURE__ */ jsx("div", { className: "border-b border-gray-100 p-2", children: /* @__PURE__ */ jsx(
5152
+ /* @__PURE__ */ jsx("div", { className: "lex4-variable-picker-search", children: /* @__PURE__ */ jsx(
5127
5153
  "input",
5128
5154
  {
5129
5155
  type: "text",
5130
- className: "w-full rounded border border-gray-200 px-2 py-1 text-xs\n focus:border-blue-400 focus:outline-none",
5131
5156
  placeholder: "Search variables...",
5132
5157
  "data-testid": "variable-picker-search",
5133
5158
  value: filter,
@@ -5135,20 +5160,20 @@ const VariablePicker = ({ onInsert, disabled = false }) => {
5135
5160
  autoFocus: true
5136
5161
  }
5137
5162
  ) }),
5138
- /* @__PURE__ */ jsxs("div", { className: "max-h-48 overflow-y-auto p-1", children: [
5139
- Object.keys(grouped).length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-xs text-gray-400", children: "No variables found" }),
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" }),
5140
5165
  Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxs("div", { children: [
5141
- /* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-[10px] font-semibold uppercase text-gray-400", children: group }),
5166
+ /* @__PURE__ */ jsx("div", { className: "lex4-variable-picker-group-label", children: group }),
5142
5167
  defs.map((def) => /* @__PURE__ */ jsxs(
5143
5168
  "button",
5144
5169
  {
5145
5170
  type: "button",
5146
- className: "flex w-full items-center gap-2 rounded px-2 py-1 text-left text-xs\n hover:bg-blue-50",
5171
+ className: "lex4-variable-picker-option",
5147
5172
  "data-testid": `variable-option-${def.key}`,
5148
5173
  onClick: () => handleInsert(def.key),
5149
5174
  children: [
5150
- /* @__PURE__ */ jsx("span", { className: "font-medium text-blue-700", children: `{{${def.key}}}` }),
5151
- /* @__PURE__ */ jsx("span", { className: "text-gray-500", children: def.label })
5175
+ /* @__PURE__ */ jsx("span", { className: "lex4-variable-picker-key", children: `{{${def.key}}}` }),
5176
+ /* @__PURE__ */ jsx("span", { className: "lex4-variable-picker-label", children: def.label })
5152
5177
  ]
5153
5178
  },
5154
5179
  def.key
@@ -5206,20 +5231,20 @@ const VariablePanel = ({ open, onClose }) => {
5206
5231
  "button",
5207
5232
  {
5208
5233
  type: "button",
5209
- className: "flex h-6 w-6 items-center justify-center rounded text-gray-400\n transition-colors hover:bg-gray-100 hover:text-gray-600",
5234
+ className: "lex4-sidebar-action-btn",
5210
5235
  title: t.variables.refreshVariables,
5211
5236
  "data-testid": "btn-refresh-variables",
5212
5237
  children: /* @__PURE__ */ jsx(RefreshCw, { size: 12 })
5213
5238
  }
5214
5239
  ),
5215
5240
  children: [
5216
- /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
5217
- /* @__PURE__ */ jsx(Search, { size: 14, className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400" }),
5241
+ /* @__PURE__ */ jsx("div", { className: "lex4-variable-search-container", children: /* @__PURE__ */ jsxs("div", { className: "lex4-variable-search-wrapper", children: [
5242
+ /* @__PURE__ */ jsx(Search, { size: 14, className: "lex4-variable-search-icon" }),
5218
5243
  /* @__PURE__ */ jsx(
5219
5244
  "input",
5220
5245
  {
5221
5246
  type: "text",
5222
- className: "w-full rounded-lg border border-gray-200 bg-gray-50 py-1.5 pl-8 pr-3 text-xs\n placeholder-gray-400 focus:border-blue-400 focus:bg-white focus:outline-none\n focus:ring-1 focus:ring-blue-400",
5247
+ className: "lex4-variable-search-input",
5223
5248
  placeholder: t.variables.searchPlaceholder,
5224
5249
  "data-testid": "variable-panel-search",
5225
5250
  value: filter,
@@ -5227,25 +5252,19 @@ const VariablePanel = ({ open, onClose }) => {
5227
5252
  }
5228
5253
  )
5229
5254
  ] }) }),
5230
- /* @__PURE__ */ jsxs("div", { className: "px-3 pb-3", children: [
5231
- Object.keys(grouped).length === 0 && /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-xs text-gray-400", children: t.variables.noVariablesFound }),
5232
- Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: defs.map((def) => /* @__PURE__ */ jsxs(
5255
+ /* @__PURE__ */ jsxs("div", { className: "lex4-variable-list", children: [
5256
+ 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(
5233
5258
  "button",
5234
5259
  {
5235
5260
  type: "button",
5236
- className: "flex items-center justify-between gap-2 rounded-lg px-2.5 py-1.5 text-left text-xs\n transition-colors hover:bg-blue-50 group",
5261
+ className: "lex4-variable-list-item",
5237
5262
  "data-testid": `variable-panel-${def.key}`,
5238
5263
  onClick: () => handleInsert(def.key),
5239
5264
  disabled: !activeEditor,
5240
5265
  children: [
5241
- /* @__PURE__ */ jsx(
5242
- "span",
5243
- {
5244
- className: "inline-flex items-center rounded-full border border-blue-300 bg-white\n px-2 py-0.5 text-[11px] font-medium text-blue-700\n group-hover:border-blue-400 group-hover:bg-blue-50",
5245
- children: def.label
5246
- }
5247
- ),
5248
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-gray-400", children: group })
5266
+ /* @__PURE__ */ jsx("span", { className: "lex4-variable-badge", children: def.label }),
5267
+ /* @__PURE__ */ jsx("span", { className: "lex4-variable-group-label", children: group })
5249
5268
  ]
5250
5269
  },
5251
5270
  def.key
@@ -5299,10 +5318,7 @@ const VariablePanelToggle = () => {
5299
5318
  "aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
5300
5319
  onMouseDown: (e) => e.preventDefault(),
5301
5320
  onClick: () => setPanelOpen(!panelOpen),
5302
- className: `
5303
- flex h-7 w-7 items-center justify-center rounded transition-colors
5304
- ${panelOpen ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}
5305
- `,
5321
+ className: `lex4-toolbar-btn${panelOpen ? " active" : ""}`,
5306
5322
  "data-testid": "toggle-variable-panel",
5307
5323
  children: /* @__PURE__ */ jsx(Braces, { size: 15 })
5308
5324
  }