best-email-builder 0.12.0 → 0.13.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/ai-server.js +3 -3
  3. package/dist/ai.js +6 -6
  4. package/dist/builder.js +8 -8
  5. package/dist/{chunk-FYIIO7LD.js → chunk-2KOI5SWV.js} +2 -2
  6. package/dist/{chunk-LAUNZIFX.js → chunk-6QTSXJG4.js} +5 -5
  7. package/dist/chunk-6QTSXJG4.js.map +1 -0
  8. package/dist/{chunk-46ZVS2VX.js → chunk-A4KF7NMP.js} +2 -2
  9. package/dist/{chunk-OACK2DIW.js → chunk-G7G3TRWG.js} +662 -441
  10. package/dist/chunk-G7G3TRWG.js.map +1 -0
  11. package/dist/{chunk-6CTFMKAQ.js → chunk-JJ56652Y.js} +4 -4
  12. package/dist/{chunk-UUS325J7.js → chunk-LSOR4OII.js} +3 -3
  13. package/dist/{chunk-MOWQZKIQ.js → chunk-Q2ZUJFOY.js} +2 -2
  14. package/dist/{chunk-LZYDCWVH.js → chunk-Q5UPFM7J.js} +3 -3
  15. package/dist/{chunk-LZYDCWVH.js.map → chunk-Q5UPFM7J.js.map} +1 -1
  16. package/dist/{chunk-RONYPWDK.js → chunk-UGF3YJ64.js} +2 -2
  17. package/dist/{chunk-RSTERB7K.js → chunk-XF25DLD4.js} +3 -3
  18. package/dist/{chunk-JFIXYIZ3.js → chunk-ZTUW25BG.js} +7 -1
  19. package/dist/chunk-ZTUW25BG.js.map +1 -0
  20. package/dist/index.js +10 -10
  21. package/dist/library.js +5 -5
  22. package/dist/server.js +5 -5
  23. package/dist/styles.css +1 -1
  24. package/dist/types/core/EmailEditor.d.ts +16 -4
  25. package/dist/types/core/constants.d.ts +1 -1
  26. package/dist/types/engine/NativeAdapter.d.ts +1 -1
  27. package/dist/types/engine/canvas/CanvasView.d.ts +3 -0
  28. package/dist/types/engine/canvas/canvasStyles.d.ts +1 -1
  29. package/dist/types/features/variables/VariablesBar.d.ts +16 -0
  30. package/dist/types/ui/primitives/misc.d.ts +6 -2
  31. package/package.json +1 -1
  32. package/dist/chunk-JFIXYIZ3.js.map +0 -1
  33. package/dist/chunk-LAUNZIFX.js.map +0 -1
  34. package/dist/chunk-OACK2DIW.js.map +0 -1
  35. /package/dist/{chunk-FYIIO7LD.js.map → chunk-2KOI5SWV.js.map} +0 -0
  36. /package/dist/{chunk-46ZVS2VX.js.map → chunk-A4KF7NMP.js.map} +0 -0
  37. /package/dist/{chunk-6CTFMKAQ.js.map → chunk-JJ56652Y.js.map} +0 -0
  38. /package/dist/{chunk-UUS325J7.js.map → chunk-LSOR4OII.js.map} +0 -0
  39. /package/dist/{chunk-MOWQZKIQ.js.map → chunk-Q2ZUJFOY.js.map} +0 -0
  40. /package/dist/{chunk-RONYPWDK.js.map → chunk-UGF3YJ64.js.map} +0 -0
  41. /package/dist/{chunk-RSTERB7K.js.map → chunk-XF25DLD4.js.map} +0 -0
@@ -6,17 +6,17 @@ import {
6
6
  useLayoutWidth,
7
7
  useSearchShortcut,
8
8
  withThumbnailImages
9
- } from "./chunk-UUS325J7.js";
9
+ } from "./chunk-LSOR4OII.js";
10
10
  import {
11
11
  BRAND_NOTES,
12
12
  countImagesWithoutPicture,
13
13
  logoWidthFor
14
- } from "./chunk-MOWQZKIQ.js";
14
+ } from "./chunk-Q2ZUJFOY.js";
15
15
  import {
16
16
  cleanRichText,
17
17
  escapeText,
18
18
  textOfHtml
19
- } from "./chunk-RONYPWDK.js";
19
+ } from "./chunk-UGF3YJ64.js";
20
20
  import {
21
21
  Button,
22
22
  Dialog,
@@ -51,7 +51,7 @@ import {
51
51
  createHttpStorage,
52
52
  useT,
53
53
  useUi
54
- } from "./chunk-LAUNZIFX.js";
54
+ } from "./chunk-6QTSXJG4.js";
55
55
  import {
56
56
  DEFAULT_BUTTON_URL,
57
57
  EMAIL_BUILDER_VERSION,
@@ -94,10 +94,10 @@ import {
94
94
  setAttributes,
95
95
  setContent,
96
96
  toProject
97
- } from "./chunk-LZYDCWVH.js";
97
+ } from "./chunk-Q5UPFM7J.js";
98
98
  import {
99
99
  isSafeUrl
100
- } from "./chunk-46ZVS2VX.js";
100
+ } from "./chunk-A4KF7NMP.js";
101
101
  import {
102
102
  CUSTOM_GROUP,
103
103
  DEFAULT_VARIABLES,
@@ -110,7 +110,7 @@ import {
110
110
  tokenOf,
111
111
  translatorFor,
112
112
  validateCustomVariable
113
- } from "./chunk-JFIXYIZ3.js";
113
+ } from "./chunk-ZTUW25BG.js";
114
114
 
115
115
  // src/core/events.js
116
116
  function createEmitter({ onError } = {}) {
@@ -450,6 +450,19 @@ function createEmailEditor({ adapter, blocks, features = null, t = identity }) {
450
450
  requireReady("insertLink");
451
451
  return adapter.insertLink(String(href || ""), String(label || href || ""));
452
452
  },
453
+ /**
454
+ * Shows a text where `insertText` would put it, without inserting it (a
455
+ * variable under the pointer). False if nothing would take it, or if the
456
+ * engine cannot preview. Not ready yet: nothing to show, no error.
457
+ */
458
+ previewText(text) {
459
+ if (status !== "ready" || !adapter.previewText) return false;
460
+ return adapter.previewText(String(text ?? ""));
461
+ },
462
+ /** Takes the previewed text away. */
463
+ clearPreview() {
464
+ if (status === "ready") adapter.clearPreview?.();
465
+ },
453
466
  undo() {
454
467
  requireReady("undo");
455
468
  adapter.undo();
@@ -1424,21 +1437,113 @@ var versionsFeature = {
1424
1437
  toolbar: VersionsMenu
1425
1438
  };
1426
1439
 
1440
+ // src/features/variables/VariablesBar.jsx
1441
+ import { useId, useState as useState2 } from "react";
1442
+ import { Braces, ChevronDown } from "lucide-react";
1443
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1444
+ function VariablesBar({ context }) {
1445
+ const { editor, ready, variables } = context;
1446
+ const { t, notify } = useUi();
1447
+ const [hovered, setHovered] = useState2(false);
1448
+ const [open, setOpen] = useState2(false);
1449
+ const listId = useId();
1450
+ const fields = variables.filter((v) => v.kind !== "link");
1451
+ if (!fields.length) return null;
1452
+ const expanded = hovered || open;
1453
+ const insert = (variable) => {
1454
+ if (!editor.insertText(tokenOf(variable))) notify("info", t("Select a text or a button first."));
1455
+ };
1456
+ return /* @__PURE__ */ jsx2("div", { className: "eb:relative eb:z-10 eb:h-6 eb:shrink-0", "data-testid": "eb-variables-bar", children: /* @__PURE__ */ jsxs2(
1457
+ "div",
1458
+ {
1459
+ className: cn(
1460
+ "eb:absolute eb:inset-x-0 eb:top-0 eb:border-b eb:border-border eb:bg-background eb:transition-shadow",
1461
+ expanded && "eb:shadow-md"
1462
+ ),
1463
+ "data-expanded": expanded,
1464
+ onMouseEnter: () => setHovered(true),
1465
+ onMouseLeave: () => setHovered(false),
1466
+ onKeyDown: (e) => e.key === "Escape" && open && setOpen(false),
1467
+ onMouseDown: (e) => e.preventDefault(),
1468
+ children: [
1469
+ /* @__PURE__ */ jsxs2(
1470
+ "button",
1471
+ {
1472
+ type: "button",
1473
+ "aria-expanded": expanded,
1474
+ "aria-controls": listId,
1475
+ onClick: () => setOpen((value) => !value),
1476
+ className: "eb:flex eb:h-6 eb:w-full eb:items-center eb:gap-1.5 eb:px-3 eb:text-[11px] eb:font-medium eb:text-foreground-lighter eb:outline-none eb:hover:text-foreground eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40 eb:focus-visible:ring-inset",
1477
+ children: [
1478
+ /* @__PURE__ */ jsx2(Braces, { className: "eb:size-3", "aria-hidden": "true" }),
1479
+ t("Personalization fields"),
1480
+ /* @__PURE__ */ jsx2("span", { className: "eb:text-foreground-muted", children: fields.length }),
1481
+ /* @__PURE__ */ jsx2(ChevronDown, { className: cn("eb:ml-auto eb:size-3.5 eb:transition-transform", expanded && "eb:rotate-180"), "aria-hidden": "true" })
1482
+ ]
1483
+ }
1484
+ ),
1485
+ /* @__PURE__ */ jsx2(
1486
+ "div",
1487
+ {
1488
+ id: listId,
1489
+ className: cn(
1490
+ "eb:grid eb:transition-[grid-template-rows,visibility] eb:duration-200 eb:ease-out",
1491
+ expanded ? "eb:visible eb:grid-rows-[1fr]" : "eb:invisible eb:grid-rows-[0fr]"
1492
+ ),
1493
+ children: /* @__PURE__ */ jsx2("div", { className: "eb:min-h-0 eb:overflow-hidden", children: /* @__PURE__ */ jsx2(
1494
+ "div",
1495
+ {
1496
+ role: "group",
1497
+ "aria-label": t("Personalization fields"),
1498
+ className: "eb:flex eb:max-h-24 eb:flex-wrap eb:gap-1 eb:overflow-y-auto eb:px-3 eb:pt-0.5 eb:pb-2",
1499
+ children: fields.map((variable) => /* @__PURE__ */ jsx2(
1500
+ TooltipWrap,
1501
+ {
1502
+ label: /* @__PURE__ */ jsx2("code", { className: "eb:font-mono", children: tokenOf(variable) }),
1503
+ delayDuration: 100,
1504
+ hoverable: false,
1505
+ children: /* @__PURE__ */ jsx2(
1506
+ "button",
1507
+ {
1508
+ type: "button",
1509
+ disabled: !ready,
1510
+ "data-variable": variable.key,
1511
+ onMouseEnter: () => editor.previewText(tokenOf(variable)),
1512
+ onMouseLeave: () => editor.clearPreview(),
1513
+ onFocus: () => editor.previewText(tokenOf(variable)),
1514
+ onBlur: () => editor.clearPreview(),
1515
+ onClick: () => insert(variable),
1516
+ className: "eb:inline-flex eb:h-5.5 eb:items-center eb:rounded-full eb:border eb:border-border eb:bg-surface-75 eb:px-2 eb:text-[11px] eb:font-medium eb:text-foreground eb:transition-colors eb:outline-none eb:hover:border-primary/50 eb:hover:bg-primary/5 eb:hover:text-primary eb:focus-visible:ring-2 eb:focus-visible:ring-ring/50 eb:disabled:opacity-50",
1517
+ children: variable.custom ? variable.label : t(variable.label)
1518
+ }
1519
+ )
1520
+ },
1521
+ variable.key
1522
+ ))
1523
+ }
1524
+ ) })
1525
+ }
1526
+ )
1527
+ ]
1528
+ }
1529
+ ) });
1530
+ }
1531
+
1427
1532
  // src/features/variables/VariablesMenu.jsx
1428
- import { useState as useState4 } from "react";
1429
- import { Braces as Braces2 } from "lucide-react";
1533
+ import { useState as useState5 } from "react";
1534
+ import { Braces as Braces3 } from "lucide-react";
1430
1535
 
1431
1536
  // src/features/variables/VariableDialog.jsx
1432
- import { useEffect, useMemo, useState as useState2 } from "react";
1433
- import { Braces, Loader2, Trash2 } from "lucide-react";
1434
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1537
+ import { useEffect, useMemo, useState as useState3 } from "react";
1538
+ import { Braces as Braces2, Loader2, Trash2 } from "lucide-react";
1539
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1435
1540
  function VariableDialog({ open, onOpenChange, variable = null, initialLabel = "", variables, storage, onSaved, onDeleted }) {
1436
1541
  const { t, notify, confirm } = useUi();
1437
1542
  const editing = Boolean(variable);
1438
- const [form, setForm] = useState2({ label: "", key: "", group: "", sample: "", fallback: "" });
1439
- const [keyTouched, setKeyTouched] = useState2(false);
1440
- const [errors, setErrors] = useState2({});
1441
- const [busy, setBusy] = useState2(false);
1543
+ const [form, setForm] = useState3({ label: "", key: "", group: "", sample: "", fallback: "" });
1544
+ const [keyTouched, setKeyTouched] = useState3(false);
1545
+ const [errors, setErrors] = useState3({});
1546
+ const [busy, setBusy] = useState3(false);
1442
1547
  useEffect(() => {
1443
1548
  if (!open) return;
1444
1549
  setForm(
@@ -1505,9 +1610,9 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
1505
1610
  setBusy(false);
1506
1611
  }
1507
1612
  };
1508
- const field = (name, label, props = {}, hint = null) => /* @__PURE__ */ jsxs2("div", { className: "eb:space-y-1.5", children: [
1509
- /* @__PURE__ */ jsx2(Label, { htmlFor: `eb-var-${name}`, children: label }),
1510
- /* @__PURE__ */ jsx2(
1613
+ const field = (name, label, props = {}, hint = null) => /* @__PURE__ */ jsxs3("div", { className: "eb:space-y-1.5", children: [
1614
+ /* @__PURE__ */ jsx3(Label, { htmlFor: `eb-var-${name}`, children: label }),
1615
+ /* @__PURE__ */ jsx3(
1511
1616
  Input,
1512
1617
  {
1513
1618
  id: `eb-var-${name}`,
@@ -1518,23 +1623,23 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
1518
1623
  ...props
1519
1624
  }
1520
1625
  ),
1521
- (errors[name] || hint) && /* @__PURE__ */ jsx2("p", { id: `eb-var-${name}-hint`, className: errors[name] ? "eb:text-[12px] eb:text-destructive" : "eb:text-[11px] eb:text-foreground-muted", children: errors[name] || hint })
1626
+ (errors[name] || hint) && /* @__PURE__ */ jsx3("p", { id: `eb-var-${name}-hint`, className: errors[name] ? "eb:text-[12px] eb:text-destructive" : "eb:text-[11px] eb:text-foreground-muted", children: errors[name] || hint })
1522
1627
  ] });
1523
- return /* @__PURE__ */ jsx2(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs2(
1628
+ return /* @__PURE__ */ jsx3(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs3(
1524
1629
  DialogContent,
1525
1630
  {
1526
1631
  className: "eb:sm:max-w-[440px]",
1527
1632
  onMouseDown: (e) => e.stopPropagation(),
1528
1633
  onCloseAutoFocus: (e) => e.preventDefault(),
1529
1634
  children: [
1530
- /* @__PURE__ */ jsx2(DialogHeader, { children: /* @__PURE__ */ jsxs2("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
1531
- /* @__PURE__ */ jsx2("span", { className: "eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-lg2 eb:bg-primary/10 eb:text-primary", children: /* @__PURE__ */ jsx2(Braces, { className: "eb:size-5" }) }),
1532
- /* @__PURE__ */ jsxs2("div", { children: [
1533
- /* @__PURE__ */ jsx2(DialogTitle, { className: "eb:text-base eb:font-normal", children: editing ? t("Edit variable") : t("New variable") }),
1534
- /* @__PURE__ */ jsx2(DialogDescription, { className: "eb:text-[13px]", children: t("Available in every template.") })
1635
+ /* @__PURE__ */ jsx3(DialogHeader, { children: /* @__PURE__ */ jsxs3("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
1636
+ /* @__PURE__ */ jsx3("span", { className: "eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-lg2 eb:bg-primary/10 eb:text-primary", children: /* @__PURE__ */ jsx3(Braces2, { className: "eb:size-5" }) }),
1637
+ /* @__PURE__ */ jsxs3("div", { children: [
1638
+ /* @__PURE__ */ jsx3(DialogTitle, { className: "eb:text-base eb:font-normal", children: editing ? t("Edit variable") : t("New variable") }),
1639
+ /* @__PURE__ */ jsx3(DialogDescription, { className: "eb:text-[13px]", children: t("Available in every template.") })
1535
1640
  ] })
1536
1641
  ] }) }),
1537
- /* @__PURE__ */ jsxs2("form", { onSubmit: submit, className: "eb:space-y-3", noValidate: true, children: [
1642
+ /* @__PURE__ */ jsxs3("form", { onSubmit: submit, className: "eb:space-y-3", noValidate: true, children: [
1538
1643
  field("label", t("Name"), { autoFocus: true, maxLength: VARIABLE_LIMITS.label, placeholder: t("Order number") }),
1539
1644
  field(
1540
1645
  "key",
@@ -1542,9 +1647,9 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
1542
1647
  { disabled: editing, maxLength: VARIABLE_LIMITS.key, className: "eb:font-mono eb:text-[12px]" },
1543
1648
  form.key ? `{{${form.key}}}` : null
1544
1649
  ),
1545
- /* @__PURE__ */ jsxs2("div", { className: "eb:space-y-1.5", children: [
1546
- /* @__PURE__ */ jsx2(Label, { htmlFor: "eb-var-group", children: t("Group") }),
1547
- /* @__PURE__ */ jsx2(
1650
+ /* @__PURE__ */ jsxs3("div", { className: "eb:space-y-1.5", children: [
1651
+ /* @__PURE__ */ jsx3(Label, { htmlFor: "eb-var-group", children: t("Group") }),
1652
+ /* @__PURE__ */ jsx3(
1548
1653
  Input,
1549
1654
  {
1550
1655
  id: "eb-var-group",
@@ -1555,21 +1660,21 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
1555
1660
  placeholder: t(CUSTOM_GROUP)
1556
1661
  }
1557
1662
  ),
1558
- /* @__PURE__ */ jsx2("datalist", { id: "eb-var-groups", children: groups.map((g) => /* @__PURE__ */ jsx2("option", { value: g }, g)) })
1663
+ /* @__PURE__ */ jsx3("datalist", { id: "eb-var-groups", children: groups.map((g) => /* @__PURE__ */ jsx3("option", { value: g }, g)) })
1559
1664
  ] }),
1560
- /* @__PURE__ */ jsxs2("div", { className: "eb:grid eb:grid-cols-2 eb:gap-3", children: [
1665
+ /* @__PURE__ */ jsxs3("div", { className: "eb:grid eb:grid-cols-2 eb:gap-3", children: [
1561
1666
  field("sample", t("Sample value"), { maxLength: VARIABLE_LIMITS.sample, placeholder: "CMD-1042" }, t("Preview and tests")),
1562
1667
  field("fallback", t("Value if empty"), { maxLength: VARIABLE_LIMITS.fallback }, t("When a recipient has none"))
1563
1668
  ] }),
1564
- /* @__PURE__ */ jsxs2("div", { className: "eb:flex eb:items-center eb:gap-2 eb:pt-1", children: [
1565
- editing && /* @__PURE__ */ jsxs2(Button, { variant: "ghost", className: "eb:text-destructive eb:hover:text-destructive", onClick: remove, disabled: busy, children: [
1566
- /* @__PURE__ */ jsx2(Trash2, {}),
1669
+ /* @__PURE__ */ jsxs3("div", { className: "eb:flex eb:items-center eb:gap-2 eb:pt-1", children: [
1670
+ editing && /* @__PURE__ */ jsxs3(Button, { variant: "ghost", className: "eb:text-destructive eb:hover:text-destructive", onClick: remove, disabled: busy, children: [
1671
+ /* @__PURE__ */ jsx3(Trash2, {}),
1567
1672
  t("Delete")
1568
1673
  ] }),
1569
- /* @__PURE__ */ jsxs2("div", { className: "eb:ml-auto eb:flex eb:gap-2", children: [
1570
- /* @__PURE__ */ jsx2(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: busy, children: t("Cancel") }),
1571
- /* @__PURE__ */ jsxs2(Button, { type: "submit", disabled: busy, children: [
1572
- busy && /* @__PURE__ */ jsx2(Loader2, { className: "eb:animate-spin" }),
1674
+ /* @__PURE__ */ jsxs3("div", { className: "eb:ml-auto eb:flex eb:gap-2", children: [
1675
+ /* @__PURE__ */ jsx3(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: busy, children: t("Cancel") }),
1676
+ /* @__PURE__ */ jsxs3(Button, { type: "submit", disabled: busy, children: [
1677
+ busy && /* @__PURE__ */ jsx3(Loader2, { className: "eb:animate-spin" }),
1573
1678
  editing ? t("Save") : t("Create")
1574
1679
  ] })
1575
1680
  ] })
@@ -1581,13 +1686,13 @@ function VariableDialog({ open, onOpenChange, variable = null, initialLabel = ""
1581
1686
  }
1582
1687
 
1583
1688
  // src/features/variables/VariablePicker.jsx
1584
- import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
1689
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState4 } from "react";
1585
1690
  import { Pencil, Plus, Search } from "lucide-react";
1586
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1691
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1587
1692
  function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, filter }) {
1588
1693
  const t = useT();
1589
- const [query, setQuery] = useState3("");
1590
- const [active, setActive] = useState3(0);
1694
+ const [query, setQuery] = useState4("");
1695
+ const [active, setActive] = useState4(0);
1591
1696
  const listRef = useRef(null);
1592
1697
  const searchRef = useRef(
1593
1698
  /** @type {HTMLInputElement|null} */
@@ -1619,10 +1724,10 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1619
1724
  else if (onCreate && query.trim()) onCreate(query.trim());
1620
1725
  }
1621
1726
  };
1622
- return /* @__PURE__ */ jsxs3("div", { className: "eb:flex eb:max-h-[min(440px,70vh)] eb:flex-col", "data-testid": "variable-picker", children: [
1623
- /* @__PURE__ */ jsxs3("div", { className: "eb:relative eb:border-b eb:border-border eb:p-2", children: [
1624
- /* @__PURE__ */ jsx3(Search, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-4 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted" }),
1625
- /* @__PURE__ */ jsx3(
1727
+ return /* @__PURE__ */ jsxs4("div", { className: "eb:flex eb:max-h-[min(440px,70vh)] eb:flex-col", "data-testid": "variable-picker", children: [
1728
+ /* @__PURE__ */ jsxs4("div", { className: "eb:relative eb:border-b eb:border-border eb:p-2", children: [
1729
+ /* @__PURE__ */ jsx4(Search, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-4 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted" }),
1730
+ /* @__PURE__ */ jsx4(
1626
1731
  "input",
1627
1732
  {
1628
1733
  ref: searchRef,
@@ -1636,29 +1741,29 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1636
1741
  className: "eb:h-8 eb:w-full eb:rounded-md eb:bg-surface-75 eb:pr-14 eb:pl-7 eb:text-[13px] eb:text-foreground eb:outline-none eb:placeholder:text-foreground-muted eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40"
1637
1742
  }
1638
1743
  ),
1639
- /* @__PURE__ */ jsx3(SearchKeyHint, { className: "eb:absolute eb:top-1/2 eb:right-4 eb:-translate-y-1/2" })
1744
+ /* @__PURE__ */ jsx4(SearchKeyHint, { className: "eb:absolute eb:top-1/2 eb:right-4 eb:-translate-y-1/2" })
1640
1745
  ] }),
1641
- /* @__PURE__ */ jsxs3("div", { ref: listRef, role: "listbox", "aria-label": t("Variables"), className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: [
1642
- ordered.length === 0 && /* @__PURE__ */ jsxs3("div", { className: "eb:px-2 eb:py-3 eb:text-center eb:text-[12px] eb:text-foreground-muted", children: [
1643
- /* @__PURE__ */ jsx3("p", { children: t("No variable found.") }),
1644
- onCreate && query.trim() && /* @__PURE__ */ jsxs3(
1746
+ /* @__PURE__ */ jsxs4("div", { ref: listRef, role: "listbox", "aria-label": t("Variables"), className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: [
1747
+ ordered.length === 0 && /* @__PURE__ */ jsxs4("div", { className: "eb:px-2 eb:py-3 eb:text-center eb:text-[12px] eb:text-foreground-muted", children: [
1748
+ /* @__PURE__ */ jsx4("p", { children: t("No variable found.") }),
1749
+ onCreate && query.trim() && /* @__PURE__ */ jsxs4(
1645
1750
  "button",
1646
1751
  {
1647
1752
  type: "button",
1648
1753
  onClick: () => onCreate(query.trim()),
1649
1754
  className: "eb:mt-2 eb:inline-flex eb:items-center eb:gap-1 eb:rounded-md eb:px-2 eb:py-1 eb:text-[12px] eb:font-medium eb:text-primary eb:hover:bg-accent",
1650
1755
  children: [
1651
- /* @__PURE__ */ jsx3(Plus, { className: "eb:size-3.5" }),
1756
+ /* @__PURE__ */ jsx4(Plus, { className: "eb:size-3.5" }),
1652
1757
  t("Create \u201C{name}\u201D", { name: query.trim() })
1653
1758
  ]
1654
1759
  }
1655
1760
  )
1656
1761
  ] }),
1657
- groups.map((group) => /* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": t(group), children: [
1658
- /* @__PURE__ */ jsx3("p", { className: "eb:px-2 eb:pt-2 eb:pb-1 eb:text-[11px] eb:text-foreground-muted", children: t(group) }),
1762
+ groups.map((group) => /* @__PURE__ */ jsxs4("div", { role: "group", "aria-label": t(group), children: [
1763
+ /* @__PURE__ */ jsx4("p", { className: "eb:px-2 eb:pt-2 eb:pb-1 eb:text-[11px] eb:text-foreground-muted", children: t(group) }),
1659
1764
  ordered.filter((v) => v.group === group).map((variable) => {
1660
1765
  const index = ordered.indexOf(variable);
1661
- return /* @__PURE__ */ jsxs3(
1766
+ return /* @__PURE__ */ jsxs4(
1662
1767
  "div",
1663
1768
  {
1664
1769
  "data-active": index === active,
@@ -1668,7 +1773,7 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1668
1773
  ),
1669
1774
  onMouseEnter: () => setActive(index),
1670
1775
  children: [
1671
- /* @__PURE__ */ jsxs3(
1776
+ /* @__PURE__ */ jsxs4(
1672
1777
  "button",
1673
1778
  {
1674
1779
  type: "button",
@@ -1679,12 +1784,12 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1679
1784
  className: "eb:flex eb:min-w-0 eb:flex-1 eb:items-center eb:gap-2 eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px]",
1680
1785
  title: variable.description || void 0,
1681
1786
  children: [
1682
- /* @__PURE__ */ jsx3("span", { className: "eb:min-w-0 eb:flex-1 eb:truncate", children: variable.custom ? variable.label : t(variable.label) }),
1683
- /* @__PURE__ */ jsx3("code", { className: "eb:shrink-0 eb:font-mono eb:text-[10.5px] eb:text-foreground-muted", children: tokenOf(variable) })
1787
+ /* @__PURE__ */ jsx4("span", { className: "eb:min-w-0 eb:flex-1 eb:truncate", children: variable.custom ? variable.label : t(variable.label) }),
1788
+ /* @__PURE__ */ jsx4("code", { className: "eb:shrink-0 eb:font-mono eb:text-[10.5px] eb:text-foreground-muted", children: tokenOf(variable) })
1684
1789
  ]
1685
1790
  }
1686
1791
  ),
1687
- onEdit && variable.custom && /* @__PURE__ */ jsx3(
1792
+ onEdit && variable.custom && /* @__PURE__ */ jsx4(
1688
1793
  "button",
1689
1794
  {
1690
1795
  type: "button",
@@ -1692,7 +1797,7 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1692
1797
  title: t("Edit"),
1693
1798
  onClick: () => onEdit(variable),
1694
1799
  className: "eb:mr-1 eb:flex eb:size-6 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-md eb:text-foreground-muted eb:opacity-0 eb:group-hover/var:opacity-100 eb:hover:bg-background eb:hover:text-foreground eb:focus-visible:opacity-100",
1695
- children: /* @__PURE__ */ jsx3(Pencil, { className: "eb:size-3" })
1800
+ children: /* @__PURE__ */ jsx4(Pencil, { className: "eb:size-3" })
1696
1801
  }
1697
1802
  )
1698
1803
  ]
@@ -1702,14 +1807,14 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1702
1807
  })
1703
1808
  ] }, group))
1704
1809
  ] }),
1705
- onCreate && /* @__PURE__ */ jsx3("div", { className: "eb:border-t eb:border-border eb:p-1", children: /* @__PURE__ */ jsxs3(
1810
+ onCreate && /* @__PURE__ */ jsx4("div", { className: "eb:border-t eb:border-border eb:p-1", children: /* @__PURE__ */ jsxs4(
1706
1811
  "button",
1707
1812
  {
1708
1813
  type: "button",
1709
1814
  onClick: () => onCreate(query.trim()),
1710
1815
  className: "eb:flex eb:w-full eb:items-center eb:gap-2 eb:rounded-md eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px] eb:font-medium eb:text-primary eb:hover:bg-accent",
1711
1816
  children: [
1712
- /* @__PURE__ */ jsx3(Plus, { className: "eb:size-4" }),
1817
+ /* @__PURE__ */ jsx4(Plus, { className: "eb:size-4" }),
1713
1818
  t("New variable")
1714
1819
  ]
1715
1820
  }
@@ -1718,12 +1823,12 @@ function VariablePicker({ variables, onPick, onCreate = null, onEdit = null, fil
1718
1823
  }
1719
1824
 
1720
1825
  // src/features/variables/VariablesMenu.jsx
1721
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1826
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1722
1827
  function VariablesMenu({ context }) {
1723
1828
  const { editor, ready, variables, storage, canManageVariables, reloadVariables } = context;
1724
1829
  const { t, notify } = useUi();
1725
- const [open, setOpen] = useState4(false);
1726
- const [dialog, setDialog] = useState4(null);
1830
+ const [open, setOpen] = useState5(false);
1831
+ const [dialog, setDialog] = useState5(null);
1727
1832
  const insert = (variable) => {
1728
1833
  setOpen(false);
1729
1834
  const done = variable.kind === "link" ? editor.insertLink(tokenOf(variable), t("Unsubscribe")) : editor.insertText(tokenOf(variable));
@@ -1740,9 +1845,9 @@ function VariablesMenu({ context }) {
1740
1845
  setDialog({ variable, label: "" });
1741
1846
  }
1742
1847
  } : {};
1743
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
1744
- /* @__PURE__ */ jsxs4(Popover, { open, onOpenChange: setOpen, children: [
1745
- /* @__PURE__ */ jsx4(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(
1848
+ return /* @__PURE__ */ jsxs5(Fragment, { children: [
1849
+ /* @__PURE__ */ jsxs5(Popover, { open, onOpenChange: setOpen, children: [
1850
+ /* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
1746
1851
  Button,
1747
1852
  {
1748
1853
  variant: "outline",
@@ -1750,23 +1855,23 @@ function VariablesMenu({ context }) {
1750
1855
  disabled: !ready,
1751
1856
  onPointerDown: (e) => e.pointerType === "mouse" && e.preventDefault(),
1752
1857
  children: [
1753
- /* @__PURE__ */ jsx4(Braces2, {}),
1858
+ /* @__PURE__ */ jsx5(Braces3, {}),
1754
1859
  t("Variables")
1755
1860
  ]
1756
1861
  }
1757
1862
  ) }),
1758
- /* @__PURE__ */ jsx4(
1863
+ /* @__PURE__ */ jsx5(
1759
1864
  PopoverContent,
1760
1865
  {
1761
1866
  align: "end",
1762
1867
  className: "eb:w-80 eb:p-0",
1763
1868
  onCloseAutoFocus: (e) => e.preventDefault(),
1764
1869
  onMouseDown: (e) => e.stopPropagation(),
1765
- children: /* @__PURE__ */ jsx4(VariablePicker, { variables, onPick: insert, ...manage })
1870
+ children: /* @__PURE__ */ jsx5(VariablePicker, { variables, onPick: insert, ...manage })
1766
1871
  }
1767
1872
  )
1768
1873
  ] }),
1769
- canManageVariables && /* @__PURE__ */ jsx4(
1874
+ canManageVariables && /* @__PURE__ */ jsx5(
1770
1875
  VariableDialog,
1771
1876
  {
1772
1877
  open: Boolean(dialog),
@@ -1801,7 +1906,8 @@ var variablesFeature = {
1801
1906
  id: "variables",
1802
1907
  label: "Variables",
1803
1908
  order: 10,
1804
- toolbar: VariablesMenu
1909
+ toolbar: VariablesMenu,
1910
+ canvasBar: VariablesBar
1805
1911
  };
1806
1912
 
1807
1913
  // src/features/preview/index.js
@@ -1814,9 +1920,9 @@ var previewFeature = {
1814
1920
  };
1815
1921
 
1816
1922
  // src/features/test-email/TestEmailButton.jsx
1817
- import { useState as useState5 } from "react";
1923
+ import { useState as useState6 } from "react";
1818
1924
  import { CheckCircle2, Loader2 as Loader22, Send, XCircle } from "lucide-react";
1819
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1925
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1820
1926
  var KEY = "best-email-builder:test-addresses";
1821
1927
  function readAddresses(fallback) {
1822
1928
  try {
@@ -1834,10 +1940,10 @@ function rememberAddresses(value) {
1834
1940
  function TestEmailButton({ context }) {
1835
1941
  const { editor, ready, meta, storage, testRecipients = [] } = context;
1836
1942
  const { t, notify } = useUi();
1837
- const [open, setOpen] = useState5(false);
1838
- const [addresses, setAddresses] = useState5("");
1839
- const [sending, setSending] = useState5(false);
1840
- const [report, setReport] = useState5(null);
1943
+ const [open, setOpen] = useState6(false);
1944
+ const [addresses, setAddresses] = useState6("");
1945
+ const [sending, setSending] = useState6(false);
1946
+ const [report, setReport] = useState6(null);
1841
1947
  const emails = addresses.split(/[,;\s]+/).map((a) => a.trim()).filter(Boolean);
1842
1948
  const send = async () => {
1843
1949
  if (!emails.length) return;
@@ -1860,8 +1966,8 @@ function TestEmailButton({ context }) {
1860
1966
  setSending(false);
1861
1967
  }
1862
1968
  };
1863
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1864
- /* @__PURE__ */ jsxs5(
1969
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1970
+ /* @__PURE__ */ jsxs6(
1865
1971
  Button,
1866
1972
  {
1867
1973
  variant: "outline",
@@ -1873,22 +1979,22 @@ function TestEmailButton({ context }) {
1873
1979
  setOpen(true);
1874
1980
  },
1875
1981
  children: [
1876
- /* @__PURE__ */ jsx5(Send, {}),
1982
+ /* @__PURE__ */ jsx6(Send, {}),
1877
1983
  t("Test")
1878
1984
  ]
1879
1985
  }
1880
1986
  ),
1881
- /* @__PURE__ */ jsx5(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs5(DialogContent, { className: "eb:sm:max-w-[460px]", children: [
1882
- /* @__PURE__ */ jsx5(DialogHeader, { children: /* @__PURE__ */ jsxs5("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
1883
- /* @__PURE__ */ jsx5("span", { className: "eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-lg2 eb:bg-primary/10 eb:text-primary", children: /* @__PURE__ */ jsx5(Send, { className: "eb:size-5" }) }),
1884
- /* @__PURE__ */ jsxs5("div", { children: [
1885
- /* @__PURE__ */ jsx5(DialogTitle, { className: "eb:text-base eb:font-normal", children: t("Send a test") }),
1886
- /* @__PURE__ */ jsx5(DialogDescription, { className: "eb:text-[13px]", children: t("The email on screen, to reviewers\u2019 addresses.") })
1987
+ /* @__PURE__ */ jsx6(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs6(DialogContent, { className: "eb:sm:max-w-[460px]", children: [
1988
+ /* @__PURE__ */ jsx6(DialogHeader, { children: /* @__PURE__ */ jsxs6("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
1989
+ /* @__PURE__ */ jsx6("span", { className: "eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-lg2 eb:bg-primary/10 eb:text-primary", children: /* @__PURE__ */ jsx6(Send, { className: "eb:size-5" }) }),
1990
+ /* @__PURE__ */ jsxs6("div", { children: [
1991
+ /* @__PURE__ */ jsx6(DialogTitle, { className: "eb:text-base eb:font-normal", children: t("Send a test") }),
1992
+ /* @__PURE__ */ jsx6(DialogDescription, { className: "eb:text-[13px]", children: t("The email on screen, to reviewers\u2019 addresses.") })
1887
1993
  ] })
1888
1994
  ] }) }),
1889
- /* @__PURE__ */ jsxs5("div", { className: "eb:space-y-2", children: [
1890
- /* @__PURE__ */ jsx5(Label, { htmlFor: "eb-test-addresses", children: t("Addresses") }),
1891
- /* @__PURE__ */ jsx5(
1995
+ /* @__PURE__ */ jsxs6("div", { className: "eb:space-y-2", children: [
1996
+ /* @__PURE__ */ jsx6(Label, { htmlFor: "eb-test-addresses", children: t("Addresses") }),
1997
+ /* @__PURE__ */ jsx6(
1892
1998
  Textarea,
1893
1999
  {
1894
2000
  id: "eb-test-addresses",
@@ -1899,20 +2005,20 @@ function TestEmailButton({ context }) {
1899
2005
  }
1900
2006
  )
1901
2007
  ] }),
1902
- report && /* @__PURE__ */ jsxs5("ul", { className: "eb:space-y-1", "aria-label": t("Results"), children: [
1903
- report.results.map((r) => /* @__PURE__ */ jsxs5("li", { className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-foreground", children: [
1904
- r.ok ? /* @__PURE__ */ jsx5(CheckCircle2, { className: "eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-success", "aria-hidden": "true" }) : /* @__PURE__ */ jsx5(XCircle, { className: "eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-destructive", "aria-hidden": "true" }),
1905
- /* @__PURE__ */ jsxs5("span", { className: "eb:min-w-0 eb:break-all", children: [
2008
+ report && /* @__PURE__ */ jsxs6("ul", { className: "eb:space-y-1", "aria-label": t("Results"), children: [
2009
+ report.results.map((r) => /* @__PURE__ */ jsxs6("li", { className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-foreground", children: [
2010
+ r.ok ? /* @__PURE__ */ jsx6(CheckCircle2, { className: "eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-success", "aria-hidden": "true" }) : /* @__PURE__ */ jsx6(XCircle, { className: "eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-destructive", "aria-hidden": "true" }),
2011
+ /* @__PURE__ */ jsxs6("span", { className: "eb:min-w-0 eb:break-all", children: [
1906
2012
  r.email,
1907
2013
  r.error ? ` \u2014 ${r.error}` : ""
1908
2014
  ] })
1909
2015
  ] }, r.email)),
1910
- report.invalid.map((email2) => /* @__PURE__ */ jsx5("li", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("Ignored (invalid): {email}", { email: email2 }) }, `invalid-${email2}`))
2016
+ report.invalid.map((email2) => /* @__PURE__ */ jsx6("li", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("Ignored (invalid): {email}", { email: email2 }) }, `invalid-${email2}`))
1911
2017
  ] }),
1912
- /* @__PURE__ */ jsxs5("div", { className: "eb:flex eb:justify-end eb:gap-2", children: [
1913
- /* @__PURE__ */ jsx5(Button, { variant: "outline", onClick: () => setOpen(false), children: t("Close") }),
1914
- /* @__PURE__ */ jsxs5(Button, { onClick: send, disabled: sending || !emails.length, children: [
1915
- sending ? /* @__PURE__ */ jsx5(Loader22, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx5(Send, {}),
2018
+ /* @__PURE__ */ jsxs6("div", { className: "eb:flex eb:justify-end eb:gap-2", children: [
2019
+ /* @__PURE__ */ jsx6(Button, { variant: "outline", onClick: () => setOpen(false), children: t("Close") }),
2020
+ /* @__PURE__ */ jsxs6(Button, { onClick: send, disabled: sending || !emails.length, children: [
2021
+ sending ? /* @__PURE__ */ jsx6(Loader22, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx6(Send, {}),
1916
2022
  t("Send")
1917
2023
  ] })
1918
2024
  ] })
@@ -1930,9 +2036,9 @@ var testEmailFeature = {
1930
2036
  };
1931
2037
 
1932
2038
  // src/features/templates/TemplateGallery.jsx
1933
- import { useEffect as useEffect3, useMemo as useMemo3, useState as useState6 } from "react";
2039
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState7 } from "react";
1934
2040
  import { LayoutTemplate, Search as Search2, X } from "lucide-react";
1935
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2041
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1936
2042
  var THUMBNAIL_SCALE = 0.38;
1937
2043
  var EMAIL_WIDTH = 600;
1938
2044
  var FADE_LINE = "eb:block eb:overflow-hidden eb:whitespace-nowrap eb:[mask-image:linear-gradient(to_right,#000_calc(100%-2.5rem),transparent)]";
@@ -1940,12 +2046,12 @@ var NO_SCROLLBAR = "<style>html,body{overflow:hidden!important;scrollbar-width:n
1940
2046
  var withoutScrollbar = (html) => !html ? html : /<\/head>/i.test(html) ? html.replace(/<\/head>/i, `${NO_SCROLLBAR}</head>`) : NO_SCROLLBAR + html;
1941
2047
  var fold = (text) => String(text || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
1942
2048
  function Thumbnail({ html, title }) {
1943
- return /* @__PURE__ */ jsx6(
2049
+ return /* @__PURE__ */ jsx7(
1944
2050
  "div",
1945
2051
  {
1946
2052
  className: "eb:relative eb:h-[220px] eb:shrink-0 eb:overflow-hidden eb:rounded-md eb:border eb:border-border eb:bg-surface-75",
1947
2053
  "aria-hidden": "true",
1948
- children: /* @__PURE__ */ jsx6(
2054
+ children: /* @__PURE__ */ jsx7(
1949
2055
  "iframe",
1950
2056
  {
1951
2057
  title,
@@ -1967,10 +2073,10 @@ function Thumbnail({ html, title }) {
1967
2073
  function TemplateGalleryButton({ context }) {
1968
2074
  const { editor, ready, templates, isDirty, markDirty, template, setMeta } = context;
1969
2075
  const { t, confirm, notify } = useUi();
1970
- const [open, setOpen] = useState6(false);
1971
- const [query, setQuery] = useState6("");
1972
- const [chosen, setChosen] = useState6(() => /* @__PURE__ */ new Set());
1973
- const [rendered, setRendered] = useState6({});
2076
+ const [open, setOpen] = useState7(false);
2077
+ const [query, setQuery] = useState7("");
2078
+ const [chosen, setChosen] = useState7(() => /* @__PURE__ */ new Set());
2079
+ const [rendered, setRendered] = useState7({});
1974
2080
  useEffect3(() => {
1975
2081
  if (!open || !ready) return void 0;
1976
2082
  let alive = true;
@@ -2027,8 +2133,8 @@ function TemplateGalleryButton({ context }) {
2027
2133
  setOpen(false);
2028
2134
  notify("success", t("\u201C{name}\u201D template applied.", { name: entry.name }));
2029
2135
  };
2030
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
2031
- /* @__PURE__ */ jsxs6(
2136
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
2137
+ /* @__PURE__ */ jsxs7(
2032
2138
  Button,
2033
2139
  {
2034
2140
  variant: "outline",
@@ -2040,12 +2146,12 @@ function TemplateGalleryButton({ context }) {
2040
2146
  setOpen(true);
2041
2147
  },
2042
2148
  children: [
2043
- /* @__PURE__ */ jsx6(LayoutTemplate, {}),
2149
+ /* @__PURE__ */ jsx7(LayoutTemplate, {}),
2044
2150
  t("Templates")
2045
2151
  ]
2046
2152
  }
2047
2153
  ),
2048
- /* @__PURE__ */ jsx6(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs6(
2154
+ /* @__PURE__ */ jsx7(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs7(
2049
2155
  DialogContent,
2050
2156
  {
2051
2157
  className: "eb:flex eb:h-[min(48rem,calc(100dvh-2rem))] eb:flex-col eb:sm:max-w-4xl",
@@ -2057,14 +2163,14 @@ function TemplateGalleryButton({ context }) {
2057
2163
  }
2058
2164
  },
2059
2165
  children: [
2060
- /* @__PURE__ */ jsxs6(DialogHeader, { children: [
2061
- /* @__PURE__ */ jsx6(DialogTitle, { children: t("Start from a template") }),
2062
- /* @__PURE__ */ jsx6(DialogDescription, { children: t("Picking a template replaces the email being edited.") })
2166
+ /* @__PURE__ */ jsxs7(DialogHeader, { children: [
2167
+ /* @__PURE__ */ jsx7(DialogTitle, { children: t("Start from a template") }),
2168
+ /* @__PURE__ */ jsx7(DialogDescription, { children: t("Picking a template replaces the email being edited.") })
2063
2169
  ] }),
2064
- /* @__PURE__ */ jsxs6("div", { className: "eb:flex eb:shrink-0 eb:flex-col eb:gap-3", children: [
2065
- /* @__PURE__ */ jsxs6("div", { className: "eb:relative", children: [
2066
- /* @__PURE__ */ jsx6(Search2, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted" }),
2067
- /* @__PURE__ */ jsx6(
2170
+ /* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:shrink-0 eb:flex-col eb:gap-3", children: [
2171
+ /* @__PURE__ */ jsxs7("div", { className: "eb:relative", children: [
2172
+ /* @__PURE__ */ jsx7(Search2, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted" }),
2173
+ /* @__PURE__ */ jsx7(
2068
2174
  Input,
2069
2175
  {
2070
2176
  type: "search",
@@ -2075,20 +2181,20 @@ function TemplateGalleryButton({ context }) {
2075
2181
  className: "eb:pr-8 eb:pl-8 eb:[&::-webkit-search-cancel-button]:hidden"
2076
2182
  }
2077
2183
  ),
2078
- query && /* @__PURE__ */ jsx6(
2184
+ query && /* @__PURE__ */ jsx7(
2079
2185
  "button",
2080
2186
  {
2081
2187
  type: "button",
2082
2188
  onClick: () => setQuery(""),
2083
2189
  "aria-label": t("Clear the search"),
2084
2190
  className: "eb:absolute eb:top-1/2 eb:right-2.5 eb:-translate-y-1/2 eb:cursor-pointer",
2085
- children: /* @__PURE__ */ jsx6(X, { className: "eb:size-3.5 eb:text-foreground-muted eb:hover:text-foreground" })
2191
+ children: /* @__PURE__ */ jsx7(X, { className: "eb:size-3.5 eb:text-foreground-muted eb:hover:text-foreground" })
2086
2192
  }
2087
2193
  )
2088
2194
  ] }),
2089
- tags.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "eb:flex eb:items-start eb:gap-1.5", children: [
2090
- /* @__PURE__ */ jsx6("div", { role: "group", "aria-label": t("Filter by tag"), className: "eb:flex eb:min-w-0 eb:flex-1 eb:flex-wrap eb:gap-1.5", "data-testid": "eb-gallery-tags", children: tags.map((tag) => /* @__PURE__ */ jsx6(FilterChip, { pressed: chosen.has(tag), onClick: () => toggle(tag), children: tag }, tag)) }),
2091
- /* @__PURE__ */ jsx6(
2195
+ tags.length > 0 && /* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:items-start eb:gap-1.5", children: [
2196
+ /* @__PURE__ */ jsx7("div", { role: "group", "aria-label": t("Filter by tag"), className: "eb:flex eb:min-w-0 eb:flex-1 eb:flex-wrap eb:gap-1.5", "data-testid": "eb-gallery-tags", children: tags.map((tag) => /* @__PURE__ */ jsx7(FilterChip, { pressed: chosen.has(tag), onClick: () => toggle(tag), children: tag }, tag)) }),
2197
+ /* @__PURE__ */ jsx7(
2092
2198
  FilterReset,
2093
2199
  {
2094
2200
  label: t("Clear the tag filters"),
@@ -2099,7 +2205,7 @@ function TemplateGalleryButton({ context }) {
2099
2205
  )
2100
2206
  ] })
2101
2207
  ] }),
2102
- /* @__PURE__ */ jsx6("div", { className: "eb:-m-1 eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: shown.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "eb:m-0 eb:py-10 eb:text-center eb:text-[13px] eb:text-foreground-muted", children: t("Nothing matches \u201C{query}\u201D.", { query: query.trim() }) }) : /* @__PURE__ */ jsx6("ul", { className: "eb:m-0 eb:grid eb:list-none eb:grid-cols-1 eb:gap-3 eb:p-0 eb:sm:grid-cols-2 eb:md:grid-cols-3", children: shown.map((card) => /* @__PURE__ */ jsx6("li", { className: "eb:min-w-0", children: /* @__PURE__ */ jsxs6(
2208
+ /* @__PURE__ */ jsx7("div", { className: "eb:-m-1 eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:p-1", children: shown.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "eb:m-0 eb:py-10 eb:text-center eb:text-[13px] eb:text-foreground-muted", children: t("Nothing matches \u201C{query}\u201D.", { query: query.trim() }) }) : /* @__PURE__ */ jsx7("ul", { className: "eb:m-0 eb:grid eb:list-none eb:grid-cols-1 eb:gap-3 eb:p-0 eb:sm:grid-cols-2 eb:md:grid-cols-3", children: shown.map((card) => /* @__PURE__ */ jsx7("li", { className: "eb:min-w-0", children: /* @__PURE__ */ jsxs7(
2103
2209
  "button",
2104
2210
  {
2105
2211
  type: "button",
@@ -2108,10 +2214,10 @@ function TemplateGalleryButton({ context }) {
2108
2214
  title: card.description,
2109
2215
  className: "eb:group eb:flex eb:h-full eb:w-full eb:flex-col eb:gap-2 eb:rounded-lg2 eb:border eb:border-border eb:bg-background eb:p-2 eb:text-left eb:transition-colors eb:hover:border-primary/50 eb:hover:bg-primary/5 eb:focus-visible:outline-none eb:focus-visible:ring-2 eb:focus-visible:ring-ring/50",
2110
2216
  children: [
2111
- /* @__PURE__ */ jsx6(Thumbnail, { html: card.html, title: card.name }),
2112
- /* @__PURE__ */ jsxs6("span", { className: "eb:block eb:min-w-0 eb:px-1", children: [
2113
- /* @__PURE__ */ jsx6("span", { className: cn(FADE_LINE, "eb:text-[13px] eb:font-medium eb:text-foreground"), children: card.name }),
2114
- /* @__PURE__ */ jsx6("span", { className: cn(FADE_LINE, "eb:text-[12px] eb:text-foreground-muted"), children: card.description })
2217
+ /* @__PURE__ */ jsx7(Thumbnail, { html: card.html, title: card.name }),
2218
+ /* @__PURE__ */ jsxs7("span", { className: "eb:block eb:min-w-0 eb:px-1", children: [
2219
+ /* @__PURE__ */ jsx7("span", { className: cn(FADE_LINE, "eb:text-[13px] eb:font-medium eb:text-foreground"), children: card.name }),
2220
+ /* @__PURE__ */ jsx7("span", { className: cn(FADE_LINE, "eb:text-[12px] eb:text-foreground-muted"), children: card.description })
2115
2221
  ] })
2116
2222
  ]
2117
2223
  }
@@ -2132,14 +2238,14 @@ var templatesFeature = {
2132
2238
  };
2133
2239
 
2134
2240
  // src/features/brand/BrandButton.jsx
2135
- import { useEffect as useEffect4, useRef as useRef2, useState as useState8 } from "react";
2241
+ import { useEffect as useEffect4, useRef as useRef2, useState as useState9 } from "react";
2136
2242
  import { Globe, ImageOff, Loader2 as Loader23, Palette, Trash2 as Trash22, TriangleAlert, Upload, WandSparkles } from "lucide-react";
2137
2243
 
2138
2244
  // src/ui/primitives/color-picker.jsx
2139
- import { useState as useState7 } from "react";
2245
+ import { useState as useState8 } from "react";
2140
2246
  import { HexAlphaColorPicker } from "react-colorful";
2141
2247
  import { Check } from "lucide-react";
2142
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2248
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2143
2249
  var PRESET_COLORS = [
2144
2250
  "#8B5CF6",
2145
2251
  "#A78BFA",
@@ -2170,20 +2276,20 @@ function normalizeHex(v) {
2170
2276
  }
2171
2277
  function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
2172
2278
  const t = useT();
2173
- const [open, setOpen] = useState7(false);
2279
+ const [open, setOpen] = useState8(false);
2174
2280
  const valid = isValidHex(value);
2175
2281
  const normalized = valid ? normalizeHex(value) : "";
2176
2282
  const emit = (hex) => onChange(normalizeHex(hex));
2177
- return /* @__PURE__ */ jsxs7("div", { className: cn("eb:flex eb:gap-2", className), children: [
2178
- /* @__PURE__ */ jsxs7(Popover, { open, onOpenChange: setOpen, children: [
2179
- /* @__PURE__ */ jsx7(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
2283
+ return /* @__PURE__ */ jsxs8("div", { className: cn("eb:flex eb:gap-2", className), children: [
2284
+ /* @__PURE__ */ jsxs8(Popover, { open, onOpenChange: setOpen, children: [
2285
+ /* @__PURE__ */ jsx8(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx8(
2180
2286
  "button",
2181
2287
  {
2182
2288
  type: "button",
2183
2289
  "aria-label": t("Pick a colour"),
2184
2290
  title: t("Pick a colour"),
2185
2291
  className: "eb-checker eb:relative eb:h-8 eb:w-[44px] eb:shrink-0 eb:overflow-hidden eb:rounded-lg2 eb:shadow-sm eb:ring-1 eb:ring-black/10 eb:ring-inset eb:transition-transform eb:hover:scale-[1.04]",
2186
- children: /* @__PURE__ */ jsx7(
2292
+ children: /* @__PURE__ */ jsx8(
2187
2293
  "span",
2188
2294
  {
2189
2295
  className: "eb:absolute eb:inset-0",
@@ -2193,18 +2299,18 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
2193
2299
  )
2194
2300
  }
2195
2301
  ) }),
2196
- /* @__PURE__ */ jsxs7(PopoverContent, { align: "start", className: "eb:w-64 eb:space-y-3 eb:p-3", children: [
2197
- /* @__PURE__ */ jsx7("div", { className: "eb-picker", children: /* @__PURE__ */ jsx7(HexAlphaColorPicker, { color: valid ? value : FALLBACK_COLOR, onChange: emit }) }),
2198
- /* @__PURE__ */ jsxs7("div", { className: "eb:flex eb:items-center eb:gap-2", children: [
2199
- /* @__PURE__ */ jsx7(
2302
+ /* @__PURE__ */ jsxs8(PopoverContent, { align: "start", className: "eb:w-64 eb:space-y-3 eb:p-3", children: [
2303
+ /* @__PURE__ */ jsx8("div", { className: "eb-picker", children: /* @__PURE__ */ jsx8(HexAlphaColorPicker, { color: valid ? value : FALLBACK_COLOR, onChange: emit }) }),
2304
+ /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:items-center eb:gap-2", children: [
2305
+ /* @__PURE__ */ jsx8(
2200
2306
  "span",
2201
2307
  {
2202
2308
  className: "eb-checker eb:relative eb:size-8 eb:shrink-0 eb:overflow-hidden eb:rounded-lg2 eb:ring-1 eb:ring-black/10 eb:ring-inset",
2203
2309
  "aria-hidden": true,
2204
- children: /* @__PURE__ */ jsx7("span", { className: "eb:absolute eb:inset-0", style: { backgroundColor: valid ? value : "transparent" } })
2310
+ children: /* @__PURE__ */ jsx8("span", { className: "eb:absolute eb:inset-0", style: { backgroundColor: valid ? value : "transparent" } })
2205
2311
  }
2206
2312
  ),
2207
- /* @__PURE__ */ jsx7(
2313
+ /* @__PURE__ */ jsx8(
2208
2314
  Input,
2209
2315
  {
2210
2316
  value: value || "",
@@ -2216,7 +2322,7 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
2216
2322
  }
2217
2323
  )
2218
2324
  ] }),
2219
- /* @__PURE__ */ jsx7("div", { className: "eb:grid eb:grid-cols-6 eb:gap-1.5 eb:border-t eb:border-border eb:pt-2.5", children: PRESET_COLORS.map((c) => /* @__PURE__ */ jsx7(
2325
+ /* @__PURE__ */ jsx8("div", { className: "eb:grid eb:grid-cols-6 eb:gap-1.5 eb:border-t eb:border-border eb:pt-2.5", children: PRESET_COLORS.map((c) => /* @__PURE__ */ jsx8(
2220
2326
  "button",
2221
2327
  {
2222
2328
  type: "button",
@@ -2225,13 +2331,13 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
2225
2331
  "aria-label": c,
2226
2332
  className: "eb:relative eb:size-6 eb:rounded-lg2 eb:ring-1 eb:ring-black/10 eb:ring-inset eb:transition-transform eb:hover:scale-110",
2227
2333
  style: { backgroundColor: c },
2228
- children: normalized === c && /* @__PURE__ */ jsx7(Check, { className: "eb:absolute eb:inset-0 eb:m-auto eb:size-3.5 eb:text-white eb:drop-shadow-[0_1px_1px_rgba(0,0,0,0.6)]" })
2334
+ children: normalized === c && /* @__PURE__ */ jsx8(Check, { className: "eb:absolute eb:inset-0 eb:m-auto eb:size-3.5 eb:text-white eb:drop-shadow-[0_1px_1px_rgba(0,0,0,0.6)]" })
2229
2335
  },
2230
2336
  c
2231
2337
  )) })
2232
2338
  ] })
2233
2339
  ] }),
2234
- /* @__PURE__ */ jsx7(
2340
+ /* @__PURE__ */ jsx8(
2235
2341
  Input,
2236
2342
  {
2237
2343
  id,
@@ -2246,7 +2352,7 @@ function ColorPicker({ value, onChange, id, className, placeholder = "" }) {
2246
2352
  }
2247
2353
 
2248
2354
  // src/features/brand/BrandButton.jsx
2249
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2355
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2250
2356
  var toForm = (brand) => ({
2251
2357
  name: brand.name || "",
2252
2358
  logoUrl: brand.logoUrl || "",
@@ -2260,18 +2366,18 @@ var isLogoUrl = (url) => !url || url.startsWith("/") || /^https:\/\//i.test(url)
2260
2366
  function BrandButton({ context }) {
2261
2367
  const { editor, ready, storage, brand, setBrand, hostSender } = context;
2262
2368
  const { t, notify } = useUi();
2263
- const [open, setOpen] = useState8(false);
2264
- const [form, setForm] = useState8(() => toForm(brand || {}));
2265
- const [applyNow, setApplyNow] = useState8(true);
2266
- const [reading, setReading] = useState8(false);
2267
- const [readError, setReadError] = useState8("");
2268
- const [notes, setNotes] = useState8(
2369
+ const [open, setOpen] = useState9(false);
2370
+ const [form, setForm] = useState9(() => toForm(brand || {}));
2371
+ const [applyNow, setApplyNow] = useState9(true);
2372
+ const [reading, setReading] = useState9(false);
2373
+ const [readError, setReadError] = useState9("");
2374
+ const [notes, setNotes] = useState9(
2269
2375
  /** @type {string[]} */
2270
2376
  []
2271
2377
  );
2272
- const [uploading, setUploading] = useState8(false);
2273
- const [saving, setSaving] = useState8(false);
2274
- const [error, setError] = useState8("");
2378
+ const [uploading, setUploading] = useState9(false);
2379
+ const [saving, setSaving] = useState9(false);
2380
+ const [error, setError] = useState9("");
2275
2381
  const abortRef = useRef2(
2276
2382
  /** @type {AbortController|null} */
2277
2383
  null
@@ -2393,11 +2499,11 @@ function BrandButton({ context }) {
2393
2499
  }
2394
2500
  };
2395
2501
  const swatch = brand?.primary;
2396
- return /* @__PURE__ */ jsxs8(Fragment4, { children: [
2397
- /* @__PURE__ */ jsxs8(Button, { variant: "outline", size: "sm-compact", disabled: !ready, onClick: openDialog, "data-testid": "eb-brand-button", children: [
2398
- /* @__PURE__ */ jsx8(Palette, {}),
2502
+ return /* @__PURE__ */ jsxs9(Fragment4, { children: [
2503
+ /* @__PURE__ */ jsxs9(Button, { variant: "outline", size: "sm-compact", disabled: !ready, onClick: openDialog, "data-testid": "eb-brand-button", children: [
2504
+ /* @__PURE__ */ jsx9(Palette, {}),
2399
2505
  t("Brand"),
2400
- swatch && /* @__PURE__ */ jsx8(
2506
+ swatch && /* @__PURE__ */ jsx9(
2401
2507
  "span",
2402
2508
  {
2403
2509
  "aria-hidden": "true",
@@ -2406,18 +2512,18 @@ function BrandButton({ context }) {
2406
2512
  }
2407
2513
  )
2408
2514
  ] }),
2409
- /* @__PURE__ */ jsx8(Dialog, { open, onOpenChange: close, children: /* @__PURE__ */ jsxs8(
2515
+ /* @__PURE__ */ jsx9(Dialog, { open, onOpenChange: close, children: /* @__PURE__ */ jsxs9(
2410
2516
  DialogContent,
2411
2517
  {
2412
2518
  className: "eb:flex eb:max-h-[calc(100dvh-2rem)] eb:flex-col eb:gap-0 eb:p-0 eb:sm:max-w-2xl",
2413
2519
  "data-testid": "eb-brand",
2414
2520
  children: [
2415
- /* @__PURE__ */ jsxs8(DialogHeader, { className: "eb:gap-1.5 eb:px-6 eb:pt-6 eb:pb-4", children: [
2416
- /* @__PURE__ */ jsx8(DialogTitle, { children: t("Your brand") }),
2417
- /* @__PURE__ */ jsx8(DialogDescription, { children: t("Set it once: new emails, templates and blocks take your logo, colors and font.") })
2521
+ /* @__PURE__ */ jsxs9(DialogHeader, { className: "eb:gap-1.5 eb:px-6 eb:pt-6 eb:pb-4", children: [
2522
+ /* @__PURE__ */ jsx9(DialogTitle, { children: t("Your brand") }),
2523
+ /* @__PURE__ */ jsx9(DialogDescription, { children: t("Set it once: new emails, templates and blocks take your logo, colors and font.") })
2418
2524
  ] }),
2419
- /* @__PURE__ */ jsxs8("div", { className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:border-y eb:border-border eb:px-6 eb:py-5", children: [
2420
- canRead && /* @__PURE__ */ jsxs8(
2525
+ /* @__PURE__ */ jsxs9("div", { className: "eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:border-y eb:border-border eb:px-6 eb:py-5", children: [
2526
+ canRead && /* @__PURE__ */ jsxs9(
2421
2527
  "form",
2422
2528
  {
2423
2529
  className: "eb:mb-5 eb:flex eb:flex-col eb:gap-1.5",
@@ -2426,11 +2532,11 @@ function BrandButton({ context }) {
2426
2532
  readWebsite();
2427
2533
  },
2428
2534
  children: [
2429
- /* @__PURE__ */ jsx8(Label, { htmlFor: "eb-brand-website", children: t("Your website") }),
2430
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-2 eb:sm:flex-row", children: [
2431
- /* @__PURE__ */ jsxs8("div", { className: "eb:relative eb:min-w-0 eb:flex-1", children: [
2432
- /* @__PURE__ */ jsx8(Globe, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted", "aria-hidden": "true" }),
2433
- /* @__PURE__ */ jsx8(
2535
+ /* @__PURE__ */ jsx9(Label, { htmlFor: "eb-brand-website", children: t("Your website") }),
2536
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-2 eb:sm:flex-row", children: [
2537
+ /* @__PURE__ */ jsxs9("div", { className: "eb:relative eb:min-w-0 eb:flex-1", children: [
2538
+ /* @__PURE__ */ jsx9(Globe, { className: "eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted", "aria-hidden": "true" }),
2539
+ /* @__PURE__ */ jsx9(
2434
2540
  Input,
2435
2541
  {
2436
2542
  id: "eb-brand-website",
@@ -2444,28 +2550,28 @@ function BrandButton({ context }) {
2444
2550
  }
2445
2551
  )
2446
2552
  ] }),
2447
- /* @__PURE__ */ jsxs8(Button, { type: "submit", size: "sm", disabled: reading || !form.website.trim(), children: [
2448
- reading ? /* @__PURE__ */ jsx8(Loader23, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx8(WandSparkles, {}),
2553
+ /* @__PURE__ */ jsxs9(Button, { type: "submit", size: "sm", disabled: reading || !form.website.trim(), children: [
2554
+ reading ? /* @__PURE__ */ jsx9(Loader23, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx9(WandSparkles, {}),
2449
2555
  t("Get my brand")
2450
2556
  ] })
2451
2557
  ] }),
2452
- reading ? /* @__PURE__ */ jsx8("p", { role: "status", className: "eb:text-[12px] eb:text-foreground-muted", children: t("Reading the website\u2026") }) : readError ? /* @__PURE__ */ jsxs8("p", { role: "alert", className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive", children: [
2453
- /* @__PURE__ */ jsx8(TriangleAlert, { className: "eb:mt-px eb:size-3.5 eb:shrink-0", "aria-hidden": "true" }),
2558
+ reading ? /* @__PURE__ */ jsx9("p", { role: "status", className: "eb:text-[12px] eb:text-foreground-muted", children: t("Reading the website\u2026") }) : readError ? /* @__PURE__ */ jsxs9("p", { role: "alert", className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive", children: [
2559
+ /* @__PURE__ */ jsx9(TriangleAlert, { className: "eb:mt-px eb:size-3.5 eb:shrink-0", "aria-hidden": "true" }),
2454
2560
  readError
2455
- ] }) : notes.length ? /* @__PURE__ */ jsx8("ul", { className: "eb:flex eb:flex-col eb:gap-1 eb:text-[12px] eb:text-foreground-muted", "data-testid": "eb-brand-notes", children: notes.map((note) => /* @__PURE__ */ jsx8("li", { children: t(BRAND_NOTES[note]) }, note)) }) : /* @__PURE__ */ jsx8("p", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("The logo, colors and font are read from it. You can change them before saving.") })
2561
+ ] }) : notes.length ? /* @__PURE__ */ jsx9("ul", { className: "eb:flex eb:flex-col eb:gap-1 eb:text-[12px] eb:text-foreground-muted", "data-testid": "eb-brand-notes", children: notes.map((note) => /* @__PURE__ */ jsx9("li", { children: t(BRAND_NOTES[note]) }, note)) }) : /* @__PURE__ */ jsx9("p", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("The logo, colors and font are read from it. You can change them before saving.") })
2456
2562
  ]
2457
2563
  }
2458
2564
  ),
2459
- /* @__PURE__ */ jsxs8("div", { className: "eb:grid eb:gap-6 eb:sm:grid-cols-[minmax(0,1fr)_220px]", children: [
2460
- /* @__PURE__ */ jsxs8("fieldset", { disabled: reading, className: "eb:m-0 eb:flex eb:min-w-0 eb:flex-col eb:gap-4 eb:border-0 eb:p-0", children: [
2461
- /* @__PURE__ */ jsx8("legend", { className: "eb:sr-only", children: t("Your brand") }),
2462
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2463
- /* @__PURE__ */ jsx8(Label, { htmlFor: "eb-brand-logo", children: t("Logo") }),
2464
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
2465
- /* @__PURE__ */ jsx8("div", { className: "eb-checker eb:flex eb:h-12 eb:w-24 eb:shrink-0 eb:items-center eb:justify-center eb:overflow-hidden eb:rounded-lg2 eb:ring-1 eb:ring-border eb:ring-inset", children: form.logoUrl && !logoInvalid ? /* @__PURE__ */ jsx8("img", { src: form.logoUrl, alt: t("Logo"), className: "eb:max-h-10 eb:max-w-20 eb:object-contain" }) : /* @__PURE__ */ jsx8(ImageOff, { className: "eb:size-4 eb:text-foreground-muted", "aria-hidden": "true" }) }),
2466
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-wrap eb:gap-2", children: [
2467
- canUpload && /* @__PURE__ */ jsxs8(Fragment4, { children: [
2468
- /* @__PURE__ */ jsx8(
2565
+ /* @__PURE__ */ jsxs9("div", { className: "eb:grid eb:gap-6 eb:sm:grid-cols-[minmax(0,1fr)_220px]", children: [
2566
+ /* @__PURE__ */ jsxs9("fieldset", { disabled: reading, className: "eb:m-0 eb:flex eb:min-w-0 eb:flex-col eb:gap-4 eb:border-0 eb:p-0", children: [
2567
+ /* @__PURE__ */ jsx9("legend", { className: "eb:sr-only", children: t("Your brand") }),
2568
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2569
+ /* @__PURE__ */ jsx9(Label, { htmlFor: "eb-brand-logo", children: t("Logo") }),
2570
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:items-center eb:gap-3", children: [
2571
+ /* @__PURE__ */ jsx9("div", { className: "eb-checker eb:flex eb:h-12 eb:w-24 eb:shrink-0 eb:items-center eb:justify-center eb:overflow-hidden eb:rounded-lg2 eb:ring-1 eb:ring-border eb:ring-inset", children: form.logoUrl && !logoInvalid ? /* @__PURE__ */ jsx9("img", { src: form.logoUrl, alt: t("Logo"), className: "eb:max-h-10 eb:max-w-20 eb:object-contain" }) : /* @__PURE__ */ jsx9(ImageOff, { className: "eb:size-4 eb:text-foreground-muted", "aria-hidden": "true" }) }),
2572
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-wrap eb:gap-2", children: [
2573
+ canUpload && /* @__PURE__ */ jsxs9(Fragment4, { children: [
2574
+ /* @__PURE__ */ jsx9(
2469
2575
  "input",
2470
2576
  {
2471
2577
  ref: fileRef,
@@ -2478,18 +2584,18 @@ function BrandButton({ context }) {
2478
2584
  onChange: pickLogo
2479
2585
  }
2480
2586
  ),
2481
- /* @__PURE__ */ jsxs8(Button, { type: "button", variant: "outline", size: "sm", disabled: uploading, onClick: () => fileRef.current?.click(), children: [
2482
- uploading ? /* @__PURE__ */ jsx8(Loader23, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx8(Upload, {}),
2587
+ /* @__PURE__ */ jsxs9(Button, { type: "button", variant: "outline", size: "sm", disabled: uploading, onClick: () => fileRef.current?.click(), children: [
2588
+ uploading ? /* @__PURE__ */ jsx9(Loader23, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx9(Upload, {}),
2483
2589
  t("Upload")
2484
2590
  ] })
2485
2591
  ] }),
2486
- form.logoUrl && /* @__PURE__ */ jsxs8(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => set({ logoUrl: "", logoWidth: 0 }), children: [
2487
- /* @__PURE__ */ jsx8(Trash22, {}),
2592
+ form.logoUrl && /* @__PURE__ */ jsxs9(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => set({ logoUrl: "", logoWidth: 0 }), children: [
2593
+ /* @__PURE__ */ jsx9(Trash22, {}),
2488
2594
  t("Remove")
2489
2595
  ] })
2490
2596
  ] })
2491
2597
  ] }),
2492
- /* @__PURE__ */ jsx8(
2598
+ /* @__PURE__ */ jsx9(
2493
2599
  Input,
2494
2600
  {
2495
2601
  id: "eb-brand-logo",
@@ -2500,11 +2606,11 @@ function BrandButton({ context }) {
2500
2606
  className: "eb:text-[12px]"
2501
2607
  }
2502
2608
  ),
2503
- logoInvalid && /* @__PURE__ */ jsx8("p", { className: "eb:text-[12px] eb:text-destructive", children: t("An https:// address is required") })
2609
+ logoInvalid && /* @__PURE__ */ jsx9("p", { className: "eb:text-[12px] eb:text-destructive", children: t("An https:// address is required") })
2504
2610
  ] }),
2505
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2506
- /* @__PURE__ */ jsx8(Label, { htmlFor: "eb-brand-name", children: t("Company name") }),
2507
- /* @__PURE__ */ jsx8(
2611
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2612
+ /* @__PURE__ */ jsx9(Label, { htmlFor: "eb-brand-name", children: t("Company name") }),
2613
+ /* @__PURE__ */ jsx9(
2508
2614
  Input,
2509
2615
  {
2510
2616
  id: "eb-brand-name",
@@ -2515,36 +2621,36 @@ function BrandButton({ context }) {
2515
2621
  }
2516
2622
  )
2517
2623
  ] }),
2518
- /* @__PURE__ */ jsxs8("div", { className: "eb:grid eb:gap-4 eb:sm:grid-cols-2", children: [
2519
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2520
- /* @__PURE__ */ jsx8(Label, { htmlFor: "eb-brand-primary", children: t("Main color") }),
2521
- /* @__PURE__ */ jsx8(ColorPicker, { id: "eb-brand-primary", value: form.primary, onChange: (primary) => set({ primary }), placeholder: PALETTE.navy }),
2522
- /* @__PURE__ */ jsx8("p", { className: "eb:text-[11px] eb:text-foreground-muted", children: t("Buttons, banners, links") })
2624
+ /* @__PURE__ */ jsxs9("div", { className: "eb:grid eb:gap-4 eb:sm:grid-cols-2", children: [
2625
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2626
+ /* @__PURE__ */ jsx9(Label, { htmlFor: "eb-brand-primary", children: t("Main color") }),
2627
+ /* @__PURE__ */ jsx9(ColorPicker, { id: "eb-brand-primary", value: form.primary, onChange: (primary) => set({ primary }), placeholder: PALETTE.navy }),
2628
+ /* @__PURE__ */ jsx9("p", { className: "eb:text-[11px] eb:text-foreground-muted", children: t("Buttons, banners, links") })
2523
2629
  ] }),
2524
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2525
- /* @__PURE__ */ jsx8(Label, { htmlFor: "eb-brand-heading", children: t("Headings color") }),
2526
- /* @__PURE__ */ jsx8(ColorPicker, { id: "eb-brand-heading", value: form.heading, onChange: (heading2) => set({ heading: heading2 }), placeholder: PALETTE.navy })
2630
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2631
+ /* @__PURE__ */ jsx9(Label, { htmlFor: "eb-brand-heading", children: t("Headings color") }),
2632
+ /* @__PURE__ */ jsx9(ColorPicker, { id: "eb-brand-heading", value: form.heading, onChange: (heading2) => set({ heading: heading2 }), placeholder: PALETTE.navy })
2527
2633
  ] })
2528
2634
  ] }),
2529
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2530
- /* @__PURE__ */ jsx8(Label, { htmlFor: "eb-brand-font", children: t("Font") }),
2531
- /* @__PURE__ */ jsxs8(Select, { value: form.font || FONT_STACK, onValueChange: (font) => set({ font }), children: [
2532
- /* @__PURE__ */ jsx8(SelectTrigger, { id: "eb-brand-font", children: /* @__PURE__ */ jsx8(SelectValue, {}) }),
2533
- /* @__PURE__ */ jsx8(SelectContent, { children: FONT_OPTIONS.map((option) => /* @__PURE__ */ jsx8(SelectItem, { value: option.value, children: /* @__PURE__ */ jsx8("span", { style: { fontFamily: option.value }, children: option.label }) }, option.value)) })
2635
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2636
+ /* @__PURE__ */ jsx9(Label, { htmlFor: "eb-brand-font", children: t("Font") }),
2637
+ /* @__PURE__ */ jsxs9(Select, { value: form.font || FONT_STACK, onValueChange: (font) => set({ font }), children: [
2638
+ /* @__PURE__ */ jsx9(SelectTrigger, { id: "eb-brand-font", children: /* @__PURE__ */ jsx9(SelectValue, {}) }),
2639
+ /* @__PURE__ */ jsx9(SelectContent, { children: FONT_OPTIONS.map((option) => /* @__PURE__ */ jsx9(SelectItem, { value: option.value, children: /* @__PURE__ */ jsx9("span", { style: { fontFamily: option.value }, children: option.label }) }, option.value)) })
2534
2640
  ] })
2535
2641
  ] })
2536
2642
  ] }),
2537
- /* @__PURE__ */ jsx8(BrandPreview, { brand: draft, fallbackName: hostSender?.name || t("Your company") })
2643
+ /* @__PURE__ */ jsx9(BrandPreview, { brand: draft, fallbackName: hostSender?.name || t("Your company") })
2538
2644
  ] })
2539
2645
  ] }),
2540
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-3 eb:px-6 eb:pt-3 eb:pb-6", children: [
2541
- error && /* @__PURE__ */ jsxs8("p", { role: "alert", className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive", children: [
2542
- /* @__PURE__ */ jsx8(TriangleAlert, { className: "eb:mt-px eb:size-3.5 eb:shrink-0", "aria-hidden": "true" }),
2646
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-3 eb:px-6 eb:pt-3 eb:pb-6", children: [
2647
+ error && /* @__PURE__ */ jsxs9("p", { role: "alert", className: "eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive", children: [
2648
+ /* @__PURE__ */ jsx9(TriangleAlert, { className: "eb:mt-px eb:size-3.5 eb:shrink-0", "aria-hidden": "true" }),
2543
2649
  error
2544
2650
  ] }),
2545
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-3 eb:sm:flex-row eb:sm:items-center", children: [
2546
- /* @__PURE__ */ jsxs8("label", { className: "eb:flex eb:cursor-pointer eb:items-center eb:gap-2 eb:text-[13px] eb:sm:mr-auto", children: [
2547
- /* @__PURE__ */ jsx8(
2651
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-3 eb:sm:flex-row eb:sm:items-center", children: [
2652
+ /* @__PURE__ */ jsxs9("label", { className: "eb:flex eb:cursor-pointer eb:items-center eb:gap-2 eb:text-[13px] eb:sm:mr-auto", children: [
2653
+ /* @__PURE__ */ jsx9(
2548
2654
  "input",
2549
2655
  {
2550
2656
  type: "checkbox",
@@ -2556,10 +2662,10 @@ function BrandButton({ context }) {
2556
2662
  ),
2557
2663
  t("Also apply to this email")
2558
2664
  ] }),
2559
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:justify-end eb:gap-2", children: [
2560
- /* @__PURE__ */ jsx8(Button, { variant: "outline", size: "sm", onClick: () => close(false), children: t("Cancel") }),
2561
- /* @__PURE__ */ jsxs8(Button, { size: "sm", onClick: save, disabled: saving || reading || uploading || logoInvalid, "data-testid": "eb-brand-save", children: [
2562
- saving && /* @__PURE__ */ jsx8(Loader23, { className: "eb:animate-spin" }),
2665
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:justify-end eb:gap-2", children: [
2666
+ /* @__PURE__ */ jsx9(Button, { variant: "outline", size: "sm", onClick: () => close(false), children: t("Cancel") }),
2667
+ /* @__PURE__ */ jsxs9(Button, { size: "sm", onClick: save, disabled: saving || reading || uploading || logoInvalid, "data-testid": "eb-brand-save", children: [
2668
+ saving && /* @__PURE__ */ jsx9(Loader23, { className: "eb:animate-spin" }),
2563
2669
  t("Save")
2564
2670
  ] })
2565
2671
  ] })
@@ -2578,30 +2684,30 @@ function BrandPreview({ brand, fallbackName }) {
2578
2684
  const link2 = contrastRatio(primary, PALETTE.white) >= 3 ? primary : heading2;
2579
2685
  const font = brand.font || FONT_STACK;
2580
2686
  const name = brand.name || fallbackName;
2581
- return /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2582
- /* @__PURE__ */ jsx8("span", { className: "eb:text-[12px] eb:font-medium eb:text-foreground-muted", children: t("Preview") }),
2583
- /* @__PURE__ */ jsx8(
2687
+ return /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5", children: [
2688
+ /* @__PURE__ */ jsx9("span", { className: "eb:text-[12px] eb:font-medium eb:text-foreground-muted", children: t("Preview") }),
2689
+ /* @__PURE__ */ jsx9(
2584
2690
  "div",
2585
2691
  {
2586
2692
  className: "eb:overflow-hidden eb:rounded-lg2 eb:p-2.5 eb:ring-1 eb:ring-border",
2587
2693
  style: { backgroundColor: PALETTE.canvas, fontFamily: font },
2588
2694
  "data-testid": "eb-brand-preview",
2589
2695
  "aria-hidden": "true",
2590
- children: /* @__PURE__ */ jsxs8("div", { className: "eb:overflow-hidden eb:rounded-[3px]", style: { backgroundColor: PALETTE.white }, children: [
2591
- /* @__PURE__ */ jsx8("div", { className: "eb:px-3 eb:pt-3 eb:pb-2", children: brand.logoUrl ? /* @__PURE__ */ jsx8("img", { src: brand.logoUrl, alt: "", style: { width: Math.round((brand.logoWidth || 140) * 0.45), maxWidth: "100%" } }) : /* @__PURE__ */ jsx8("span", { className: "eb:text-[11px] eb:font-bold", style: { color: PALETTE.ink }, children: name }) }),
2592
- /* @__PURE__ */ jsxs8("div", { className: "eb:px-3 eb:py-4 eb:text-center", style: { backgroundColor: primary, color: onPrimary }, children: [
2593
- /* @__PURE__ */ jsx8("div", { className: "eb:text-[13px] eb:leading-tight eb:font-bold", children: t("Big news") }),
2594
- /* @__PURE__ */ jsx8("div", { className: "eb:mt-1 eb:text-[9px] eb:opacity-90", children: t("A short line under the title.") })
2696
+ children: /* @__PURE__ */ jsxs9("div", { className: "eb:overflow-hidden eb:rounded-[3px]", style: { backgroundColor: PALETTE.white }, children: [
2697
+ /* @__PURE__ */ jsx9("div", { className: "eb:px-3 eb:pt-3 eb:pb-2", children: brand.logoUrl ? /* @__PURE__ */ jsx9("img", { src: brand.logoUrl, alt: "", style: { width: Math.round((brand.logoWidth || 140) * 0.45), maxWidth: "100%" } }) : /* @__PURE__ */ jsx9("span", { className: "eb:text-[11px] eb:font-bold", style: { color: PALETTE.ink }, children: name }) }),
2698
+ /* @__PURE__ */ jsxs9("div", { className: "eb:px-3 eb:py-4 eb:text-center", style: { backgroundColor: primary, color: onPrimary }, children: [
2699
+ /* @__PURE__ */ jsx9("div", { className: "eb:text-[13px] eb:leading-tight eb:font-bold", children: t("Big news") }),
2700
+ /* @__PURE__ */ jsx9("div", { className: "eb:mt-1 eb:text-[9px] eb:opacity-90", children: t("A short line under the title.") })
2595
2701
  ] }),
2596
- /* @__PURE__ */ jsxs8("div", { className: "eb:flex eb:flex-col eb:gap-1.5 eb:px-3 eb:py-3", children: [
2597
- /* @__PURE__ */ jsx8("div", { className: "eb:text-[11px] eb:leading-tight eb:font-bold", style: { color: heading2 }, children: t("A headline") }),
2598
- /* @__PURE__ */ jsxs8("div", { className: "eb:text-[9px] eb:leading-snug", style: { color: PALETTE.ink }, children: [
2702
+ /* @__PURE__ */ jsxs9("div", { className: "eb:flex eb:flex-col eb:gap-1.5 eb:px-3 eb:py-3", children: [
2703
+ /* @__PURE__ */ jsx9("div", { className: "eb:text-[11px] eb:leading-tight eb:font-bold", style: { color: heading2 }, children: t("A headline") }),
2704
+ /* @__PURE__ */ jsxs9("div", { className: "eb:text-[9px] eb:leading-snug", style: { color: PALETTE.ink }, children: [
2599
2705
  t("Some text, with"),
2600
2706
  " ",
2601
- /* @__PURE__ */ jsx8("span", { className: "eb:underline", style: { color: link2 }, children: t("a link") }),
2707
+ /* @__PURE__ */ jsx9("span", { className: "eb:underline", style: { color: link2 }, children: t("a link") }),
2602
2708
  "."
2603
2709
  ] }),
2604
- /* @__PURE__ */ jsx8(
2710
+ /* @__PURE__ */ jsx9(
2605
2711
  "span",
2606
2712
  {
2607
2713
  className: cn("eb:mt-1 eb:self-start eb:rounded-[3px] eb:px-2.5 eb:py-1 eb:text-[9px] eb:font-bold"),
@@ -2610,7 +2716,7 @@ function BrandPreview({ brand, fallbackName }) {
2610
2716
  }
2611
2717
  )
2612
2718
  ] }),
2613
- /* @__PURE__ */ jsx8("div", { className: "eb:px-3 eb:pb-2.5 eb:text-center eb:text-[7px]", style: { color: "#6d7484" }, children: name })
2719
+ /* @__PURE__ */ jsx9("div", { className: "eb:px-3 eb:pb-2.5 eb:text-center eb:text-[7px]", style: { color: "#6d7484" }, children: name })
2614
2720
  ] })
2615
2721
  }
2616
2722
  )
@@ -2921,6 +3027,9 @@ body { margin: 0; }
2921
3027
  .eb-node a { cursor: pointer; }
2922
3028
  [contenteditable="true"] { outline: none; cursor: text; caret-color: ${PRIMARY}; }
2923
3029
  td.eb-placeholder { outline: 1.5px dashed #c3cad6; outline-offset: -8px; }
3030
+ .eb-preview-text[data-typing]::after { content: ''; display: inline-block; width: 1.5px; height: 1em; margin-left: 1px;
3031
+ vertical-align: -0.12em; background: currentColor; animation: eb-preview-caret 0.7s steps(1) infinite; }
3032
+ @keyframes eb-preview-caret { 50% { opacity: 0; } }
2924
3033
  .eb-placeholder { user-select: none; }
2925
3034
 
2926
3035
  #eb-ui { position: absolute; top: 0; left: 0; width: 0; height: 0; z-index: 2147483000;
@@ -3205,6 +3314,7 @@ function createCanvasView(options) {
3205
3314
  }
3206
3315
  syncAttributes(doc.body, parsed.body);
3207
3316
  morphChildren(doc.body, parsed.body, (element) => element === editingEl);
3317
+ if (preview && !preview.el.isConnected) dropPreview();
3208
3318
  if (editingId && editingEl && !editingEl.isConnected) {
3209
3319
  const again = editableOf(editingId);
3210
3320
  if (again) {
@@ -3364,6 +3474,7 @@ function createCanvasView(options) {
3364
3474
  function startEditing(id, at = null) {
3365
3475
  if (editingId === id) return;
3366
3476
  finishEditing();
3477
+ dropPreview();
3367
3478
  const el = editableOf(id);
3368
3479
  if (!el) return;
3369
3480
  editingId = id;
@@ -3380,10 +3491,18 @@ function createCanvasView(options) {
3380
3491
  refreshOverlays();
3381
3492
  }
3382
3493
  function contentOfEditing() {
3383
- return editingEl ? cleanRichText(editingEl.innerHTML, doc) : "";
3494
+ if (!editingEl) return "";
3495
+ let source = editingEl;
3496
+ if (preview && editingEl.contains(preview.el)) {
3497
+ source = /** @type {HTMLElement} */
3498
+ editingEl.cloneNode(true);
3499
+ source.querySelectorAll(`.${PREVIEW_CLASS}`).forEach((el) => el.remove());
3500
+ }
3501
+ return cleanRichText(source.innerHTML, doc);
3384
3502
  }
3385
3503
  function finishEditing() {
3386
3504
  if (!editingId || !editingEl) return;
3505
+ dropPreview();
3387
3506
  const id = editingId;
3388
3507
  const html = contentOfEditing();
3389
3508
  editingEl.removeAttribute("contenteditable");
@@ -3439,6 +3558,90 @@ function createCanvasView(options) {
3439
3558
  } else placeCaret(null);
3440
3559
  return true;
3441
3560
  }
3561
+ let preview = null;
3562
+ const PREVIEW_CLASS = "eb-preview-text";
3563
+ function previewSpot() {
3564
+ if (editingEl) {
3565
+ const range = savedRange && editingEl.contains(savedRange.commonAncestorContainer) ? savedRange.cloneRange() : null;
3566
+ if (range) {
3567
+ range.collapse(false);
3568
+ return { range, prefix: "" };
3569
+ }
3570
+ return { parent: editingEl, prefix: "" };
3571
+ }
3572
+ const tag = selectedId ? info(selectedId)?.tag : null;
3573
+ if (!selectedId || tag !== "mj-text" && tag !== "mj-button") return null;
3574
+ const parent = tag === "mj-text" ? editableOf(selectedId) : elementOf(selectedId)?.querySelector("a, p");
3575
+ return parent ? { parent, prefix: " " } : null;
3576
+ }
3577
+ function stepPreview() {
3578
+ if (!preview) return;
3579
+ const current = preview;
3580
+ const { shown, target } = current;
3581
+ const next = !target.startsWith(shown) ? shown.slice(0, -1) : shown.length < target.length ? target.slice(0, shown.length + 1) : null;
3582
+ if (next === null) {
3583
+ current.timer = 0;
3584
+ current.el.removeAttribute("data-typing");
3585
+ if (!target) dropPreview();
3586
+ return;
3587
+ }
3588
+ current.shown = next;
3589
+ current.el.textContent = next;
3590
+ current.el.setAttribute("data-typing", "");
3591
+ current.timer = win.setTimeout(stepPreview, next.length < shown.length ? 12 : 22);
3592
+ scheduleOverlays();
3593
+ }
3594
+ function animatePreview() {
3595
+ if (!preview || preview.timer) return;
3596
+ if (win.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
3597
+ preview.shown = preview.target;
3598
+ preview.el.textContent = preview.target;
3599
+ if (!preview.target) dropPreview();
3600
+ scheduleOverlays();
3601
+ return;
3602
+ }
3603
+ stepPreview();
3604
+ }
3605
+ function previewText(text) {
3606
+ if (!text) {
3607
+ hidePreview();
3608
+ return false;
3609
+ }
3610
+ if (preview && !preview.el.isConnected) dropPreview();
3611
+ if (!preview) {
3612
+ const spot = previewSpot();
3613
+ if (!spot) return false;
3614
+ const el = doc.createElement("span");
3615
+ el.className = PREVIEW_CLASS;
3616
+ el.setAttribute("contenteditable", "false");
3617
+ el.setAttribute("data-testid", "eb-canvas-preview");
3618
+ if (spot.range) spot.range.insertNode(el);
3619
+ else spot.parent?.appendChild(el);
3620
+ preview = { el, prefix: spot.prefix, shown: "", target: "", timer: 0 };
3621
+ }
3622
+ preview.target = preview.prefix + text;
3623
+ animatePreview();
3624
+ return true;
3625
+ }
3626
+ function hidePreview() {
3627
+ if (!preview) return;
3628
+ preview.target = "";
3629
+ animatePreview();
3630
+ }
3631
+ function dropPreview() {
3632
+ if (!preview) return;
3633
+ win.clearTimeout(preview.timer);
3634
+ preview.el.remove();
3635
+ preview = null;
3636
+ scheduleOverlays();
3637
+ }
3638
+ function settlePreview() {
3639
+ if (!preview) return;
3640
+ win.clearTimeout(preview.timer);
3641
+ preview.el.textContent = preview.target;
3642
+ preview.el.removeAttribute("data-typing");
3643
+ preview = null;
3644
+ }
3442
3645
  const TEXT_BUTTONS = [
3443
3646
  ["bold", t("Bold (Ctrl+B)")],
3444
3647
  ["italic", t("Italic (Ctrl+I)")],
@@ -3760,6 +3963,7 @@ function createCanvasView(options) {
3760
3963
  /** @param {string|null} id */
3761
3964
  setSelection(id) {
3762
3965
  if (editingId && editingId !== id) finishEditing();
3966
+ if (id !== selectedId) dropPreview();
3763
3967
  selectedId = id;
3764
3968
  refreshOverlays();
3765
3969
  if (id) {
@@ -3792,6 +3996,7 @@ function createCanvasView(options) {
3792
3996
  editingId: () => editingId,
3793
3997
  /** @param {string} text */
3794
3998
  insertText(text) {
3999
+ dropPreview();
3795
4000
  if (!restoreCaret()) return false;
3796
4001
  doc.execCommand("insertText", false, text);
3797
4002
  options.onEditInput(
@@ -3803,6 +4008,7 @@ function createCanvasView(options) {
3803
4008
  },
3804
4009
  /** @param {string} html */
3805
4010
  insertHtml(html) {
4011
+ dropPreview();
3806
4012
  if (!restoreCaret()) return false;
3807
4013
  doc.execCommand("insertHTML", false, html);
3808
4014
  options.onEditInput(
@@ -3812,8 +4018,12 @@ function createCanvasView(options) {
3812
4018
  );
3813
4019
  return true;
3814
4020
  },
4021
+ previewText,
4022
+ hidePreview,
4023
+ settlePreview,
3815
4024
  destroy() {
3816
4025
  destroyed = true;
4026
+ dropPreview();
3817
4027
  cancelDrag();
3818
4028
  resizeObserver.disconnect();
3819
4029
  if (overlayFrame) win.cancelAnimationFrame(overlayFrame);
@@ -4358,9 +4568,16 @@ function createNativeAdapter({ loadCompiler = defaultLoadCompiler } = {}) {
4358
4568
  if (view.editingId()) return view.insertText(text);
4359
4569
  const node = selectedId ? findNode(root, selectedId) : null;
4360
4570
  if (!node || !TEXT_TAGS.has(node.tag)) return false;
4571
+ view.settlePreview();
4361
4572
  commit(setContent(root, node.id, `${node.content || ""} ${escapeText(text)}`));
4362
4573
  return true;
4363
4574
  },
4575
+ previewText(text) {
4576
+ return view ? view.previewText(text) : false;
4577
+ },
4578
+ clearPreview() {
4579
+ view?.hidePreview();
4580
+ },
4364
4581
  insertLink(href, label) {
4365
4582
  if (!view || !root) return false;
4366
4583
  const safeHref = escapeText(href).replace(/"/g, "&quot;");
@@ -4456,7 +4673,7 @@ function createDefaultEmailEditor(options = {}) {
4456
4673
 
4457
4674
  // src/ui/EmailCanvas.jsx
4458
4675
  import { useEffect as useEffect5, useRef as useRef3 } from "react";
4459
- import { jsx as jsx9 } from "react/jsx-runtime";
4676
+ import { jsx as jsx10 } from "react/jsx-runtime";
4460
4677
  function EmailCanvas({ editor, document = null, onReady, onError, className }) {
4461
4678
  const ref = useRef3(null);
4462
4679
  const documentRef = useRef3(document);
@@ -4475,15 +4692,15 @@ function EmailCanvas({ editor, document = null, onReady, onError, className }) {
4475
4692
  editor.destroy();
4476
4693
  };
4477
4694
  }, [editor]);
4478
- return /* @__PURE__ */ jsx9("div", { ref, className: cn("eb-canvas eb:h-full eb:w-full", className), "data-testid": "eb-canvas" });
4695
+ return /* @__PURE__ */ jsx10("div", { ref, className: cn("eb-canvas eb:h-full eb:w-full", className), "data-testid": "eb-canvas" });
4479
4696
  }
4480
4697
 
4481
4698
  // src/ui/EmailBuilder.jsx
4482
- import { useCallback as useCallback3, useEffect as useEffect10, useMemo as useMemo5, useRef as useRef8, useState as useState13 } from "react";
4699
+ import { useCallback as useCallback3, useEffect as useEffect10, useMemo as useMemo5, useRef as useRef8, useState as useState14 } from "react";
4483
4700
  import { AlertTriangle, Loader2 as Loader25, Plus as Plus2, Save, SlidersHorizontal } from "lucide-react";
4484
4701
 
4485
4702
  // src/features/brand/useBrand.js
4486
- import { useCallback, useEffect as useEffect6, useState as useState9 } from "react";
4703
+ import { useCallback, useEffect as useEffect6, useState as useState10 } from "react";
4487
4704
  var entries = /* @__PURE__ */ new WeakMap();
4488
4705
  function brandOf(storage) {
4489
4706
  let entry = entries.get(storage);
@@ -4504,7 +4721,7 @@ function brandOf(storage) {
4504
4721
  return created;
4505
4722
  }
4506
4723
  function useBrand(storage) {
4507
- const [brand, setState] = useState9(() => brandOf(storage).value);
4724
+ const [brand, setState] = useState10(() => brandOf(storage).value);
4508
4725
  useEffect6(() => {
4509
4726
  const entry = brandOf(storage);
4510
4727
  setState(entry.value);
@@ -4633,7 +4850,7 @@ import {
4633
4850
  Type
4634
4851
  } from "lucide-react";
4635
4852
  import { useRef as useRef4 } from "react";
4636
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4853
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
4637
4854
  var DRAG_THRESHOLD = 5;
4638
4855
  var ICONS2 = {
4639
4856
  Heading,
@@ -4659,7 +4876,7 @@ function BlockTile({ block, disabled, onAdd, onDragStart }) {
4659
4876
  const t = useT();
4660
4877
  const Icon = ICONS2[block.icon] || Square;
4661
4878
  const press = useRef4(null);
4662
- return /* @__PURE__ */ jsx10(TooltipWrap, { label: block.description ? t(block.description) : block.description, children: /* @__PURE__ */ jsxs9(
4879
+ return /* @__PURE__ */ jsx11(TooltipWrap, { label: block.description ? t(block.description) : block.description, children: /* @__PURE__ */ jsxs10(
4663
4880
  "button",
4664
4881
  {
4665
4882
  type: "button",
@@ -4699,8 +4916,8 @@ function BlockTile({ block, disabled, onAdd, onDragStart }) {
4699
4916
  disabled && "eb:cursor-not-allowed eb:opacity-50"
4700
4917
  ),
4701
4918
  children: [
4702
- /* @__PURE__ */ jsx10(Icon, { className: "eb:h-5 eb:w-5 eb:text-foreground-lighter", "aria-hidden": "true" }),
4703
- /* @__PURE__ */ jsx10("span", { className: "eb:text-[12px] eb:text-foreground", children: t(block.label) })
4919
+ /* @__PURE__ */ jsx11(Icon, { className: "eb:h-5 eb:w-5 eb:text-foreground-lighter", "aria-hidden": "true" }),
4920
+ /* @__PURE__ */ jsx11("span", { className: "eb:text-[12px] eb:text-foreground", children: t(block.label) })
4704
4921
  ]
4705
4922
  }
4706
4923
  ) });
@@ -4708,12 +4925,12 @@ function BlockTile({ block, disabled, onAdd, onDragStart }) {
4708
4925
  function BlockSidebar({ editor, disabled = false, onAdded }) {
4709
4926
  const t = useT();
4710
4927
  const blocks = editor.listBlocks();
4711
- return /* @__PURE__ */ jsx10("aside", { "aria-label": t("Blocks"), className: "eb:min-h-0 eb:space-y-5 eb:overflow-y-auto eb:border-r eb:border-border eb:p-3", children: GROUPS2.map((group) => {
4928
+ return /* @__PURE__ */ jsx11("aside", { "aria-label": t("Blocks"), className: "eb:min-h-0 eb:space-y-5 eb:overflow-y-auto eb:border-r eb:border-border eb:p-3", children: GROUPS2.map((group) => {
4712
4929
  const items = blocks.filter((b) => b.kind === group.kind && b.mjml);
4713
4930
  if (!items.length) return null;
4714
- return /* @__PURE__ */ jsxs9("section", { className: "eb:space-y-2", children: [
4715
- /* @__PURE__ */ jsx10(SectionLabel, { children: t(group.title) }),
4716
- /* @__PURE__ */ jsx10("div", { className: "eb:grid eb:grid-cols-2 eb:gap-2", children: items.map((block) => /* @__PURE__ */ jsx10(
4931
+ return /* @__PURE__ */ jsxs10("section", { className: "eb:space-y-2", children: [
4932
+ /* @__PURE__ */ jsx11(SectionLabel, { children: t(group.title) }),
4933
+ /* @__PURE__ */ jsx11("div", { className: "eb:grid eb:grid-cols-2 eb:gap-2", children: items.map((block) => /* @__PURE__ */ jsx11(
4717
4934
  BlockTile,
4718
4935
  {
4719
4936
  block,
@@ -4731,15 +4948,15 @@ function BlockSidebar({ editor, disabled = false, onAdded }) {
4731
4948
 
4732
4949
  // src/ui/ElementActions.jsx
4733
4950
  import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Copy, Trash2 as Trash23 } from "lucide-react";
4734
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
4951
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4735
4952
  function ElementActions({ editor, selection }) {
4736
4953
  const t = useT();
4737
4954
  if (!selection || !selection.canRemove && !selection.canDuplicate) return null;
4738
4955
  const horizontal = selection.tagName === "mj-column";
4739
4956
  const [Prev, Next] = horizontal ? [ArrowLeft, ArrowRight] : [ArrowUp, ArrowDown];
4740
4957
  const [prevLabel, nextLabel] = horizontal ? [t("Move left"), t("Move right")] : [t("Move up"), t("Move down")];
4741
- return /* @__PURE__ */ jsxs10("div", { className: "eb:flex eb:items-center eb:gap-0.5 eb:border-b eb:border-border eb:px-3 eb:py-1.5", role: "toolbar", "aria-label": t("Element actions"), children: [
4742
- /* @__PURE__ */ jsx11(TooltipWrap, { label: prevLabel, children: /* @__PURE__ */ jsx11(
4958
+ return /* @__PURE__ */ jsxs11("div", { className: "eb:flex eb:items-center eb:gap-0.5 eb:border-b eb:border-border eb:px-3 eb:py-1.5", role: "toolbar", "aria-label": t("Element actions"), children: [
4959
+ /* @__PURE__ */ jsx12(TooltipWrap, { label: prevLabel, children: /* @__PURE__ */ jsx12(
4743
4960
  Button,
4744
4961
  {
4745
4962
  variant: "ghost",
@@ -4747,10 +4964,10 @@ function ElementActions({ editor, selection }) {
4747
4964
  "aria-label": prevLabel,
4748
4965
  disabled: !selection.canMoveUp,
4749
4966
  onClick: () => editor.move(-1, selection.id),
4750
- children: /* @__PURE__ */ jsx11(Prev, {})
4967
+ children: /* @__PURE__ */ jsx12(Prev, {})
4751
4968
  }
4752
4969
  ) }),
4753
- /* @__PURE__ */ jsx11(TooltipWrap, { label: nextLabel, children: /* @__PURE__ */ jsx11(
4970
+ /* @__PURE__ */ jsx12(TooltipWrap, { label: nextLabel, children: /* @__PURE__ */ jsx12(
4754
4971
  Button,
4755
4972
  {
4756
4973
  variant: "ghost",
@@ -4758,10 +4975,10 @@ function ElementActions({ editor, selection }) {
4758
4975
  "aria-label": nextLabel,
4759
4976
  disabled: !selection.canMoveDown,
4760
4977
  onClick: () => editor.move(1, selection.id),
4761
- children: /* @__PURE__ */ jsx11(Next, {})
4978
+ children: /* @__PURE__ */ jsx12(Next, {})
4762
4979
  }
4763
4980
  ) }),
4764
- /* @__PURE__ */ jsx11(TooltipWrap, { label: t("Duplicate"), children: /* @__PURE__ */ jsx11(
4981
+ /* @__PURE__ */ jsx12(TooltipWrap, { label: t("Duplicate"), children: /* @__PURE__ */ jsx12(
4765
4982
  Button,
4766
4983
  {
4767
4984
  variant: "ghost",
@@ -4769,10 +4986,10 @@ function ElementActions({ editor, selection }) {
4769
4986
  "aria-label": t("Duplicate"),
4770
4987
  disabled: !selection.canDuplicate,
4771
4988
  onClick: () => editor.duplicate(selection.id),
4772
- children: /* @__PURE__ */ jsx11(Copy, {})
4989
+ children: /* @__PURE__ */ jsx12(Copy, {})
4773
4990
  }
4774
4991
  ) }),
4775
- /* @__PURE__ */ jsx11(TooltipWrap, { label: t("Delete (Del)"), children: /* @__PURE__ */ jsx11(
4992
+ /* @__PURE__ */ jsx12(TooltipWrap, { label: t("Delete (Del)"), children: /* @__PURE__ */ jsx12(
4776
4993
  Button,
4777
4994
  {
4778
4995
  variant: "ghost",
@@ -4781,7 +4998,7 @@ function ElementActions({ editor, selection }) {
4781
4998
  disabled: !selection.canRemove,
4782
4999
  onClick: () => editor.remove(selection.id),
4783
5000
  className: "eb:ml-auto eb:text-foreground-muted eb:hover:bg-destructive/10 eb:hover:text-destructive",
4784
- children: /* @__PURE__ */ jsx11(Trash23, {})
5001
+ children: /* @__PURE__ */ jsx12(Trash23, {})
4785
5002
  }
4786
5003
  ) })
4787
5004
  ] });
@@ -4792,9 +5009,9 @@ import { useMemo as useMemo4, useRef as useRef6 } from "react";
4792
5009
  import { X as X2 } from "lucide-react";
4793
5010
 
4794
5011
  // src/ui/fields.jsx
4795
- import { useEffect as useEffect7, useRef as useRef5, useState as useState10 } from "react";
5012
+ import { useEffect as useEffect7, useRef as useRef5, useState as useState11 } from "react";
4796
5013
  import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Loader2 as Loader24, Upload as Upload2 } from "lucide-react";
4797
- import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
5014
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
4798
5015
  var COLOR_DEBOUNCE_MS = 250;
4799
5016
  function emailColor(value) {
4800
5017
  const v = String(value || "").trim();
@@ -4803,7 +5020,7 @@ function emailColor(value) {
4803
5020
  return null;
4804
5021
  }
4805
5022
  function useDraft(value, toDraft) {
4806
- const [draft, setDraft] = useState10(() => toDraft(value));
5023
+ const [draft, setDraft] = useState11(() => toDraft(value));
4807
5024
  const ref = useRef5(null);
4808
5025
  const convert = useRef5(toDraft);
4809
5026
  convert.current = toDraft;
@@ -4816,10 +5033,10 @@ function useDraft(value, toDraft) {
4816
5033
  }
4817
5034
  var asText = (v) => v || "";
4818
5035
  function FieldLabel({ id, children }) {
4819
- return /* @__PURE__ */ jsx12(Label, { htmlFor: id, className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children });
5036
+ return /* @__PURE__ */ jsx13(Label, { htmlFor: id, className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children });
4820
5037
  }
4821
5038
  function FieldError({ children }) {
4822
- return children ? /* @__PURE__ */ jsx12("p", { className: "eb:text-[12px] eb:text-destructive", children }) : null;
5039
+ return children ? /* @__PURE__ */ jsx13("p", { className: "eb:text-[12px] eb:text-destructive", children }) : null;
4823
5040
  }
4824
5041
  function TextField({ id, property, value, fallback = "", onCommit, addon = null, error = null }) {
4825
5042
  const t = useT();
@@ -4850,13 +5067,13 @@ function TextField({ id, property, value, fallback = "", onCommit, addon = null,
4850
5067
  onKeyDown: keyDown,
4851
5068
  "aria-invalid": invalid || void 0
4852
5069
  };
4853
- return /* @__PURE__ */ jsxs11("div", { ref, className: "eb:space-y-1.5", children: [
4854
- /* @__PURE__ */ jsx12(FieldLabel, { id, children: t(property.label) }),
4855
- property.type === "textarea" ? /* @__PURE__ */ jsx12(Textarea, { rows: 3, ...shared }) : addon ? /* @__PURE__ */ jsxs11("div", { className: "eb:flex eb:gap-2", children: [
4856
- /* @__PURE__ */ jsx12(Input, { ...shared, className: "eb:min-w-0 eb:flex-1" }),
5070
+ return /* @__PURE__ */ jsxs12("div", { ref, className: "eb:space-y-1.5", children: [
5071
+ /* @__PURE__ */ jsx13(FieldLabel, { id, children: t(property.label) }),
5072
+ property.type === "textarea" ? /* @__PURE__ */ jsx13(Textarea, { rows: 3, ...shared }) : addon ? /* @__PURE__ */ jsxs12("div", { className: "eb:flex eb:gap-2", children: [
5073
+ /* @__PURE__ */ jsx13(Input, { ...shared, className: "eb:min-w-0 eb:flex-1" }),
4857
5074
  addon
4858
- ] }) : /* @__PURE__ */ jsx12(Input, { ...shared }),
4859
- /* @__PURE__ */ jsx12(FieldError, { children: invalid ? kind === "image" ? t("An https:// address is required") : t("Address not allowed") : error })
5075
+ ] }) : /* @__PURE__ */ jsx13(Input, { ...shared }),
5076
+ /* @__PURE__ */ jsx13(FieldError, { children: invalid ? kind === "image" ? t("An https:// address is required") : t("Address not allowed") : error })
4860
5077
  ] });
4861
5078
  }
4862
5079
  function ImageField({ id, property, value, onCommit, services }) {
@@ -4865,8 +5082,8 @@ function ImageField({ id, property, value, onCommit, services }) {
4865
5082
  /** @type {HTMLInputElement|null} */
4866
5083
  null
4867
5084
  );
4868
- const [busy, setBusy] = useState10(false);
4869
- const [error, setError] = useState10(
5085
+ const [busy, setBusy] = useState11(false);
5086
+ const [error, setError] = useState11(
4870
5087
  /** @type {string|null} */
4871
5088
  null
4872
5089
  );
@@ -4888,8 +5105,8 @@ function ImageField({ id, property, value, onCommit, services }) {
4888
5105
  setBusy(false);
4889
5106
  }
4890
5107
  };
4891
- const addon = upload ? /* @__PURE__ */ jsxs11(Fragment5, { children: [
4892
- /* @__PURE__ */ jsx12(
5108
+ const addon = upload ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
5109
+ /* @__PURE__ */ jsx13(
4893
5110
  "input",
4894
5111
  {
4895
5112
  ref: inputRef,
@@ -4902,7 +5119,7 @@ function ImageField({ id, property, value, onCommit, services }) {
4902
5119
  onChange: pick
4903
5120
  }
4904
5121
  ),
4905
- /* @__PURE__ */ jsxs11(
5122
+ /* @__PURE__ */ jsxs12(
4906
5123
  Button,
4907
5124
  {
4908
5125
  type: "button",
@@ -4911,13 +5128,13 @@ function ImageField({ id, property, value, onCommit, services }) {
4911
5128
  disabled: busy,
4912
5129
  onClick: () => inputRef.current?.click(),
4913
5130
  children: [
4914
- busy ? /* @__PURE__ */ jsx12(Loader24, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx12(Upload2, {}),
5131
+ busy ? /* @__PURE__ */ jsx13(Loader24, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx13(Upload2, {}),
4915
5132
  t("Upload")
4916
5133
  ]
4917
5134
  }
4918
5135
  )
4919
5136
  ] }) : null;
4920
- return /* @__PURE__ */ jsx12(TextField, { id, property, value, onCommit, addon, error });
5137
+ return /* @__PURE__ */ jsx13(TextField, { id, property, value, onCommit, addon, error });
4921
5138
  }
4922
5139
  var asNumber = (v) => {
4923
5140
  const n = Number.parseFloat(String(v || ""));
@@ -4939,10 +5156,10 @@ function SizeField({ id, property, value, fallback = "", onCommit }) {
4939
5156
  const next = `${n}${property.unit || "px"}`;
4940
5157
  if (next !== value) onCommit(next);
4941
5158
  };
4942
- return /* @__PURE__ */ jsxs11("div", { ref, className: "eb:space-y-1.5", children: [
4943
- /* @__PURE__ */ jsx12(FieldLabel, { id, children: t(property.label) }),
4944
- /* @__PURE__ */ jsxs11("div", { className: "eb:relative", children: [
4945
- /* @__PURE__ */ jsx12(
5159
+ return /* @__PURE__ */ jsxs12("div", { ref, className: "eb:space-y-1.5", children: [
5160
+ /* @__PURE__ */ jsx13(FieldLabel, { id, children: t(property.label) }),
5161
+ /* @__PURE__ */ jsxs12("div", { className: "eb:relative", children: [
5162
+ /* @__PURE__ */ jsx13(
4946
5163
  Input,
4947
5164
  {
4948
5165
  id,
@@ -4961,7 +5178,7 @@ function SizeField({ id, property, value, fallback = "", onCommit }) {
4961
5178
  className: "eb:pr-9 eb:tabular-nums"
4962
5179
  }
4963
5180
  ),
4964
- /* @__PURE__ */ jsx12("span", { className: "eb:pointer-events-none eb:absolute eb:right-3 eb:top-1/2 eb:-translate-y-1/2 eb:text-[12px] eb:text-foreground-muted", children: property.unit || "px" })
5181
+ /* @__PURE__ */ jsx13("span", { className: "eb:pointer-events-none eb:absolute eb:right-3 eb:top-1/2 eb:-translate-y-1/2 eb:text-[12px] eb:text-foreground-muted", children: property.unit || "px" })
4965
5182
  ] })
4966
5183
  ] });
4967
5184
  }
@@ -4984,9 +5201,9 @@ function ColorField({ id, property, value, fallback = "", onCommit }) {
4984
5201
  }
4985
5202
  }, COLOR_DEBOUNCE_MS);
4986
5203
  };
4987
- return /* @__PURE__ */ jsxs11("div", { ref, className: "eb:space-y-1.5", children: [
4988
- /* @__PURE__ */ jsx12(FieldLabel, { id, children: t(property.label) }),
4989
- /* @__PURE__ */ jsx12(ColorPicker, { id, value: draft, placeholder: emailColor(fallback) || "", onChange: change })
5204
+ return /* @__PURE__ */ jsxs12("div", { ref, className: "eb:space-y-1.5", children: [
5205
+ /* @__PURE__ */ jsx13(FieldLabel, { id, children: t(property.label) }),
5206
+ /* @__PURE__ */ jsx13(ColorPicker, { id, value: draft, placeholder: emailColor(fallback) || "", onChange: change })
4990
5207
  ] });
4991
5208
  }
4992
5209
  var DEFAULT_OPTION = "__default__";
@@ -4996,9 +5213,9 @@ function SelectField({ id, property, value, fallback = "", onCommit }) {
4996
5213
  const words = (v) => String(v || "").trim().split(/\s+/).sort().join(" ");
4997
5214
  const fallbackOption = (property.options || []).find((o) => o.value && words(o.value) === words(fallback));
4998
5215
  const defaultLabel = fallbackOption ? t("Default ({value})", { value: t(fallbackOption.label) }) : t("Default");
4999
- return /* @__PURE__ */ jsxs11("div", { className: "eb:space-y-1.5", children: [
5000
- /* @__PURE__ */ jsx12(FieldLabel, { id, children: t(property.label) }),
5001
- /* @__PURE__ */ jsxs11(
5216
+ return /* @__PURE__ */ jsxs12("div", { className: "eb:space-y-1.5", children: [
5217
+ /* @__PURE__ */ jsx13(FieldLabel, { id, children: t(property.label) }),
5218
+ /* @__PURE__ */ jsxs12(
5002
5219
  Select,
5003
5220
  {
5004
5221
  value: current,
@@ -5007,10 +5224,10 @@ function SelectField({ id, property, value, fallback = "", onCommit }) {
5007
5224
  if (v !== (value || "")) onCommit(v || null);
5008
5225
  },
5009
5226
  children: [
5010
- /* @__PURE__ */ jsx12(SelectTrigger, { id, className: "eb:w-full", children: /* @__PURE__ */ jsx12(SelectValue, { placeholder: defaultLabel }) }),
5011
- /* @__PURE__ */ jsxs11(SelectContent, { children: [
5012
- !(property.options || []).some((o) => !o.value) && /* @__PURE__ */ jsx12(SelectItem, { value: DEFAULT_OPTION, children: defaultLabel }),
5013
- (property.options || []).map((o) => /* @__PURE__ */ jsx12(SelectItem, { value: o.value || DEFAULT_OPTION, children: t(o.label) }, o.value || DEFAULT_OPTION))
5227
+ /* @__PURE__ */ jsx13(SelectTrigger, { id, className: "eb:w-full", children: /* @__PURE__ */ jsx13(SelectValue, { placeholder: defaultLabel }) }),
5228
+ /* @__PURE__ */ jsxs12(SelectContent, { children: [
5229
+ !(property.options || []).some((o) => !o.value) && /* @__PURE__ */ jsx13(SelectItem, { value: DEFAULT_OPTION, children: defaultLabel }),
5230
+ (property.options || []).map((o) => /* @__PURE__ */ jsx13(SelectItem, { value: o.value || DEFAULT_OPTION, children: t(o.label) }, o.value || DEFAULT_OPTION))
5014
5231
  ] })
5015
5232
  ]
5016
5233
  }
@@ -5020,9 +5237,9 @@ function SelectField({ id, property, value, fallback = "", onCommit }) {
5020
5237
  var ALIGN_ICONS = { left: AlignLeft, center: AlignCenter, right: AlignRight, justify: AlignJustify };
5021
5238
  function AlignField({ id, property, value, fallback = "", onCommit }) {
5022
5239
  const t = useT();
5023
- return /* @__PURE__ */ jsxs11("div", { className: "eb:space-y-1.5", children: [
5024
- /* @__PURE__ */ jsx12(FieldLabel, { id, children: t(property.label) }),
5025
- /* @__PURE__ */ jsx12(
5240
+ return /* @__PURE__ */ jsxs12("div", { className: "eb:space-y-1.5", children: [
5241
+ /* @__PURE__ */ jsx13(FieldLabel, { id, children: t(property.label) }),
5242
+ /* @__PURE__ */ jsx13(
5026
5243
  "div",
5027
5244
  {
5028
5245
  id,
@@ -5032,7 +5249,7 @@ function AlignField({ id, property, value, fallback = "", onCommit }) {
5032
5249
  children: (property.options || []).map((option) => {
5033
5250
  const Icon = ALIGN_ICONS[option.value] || AlignLeft;
5034
5251
  const active = (value || fallback) === option.value;
5035
- return /* @__PURE__ */ jsx12(TooltipWrap, { label: t(option.label), children: /* @__PURE__ */ jsx12(
5252
+ return /* @__PURE__ */ jsx13(TooltipWrap, { label: t(option.label), children: /* @__PURE__ */ jsx13(
5036
5253
  "button",
5037
5254
  {
5038
5255
  type: "button",
@@ -5044,7 +5261,7 @@ function AlignField({ id, property, value, fallback = "", onCommit }) {
5044
5261
  "eb:inline-flex eb:h-7 eb:w-7 eb:items-center eb:justify-center eb:rounded-lg2 eb:transition-colors",
5045
5262
  active ? "eb:bg-primary eb:text-primary-foreground" : "eb:text-foreground-lighter eb:hover:bg-surface-200 eb:hover:text-foreground"
5046
5263
  ),
5047
- children: /* @__PURE__ */ jsx12(Icon, { className: "eb:h-3.5 eb:w-3.5" })
5264
+ children: /* @__PURE__ */ jsx13(Icon, { className: "eb:h-3.5 eb:w-3.5" })
5048
5265
  }
5049
5266
  ) }, option.value);
5050
5267
  })
@@ -5073,10 +5290,10 @@ function SpacingField({ id, property, value, fallback = "", onCommit }) {
5073
5290
  );
5074
5291
  if (next !== (value || "")) onCommit(next);
5075
5292
  };
5076
- return /* @__PURE__ */ jsxs11("fieldset", { ref, className: "eb:space-y-1.5", children: [
5077
- /* @__PURE__ */ jsx12("legend", { className: "eb:mb-1.5 eb:text-[12px] eb:text-foreground-lighter", children: t(property.label) }),
5078
- /* @__PURE__ */ jsx12("div", { className: "eb:grid eb:grid-cols-4 eb:gap-1.5", children: SIDE_LABELS.map(([side, label], i) => /* @__PURE__ */ jsxs11("div", { className: "eb:space-y-1", children: [
5079
- /* @__PURE__ */ jsx12(
5293
+ return /* @__PURE__ */ jsxs12("fieldset", { ref, className: "eb:space-y-1.5", children: [
5294
+ /* @__PURE__ */ jsx13("legend", { className: "eb:mb-1.5 eb:text-[12px] eb:text-foreground-lighter", children: t(property.label) }),
5295
+ /* @__PURE__ */ jsx13("div", { className: "eb:grid eb:grid-cols-4 eb:gap-1.5", children: SIDE_LABELS.map(([side, label], i) => /* @__PURE__ */ jsxs12("div", { className: "eb:space-y-1", children: [
5296
+ /* @__PURE__ */ jsx13(
5080
5297
  Input,
5081
5298
  {
5082
5299
  id: `${id}-${side}`,
@@ -5095,7 +5312,7 @@ function SpacingField({ id, property, value, fallback = "", onCommit }) {
5095
5312
  className: "eb:px-2 eb:text-center eb:tabular-nums"
5096
5313
  }
5097
5314
  ),
5098
- /* @__PURE__ */ jsx12("span", { className: "eb:block eb:text-center eb:text-[10.5px] eb:text-foreground-muted", children: t(label) })
5315
+ /* @__PURE__ */ jsx13("span", { className: "eb:block eb:text-center eb:text-[10.5px] eb:text-foreground-muted", children: t(label) })
5099
5316
  ] }, side)) })
5100
5317
  ] });
5101
5318
  }
@@ -5113,15 +5330,15 @@ var FIELDS = {
5113
5330
  function PropertyField({ property, value, fallback = "", onCommit, idPrefix = "eb", services = null }) {
5114
5331
  const Field = FIELDS[property.type] || TextField;
5115
5332
  const id = `${idPrefix}-${property.key}`;
5116
- return /* @__PURE__ */ jsx12(Field, { id, property, value, fallback, onCommit, services });
5333
+ return /* @__PURE__ */ jsx13(Field, { id, property, value, fallback, onCommit, services });
5117
5334
  }
5118
5335
 
5119
5336
  // src/features/variables/VariableInsertButton.jsx
5120
- import { useState as useState11 } from "react";
5121
- import { Braces as Braces3 } from "lucide-react";
5122
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
5337
+ import { useState as useState12 } from "react";
5338
+ import { Braces as Braces4 } from "lucide-react";
5339
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
5123
5340
  function VariableInsertButton({ variables, inputRef, value, onChange, label }) {
5124
- const [open, setOpen] = useState11(false);
5341
+ const [open, setOpen] = useState12(false);
5125
5342
  const insert = (variable) => {
5126
5343
  const input = inputRef.current;
5127
5344
  const text = String(value || "");
@@ -5137,14 +5354,14 @@ function VariableInsertButton({ variables, inputRef, value, onChange, label }) {
5137
5354
  input.setSelectionRange(start + token.length, start + token.length);
5138
5355
  });
5139
5356
  };
5140
- return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: setOpen, children: [
5141
- /* @__PURE__ */ jsx13(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx13(Button, { variant: "ghost", size: "icon-xs", "aria-label": label, title: label, children: /* @__PURE__ */ jsx13(Braces3, {}) }) }),
5142
- /* @__PURE__ */ jsx13(PopoverContent, { align: "end", className: "eb:w-72 eb:p-0", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx13(VariablePicker, { variables, onPick: insert, filter: (v) => v.kind !== "link" }) })
5357
+ return /* @__PURE__ */ jsxs13(Popover, { open, onOpenChange: setOpen, children: [
5358
+ /* @__PURE__ */ jsx14(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx14(Button, { variant: "ghost", size: "icon-xs", "aria-label": label, title: label, children: /* @__PURE__ */ jsx14(Braces4, {}) }) }),
5359
+ /* @__PURE__ */ jsx14(PopoverContent, { align: "end", className: "eb:w-72 eb:p-0", onCloseAutoFocus: (e) => e.preventDefault(), children: /* @__PURE__ */ jsx14(VariablePicker, { variables, onPick: insert, filter: (v) => v.kind !== "link" }) })
5143
5360
  ] });
5144
5361
  }
5145
5362
 
5146
5363
  // src/ui/PropertiesPanel.jsx
5147
- import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
5364
+ import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
5148
5365
  var GROUP_ORDER = Object.values(GROUPS);
5149
5366
  function grouped(properties) {
5150
5367
  const map = /* @__PURE__ */ new Map();
@@ -5158,9 +5375,9 @@ function grouped(properties) {
5158
5375
  }
5159
5376
  function PropertyGroups({ editor, element, block, idPrefix, services = null }) {
5160
5377
  const t = useT();
5161
- return grouped(block?.properties || []).map(([group, properties]) => /* @__PURE__ */ jsxs13("section", { className: "eb:space-y-3", children: [
5162
- /* @__PURE__ */ jsx14(SectionLabel, { children: t(group) }),
5163
- properties.map((property) => /* @__PURE__ */ jsx14(
5378
+ return grouped(block?.properties || []).map(([group, properties]) => /* @__PURE__ */ jsxs14("section", { className: "eb:space-y-3", children: [
5379
+ /* @__PURE__ */ jsx15(SectionLabel, { children: t(group) }),
5380
+ properties.map((property) => /* @__PURE__ */ jsx15(
5164
5381
  PropertyField,
5165
5382
  {
5166
5383
  idPrefix,
@@ -5178,9 +5395,9 @@ var LABEL_PROPERTY = { key: "content", label: "Label", type: "text", group: GROU
5178
5395
  var REWRITABLE_TAGS = /* @__PURE__ */ new Set(["mj-text", "mj-button"]);
5179
5396
  function PanelHeader({ title, onClose, closeLabel = "Deselect" }) {
5180
5397
  const t = useT();
5181
- return /* @__PURE__ */ jsxs13("header", { className: "eb:sticky eb:top-0 eb:z-10 eb:flex eb:h-11 eb:shrink-0 eb:items-center eb:gap-2 eb:border-b eb:border-border eb:bg-background eb:px-4", children: [
5182
- /* @__PURE__ */ jsx14("h3", { className: "eb:min-w-0 eb:flex-1 eb:text-[13px] eb:font-medium eb:text-foreground", children: /* @__PURE__ */ jsx14(TruncatedText, { children: title }) }),
5183
- onClose && /* @__PURE__ */ jsx14(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onClose, "aria-label": t(closeLabel), children: /* @__PURE__ */ jsx14(X2, {}) })
5398
+ return /* @__PURE__ */ jsxs14("header", { className: "eb:sticky eb:top-0 eb:z-10 eb:flex eb:h-11 eb:shrink-0 eb:items-center eb:gap-2 eb:border-b eb:border-border eb:bg-background eb:px-4", children: [
5399
+ /* @__PURE__ */ jsx15("h3", { className: "eb:min-w-0 eb:flex-1 eb:text-[13px] eb:font-medium eb:text-foreground", children: /* @__PURE__ */ jsx15(TruncatedText, { children: title }) }),
5400
+ onClose && /* @__PURE__ */ jsx15(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onClose, "aria-label": t(closeLabel), children: /* @__PURE__ */ jsx15(X2, {}) })
5184
5401
  ] });
5185
5402
  }
5186
5403
  function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, variables = null, ai = null, onDismiss = null }) {
@@ -5191,18 +5408,18 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
5191
5408
  const root = useMemo4(() => rootId ? editor.getElement(rootId) : null, [editor, rootId, changes]);
5192
5409
  const bodyBlock2 = root?.blockId ? editor.getBlock(root.blockId) : null;
5193
5410
  const field = (key) => (e) => onMetaChange({ ...meta, [key]: e.target.value });
5194
- return /* @__PURE__ */ jsxs13(Fragment6, { children: [
5195
- /* @__PURE__ */ jsx14(PanelHeader, { title: t("Email"), onClose: onDismiss, closeLabel: "Close" }),
5196
- /* @__PURE__ */ jsxs13("div", { className: "eb:space-y-5 eb:p-4", children: [
5197
- /* @__PURE__ */ jsxs13("section", { className: "eb:space-y-3", children: [
5198
- /* @__PURE__ */ jsx14(SectionLabel, { children: t("Template") }),
5199
- /* @__PURE__ */ jsxs13("div", { className: "eb:space-y-1.5", children: [
5200
- /* @__PURE__ */ jsxs13(Label, { htmlFor: "eb-meta-name", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: [
5411
+ return /* @__PURE__ */ jsxs14(Fragment6, { children: [
5412
+ /* @__PURE__ */ jsx15(PanelHeader, { title: t("Email"), onClose: onDismiss, closeLabel: "Close" }),
5413
+ /* @__PURE__ */ jsxs14("div", { className: "eb:space-y-5 eb:p-4", children: [
5414
+ /* @__PURE__ */ jsxs14("section", { className: "eb:space-y-3", children: [
5415
+ /* @__PURE__ */ jsx15(SectionLabel, { children: t("Template") }),
5416
+ /* @__PURE__ */ jsxs14("div", { className: "eb:space-y-1.5", children: [
5417
+ /* @__PURE__ */ jsxs14(Label, { htmlFor: "eb-meta-name", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: [
5201
5418
  t("Name"),
5202
5419
  " ",
5203
- /* @__PURE__ */ jsx14("span", { className: "eb:text-destructive", children: "*" })
5420
+ /* @__PURE__ */ jsx15("span", { className: "eb:text-destructive", children: "*" })
5204
5421
  ] }),
5205
- /* @__PURE__ */ jsx14(
5422
+ /* @__PURE__ */ jsx15(
5206
5423
  Input,
5207
5424
  {
5208
5425
  id: "eb-meta-name",
@@ -5212,12 +5429,12 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
5212
5429
  "aria-invalid": showErrors && !meta.name.trim() || void 0
5213
5430
  }
5214
5431
  ),
5215
- showErrors && !meta.name.trim() && /* @__PURE__ */ jsx14("p", { className: "eb:text-[12px] eb:text-destructive", children: t("Name is required") })
5432
+ showErrors && !meta.name.trim() && /* @__PURE__ */ jsx15("p", { className: "eb:text-[12px] eb:text-destructive", children: t("Name is required") })
5216
5433
  ] }),
5217
- /* @__PURE__ */ jsxs13("div", { className: "eb:space-y-1.5", children: [
5218
- /* @__PURE__ */ jsxs13("div", { className: "eb:flex eb:h-6 eb:items-center eb:justify-between", children: [
5219
- /* @__PURE__ */ jsx14(Label, { htmlFor: "eb-meta-subject", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: t("Subject") }),
5220
- variables && /* @__PURE__ */ jsx14(
5434
+ /* @__PURE__ */ jsxs14("div", { className: "eb:space-y-1.5", children: [
5435
+ /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:h-6 eb:items-center eb:justify-between", children: [
5436
+ /* @__PURE__ */ jsx15(Label, { htmlFor: "eb-meta-subject", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: t("Subject") }),
5437
+ variables && /* @__PURE__ */ jsx15(
5221
5438
  VariableInsertButton,
5222
5439
  {
5223
5440
  variables,
@@ -5228,8 +5445,8 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
5228
5445
  }
5229
5446
  )
5230
5447
  ] }),
5231
- /* @__PURE__ */ jsx14(Input, { ref: subjectRef, id: "eb-meta-subject", maxLength: 250, value: meta.subject, onChange: field("subject") }),
5232
- ai?.suggestSubjects && /* @__PURE__ */ jsx14(
5448
+ /* @__PURE__ */ jsx15(Input, { ref: subjectRef, id: "eb-meta-subject", maxLength: 250, value: meta.subject, onChange: field("subject") }),
5449
+ ai?.suggestSubjects && /* @__PURE__ */ jsx15(
5233
5450
  ai.components.Subjects,
5234
5451
  {
5235
5452
  editor,
@@ -5240,10 +5457,10 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
5240
5457
  }
5241
5458
  )
5242
5459
  ] }),
5243
- /* @__PURE__ */ jsxs13("div", { className: "eb:space-y-1.5", children: [
5244
- /* @__PURE__ */ jsxs13("div", { className: "eb:flex eb:h-6 eb:items-center eb:justify-between", children: [
5245
- /* @__PURE__ */ jsx14(Label, { htmlFor: "eb-meta-preheader", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: t("Preview text") }),
5246
- variables && /* @__PURE__ */ jsx14(
5460
+ /* @__PURE__ */ jsxs14("div", { className: "eb:space-y-1.5", children: [
5461
+ /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:h-6 eb:items-center eb:justify-between", children: [
5462
+ /* @__PURE__ */ jsx15(Label, { htmlFor: "eb-meta-preheader", className: "eb:text-[12px] eb:font-normal eb:text-foreground-lighter", children: t("Preview text") }),
5463
+ variables && /* @__PURE__ */ jsx15(
5247
5464
  VariableInsertButton,
5248
5465
  {
5249
5466
  variables,
@@ -5254,7 +5471,7 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
5254
5471
  }
5255
5472
  )
5256
5473
  ] }),
5257
- /* @__PURE__ */ jsx14(
5474
+ /* @__PURE__ */ jsx15(
5258
5475
  Textarea,
5259
5476
  {
5260
5477
  ref: preheaderRef,
@@ -5268,7 +5485,7 @@ function TemplateSettings({ editor, meta, onMetaChange, changes, showErrors, var
5268
5485
  )
5269
5486
  ] })
5270
5487
  ] }),
5271
- root && bodyBlock2 && /* @__PURE__ */ jsx14(PropertyGroups, { editor, element: root, block: bodyBlock2, idPrefix: "eb-page" })
5488
+ root && bodyBlock2 && /* @__PURE__ */ jsx15(PropertyGroups, { editor, element: root, block: bodyBlock2, idPrefix: "eb-page" })
5272
5489
  ] })
5273
5490
  ] });
5274
5491
  }
@@ -5287,7 +5504,7 @@ function PropertiesPanel({
5287
5504
  }) {
5288
5505
  const t = useT();
5289
5506
  const block = selection?.blockId ? editor.getBlock(selection.blockId) : null;
5290
- return /* @__PURE__ */ jsx14("aside", { "aria-label": t("Properties"), className: "eb:min-h-0 eb:overflow-y-auto eb:border-l eb:border-border", "data-testid": "eb-properties", children: !selection ? /* @__PURE__ */ jsx14(
5507
+ return /* @__PURE__ */ jsx15("aside", { "aria-label": t("Properties"), className: "eb:min-h-0 eb:overflow-y-auto eb:border-l eb:border-border", "data-testid": "eb-properties", children: !selection ? /* @__PURE__ */ jsx15(
5291
5508
  TemplateSettings,
5292
5509
  {
5293
5510
  editor,
@@ -5299,8 +5516,8 @@ function PropertiesPanel({
5299
5516
  ai,
5300
5517
  onDismiss
5301
5518
  }
5302
- ) : /* @__PURE__ */ jsxs13(Fragment6, { children: [
5303
- /* @__PURE__ */ jsx14(
5519
+ ) : /* @__PURE__ */ jsxs14(Fragment6, { children: [
5520
+ /* @__PURE__ */ jsx15(
5304
5521
  PanelHeader,
5305
5522
  {
5306
5523
  title: t(selection.label),
@@ -5309,12 +5526,12 @@ function PropertiesPanel({
5309
5526
  }
5310
5527
  ),
5311
5528
  actions,
5312
- selection.multiple ? /* @__PURE__ */ jsx14("p", { className: "eb:p-4 eb:text-[13px] eb:leading-relaxed eb:text-foreground-lighter", "data-testid": "eb-select-all", children: t("{count} sections selected. Press Delete to remove them (Ctrl+Z brings them back).", {
5529
+ selection.multiple ? /* @__PURE__ */ jsx15("p", { className: "eb:p-4 eb:text-[13px] eb:leading-relaxed eb:text-foreground-lighter", "data-testid": "eb-select-all", children: t("{count} sections selected. Press Delete to remove them (Ctrl+Z brings them back).", {
5313
5530
  count: selection.multiple
5314
- }) }) : /* @__PURE__ */ jsxs13("div", { className: "eb:space-y-5 eb:p-4", children: [
5315
- selection.content !== null && selection.content !== void 0 && /* @__PURE__ */ jsxs13("section", { className: "eb:space-y-3", children: [
5316
- /* @__PURE__ */ jsx14(SectionLabel, { children: t(GROUPS.content) }),
5317
- /* @__PURE__ */ jsx14(
5531
+ }) }) : /* @__PURE__ */ jsxs14("div", { className: "eb:space-y-5 eb:p-4", children: [
5532
+ selection.content !== null && selection.content !== void 0 && /* @__PURE__ */ jsxs14("section", { className: "eb:space-y-3", children: [
5533
+ /* @__PURE__ */ jsx15(SectionLabel, { children: t(GROUPS.content) }),
5534
+ /* @__PURE__ */ jsx15(
5318
5535
  PropertyField,
5319
5536
  {
5320
5537
  idPrefix: "eb-prop",
@@ -5325,16 +5542,16 @@ function PropertiesPanel({
5325
5542
  `${selection.id}:content`
5326
5543
  )
5327
5544
  ] }),
5328
- selection.tagName === "mj-text" && /* @__PURE__ */ jsx14("p", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("Double-click the text to edit it.") }),
5329
- ai?.rewrite && REWRITABLE_TAGS.has(selection.tagName) && /* @__PURE__ */ jsx14(ai.components.Rewrite, { editor, selection, rewrite: ai.rewrite }, selection.id),
5330
- /* @__PURE__ */ jsx14(PropertyGroups, { editor, element: selection, block, idPrefix: "eb-prop", services })
5545
+ selection.tagName === "mj-text" && /* @__PURE__ */ jsx15("p", { className: "eb:text-[12px] eb:text-foreground-muted", children: t("Double-click the text to edit it.") }),
5546
+ ai?.rewrite && REWRITABLE_TAGS.has(selection.tagName) && /* @__PURE__ */ jsx15(ai.components.Rewrite, { editor, selection, rewrite: ai.rewrite }, selection.id),
5547
+ /* @__PURE__ */ jsx15(PropertyGroups, { editor, element: selection, block, idPrefix: "eb-prop", services })
5331
5548
  ] })
5332
5549
  ] }) });
5333
5550
  }
5334
5551
 
5335
5552
  // src/ui/Toolbar.jsx
5336
5553
  import { ArrowLeft as ArrowLeft2, Monitor, Redo2, Smartphone, Undo2 } from "lucide-react";
5337
- import { Fragment as Fragment7, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
5554
+ import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
5338
5555
  function Toolbar({
5339
5556
  title,
5340
5557
  status,
@@ -5351,8 +5568,8 @@ function Toolbar({
5351
5568
  compact = false
5352
5569
  }) {
5353
5570
  const t = useT();
5354
- const identity2 = /* @__PURE__ */ jsxs14(Fragment7, { children: [
5355
- /* @__PURE__ */ jsxs14(
5571
+ const identity2 = /* @__PURE__ */ jsxs15(Fragment7, { children: [
5572
+ /* @__PURE__ */ jsxs15(
5356
5573
  Button,
5357
5574
  {
5358
5575
  variant: "ghost",
@@ -5360,50 +5577,50 @@ function Toolbar({
5360
5577
  onClick: onBack,
5361
5578
  "aria-label": compact ? t("Back") : void 0,
5362
5579
  children: [
5363
- /* @__PURE__ */ jsx15(ArrowLeft2, {}),
5580
+ /* @__PURE__ */ jsx16(ArrowLeft2, {}),
5364
5581
  !compact && t("Back")
5365
5582
  ]
5366
5583
  }
5367
5584
  ),
5368
- /* @__PURE__ */ jsx15(Separator, { orientation: "vertical", className: "eb:h-5" }),
5369
- /* @__PURE__ */ jsxs14("div", { className: cn("eb:min-w-0 eb:flex-1", !compact && "eb:max-w-[320px]"), children: [
5370
- /* @__PURE__ */ jsx15("p", { className: "eb:text-[13px] eb:font-medium eb:text-foreground", children: /* @__PURE__ */ jsx15(TruncatedText, { children: title || t("New template") }) }),
5371
- /* @__PURE__ */ jsx15("p", { className: "eb:text-[11px] eb:text-foreground-muted", role: "status", "aria-live": "polite", children: status })
5585
+ /* @__PURE__ */ jsx16(Separator, { orientation: "vertical", className: "eb:h-5" }),
5586
+ /* @__PURE__ */ jsxs15("div", { className: cn("eb:min-w-0 eb:flex-1", !compact && "eb:max-w-[320px]"), children: [
5587
+ /* @__PURE__ */ jsx16("p", { className: "eb:text-[13px] eb:font-medium eb:text-foreground", children: /* @__PURE__ */ jsx16(TruncatedText, { children: title || t("New template") }) }),
5588
+ /* @__PURE__ */ jsx16("p", { className: "eb:text-[11px] eb:text-foreground-muted", role: "status", "aria-live": "polite", children: status })
5372
5589
  ] })
5373
5590
  ] });
5374
- const editing = /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2", children: [
5375
- /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:items-center eb:gap-0.5", children: [
5376
- /* @__PURE__ */ jsx15(TooltipWrap, { label: t("Undo (Ctrl+Z)"), children: /* @__PURE__ */ jsx15(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onUndo, disabled: !ready || !canUndo, "aria-label": t("Undo"), children: /* @__PURE__ */ jsx15(Undo2, {}) }) }),
5377
- /* @__PURE__ */ jsx15(TooltipWrap, { label: t("Redo (Ctrl+Y)"), children: /* @__PURE__ */ jsx15(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onRedo, disabled: !ready || !canRedo, "aria-label": t("Redo"), children: /* @__PURE__ */ jsx15(Redo2, {}) }) })
5591
+ const editing = /* @__PURE__ */ jsxs15("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2", children: [
5592
+ /* @__PURE__ */ jsxs15("div", { className: "eb:flex eb:items-center eb:gap-0.5", children: [
5593
+ /* @__PURE__ */ jsx16(TooltipWrap, { label: t("Undo (Ctrl+Z)"), children: /* @__PURE__ */ jsx16(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onUndo, disabled: !ready || !canUndo, "aria-label": t("Undo"), children: /* @__PURE__ */ jsx16(Undo2, {}) }) }),
5594
+ /* @__PURE__ */ jsx16(TooltipWrap, { label: t("Redo (Ctrl+Y)"), children: /* @__PURE__ */ jsx16(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onRedo, disabled: !ready || !canRedo, "aria-label": t("Redo"), children: /* @__PURE__ */ jsx16(Redo2, {}) }) })
5378
5595
  ] }),
5379
- /* @__PURE__ */ jsx15(Tabs, { value: device, onValueChange: onDeviceChange, children: /* @__PURE__ */ jsxs14(TabsList, { "aria-label": t("Device"), children: [
5380
- /* @__PURE__ */ jsxs14(TabsTrigger, { value: "desktop", disabled: !ready, "aria-label": compact ? t("Desktop") : void 0, children: [
5381
- /* @__PURE__ */ jsx15(Monitor, {}),
5596
+ /* @__PURE__ */ jsx16(Tabs, { value: device, onValueChange: onDeviceChange, children: /* @__PURE__ */ jsxs15(TabsList, { "aria-label": t("Device"), children: [
5597
+ /* @__PURE__ */ jsxs15(TabsTrigger, { value: "desktop", disabled: !ready, "aria-label": compact ? t("Desktop") : void 0, children: [
5598
+ /* @__PURE__ */ jsx16(Monitor, {}),
5382
5599
  !compact && t("Desktop")
5383
5600
  ] }),
5384
- /* @__PURE__ */ jsxs14(TabsTrigger, { value: "mobile", disabled: !ready, "aria-label": compact ? t("Mobile") : void 0, children: [
5385
- /* @__PURE__ */ jsx15(Smartphone, {}),
5601
+ /* @__PURE__ */ jsxs15(TabsTrigger, { value: "mobile", disabled: !ready, "aria-label": compact ? t("Mobile") : void 0, children: [
5602
+ /* @__PURE__ */ jsx16(Smartphone, {}),
5386
5603
  !compact && t("Mobile")
5387
5604
  ] })
5388
5605
  ] }) })
5389
5606
  ] });
5390
5607
  if (compact) {
5391
- return /* @__PURE__ */ jsxs14("div", { className: "eb:shrink-0 eb:border-b eb:border-border", "data-layout": "compact", children: [
5392
- /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:h-12 eb:items-center eb:gap-2 eb:px-2", children: [
5608
+ return /* @__PURE__ */ jsxs15("div", { className: "eb:shrink-0 eb:border-b eb:border-border", "data-layout": "compact", children: [
5609
+ /* @__PURE__ */ jsxs15("div", { className: "eb:flex eb:h-12 eb:items-center eb:gap-2 eb:px-2", children: [
5393
5610
  identity2,
5394
5611
  primary
5395
5612
  ] }),
5396
- /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:items-center eb:gap-2 eb:overflow-x-auto eb:px-2 eb:pb-2", "data-testid": "eb-toolbar-actions", children: [
5613
+ /* @__PURE__ */ jsxs15("div", { className: "eb:flex eb:items-center eb:gap-2 eb:overflow-x-auto eb:px-2 eb:pb-2", "data-testid": "eb-toolbar-actions", children: [
5397
5614
  editing,
5398
- /* @__PURE__ */ jsx15(Separator, { orientation: "vertical", className: "eb:h-5 eb:shrink-0" }),
5399
- /* @__PURE__ */ jsx15("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2", children: actions })
5615
+ /* @__PURE__ */ jsx16(Separator, { orientation: "vertical", className: "eb:h-5 eb:shrink-0" }),
5616
+ /* @__PURE__ */ jsx16("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2", children: actions })
5400
5617
  ] })
5401
5618
  ] });
5402
5619
  }
5403
- return /* @__PURE__ */ jsxs14("div", { className: "eb:flex eb:h-12 eb:shrink-0 eb:items-center eb:gap-3 eb:border-b eb:border-border eb:px-3", children: [
5620
+ return /* @__PURE__ */ jsxs15("div", { className: "eb:flex eb:h-12 eb:shrink-0 eb:items-center eb:gap-3 eb:border-b eb:border-border eb:px-3", children: [
5404
5621
  identity2,
5405
5622
  editing,
5406
- /* @__PURE__ */ jsxs14("div", { className: "eb:ml-auto eb:flex eb:items-center eb:gap-2", "data-testid": "eb-toolbar-actions", children: [
5623
+ /* @__PURE__ */ jsxs15("div", { className: "eb:ml-auto eb:flex eb:items-center eb:gap-2", "data-testid": "eb-toolbar-actions", children: [
5407
5624
  actions,
5408
5625
  primary
5409
5626
  ] })
@@ -5413,7 +5630,7 @@ function Toolbar({
5413
5630
  // src/ui/Drawer.jsx
5414
5631
  import { useEffect as useEffect8 } from "react";
5415
5632
  import { X as X3 } from "lucide-react";
5416
- import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
5633
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
5417
5634
  function Drawer({ open, side = "left", title, onClose, children, testId, header: header2 = true }) {
5418
5635
  const t = useT();
5419
5636
  useEffect8(() => {
@@ -5424,9 +5641,9 @@ function Drawer({ open, side = "left", title, onClose, children, testId, header:
5424
5641
  window.addEventListener("keydown", onKey);
5425
5642
  return () => window.removeEventListener("keydown", onKey);
5426
5643
  }, [open, onClose]);
5427
- return /* @__PURE__ */ jsxs15("div", { className: cn("eb:absolute eb:inset-0 eb:z-20", !open && "eb:hidden"), "data-testid": testId, "data-open": open ? "true" : "false", children: [
5428
- /* @__PURE__ */ jsx16("div", { className: "eb:absolute eb:inset-0 eb:bg-black/30", onClick: onClose, "aria-hidden": "true" }),
5429
- /* @__PURE__ */ jsxs15(
5644
+ return /* @__PURE__ */ jsxs16("div", { className: cn("eb:absolute eb:inset-0 eb:z-20", !open && "eb:hidden"), "data-testid": testId, "data-open": open ? "true" : "false", children: [
5645
+ /* @__PURE__ */ jsx17("div", { className: "eb:absolute eb:inset-0 eb:bg-black/30", onClick: onClose, "aria-hidden": "true" }),
5646
+ /* @__PURE__ */ jsxs16(
5430
5647
  "section",
5431
5648
  {
5432
5649
  role: "dialog",
@@ -5436,11 +5653,11 @@ function Drawer({ open, side = "left", title, onClose, children, testId, header:
5436
5653
  side === "left" ? "eb:left-0 eb:border-r eb:border-border" : "eb:right-0 eb:border-l eb:border-border"
5437
5654
  ),
5438
5655
  children: [
5439
- header2 && /* @__PURE__ */ jsxs15("header", { className: "eb:flex eb:h-10 eb:shrink-0 eb:items-center eb:justify-between eb:border-b eb:border-border eb:px-3", children: [
5440
- /* @__PURE__ */ jsx16("h3", { className: "eb:text-[13px] eb:font-medium eb:text-foreground", children: title }),
5441
- /* @__PURE__ */ jsx16(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onClose, "aria-label": t("Close"), children: /* @__PURE__ */ jsx16(X3, {}) })
5656
+ header2 && /* @__PURE__ */ jsxs16("header", { className: "eb:flex eb:h-10 eb:shrink-0 eb:items-center eb:justify-between eb:border-b eb:border-border eb:px-3", children: [
5657
+ /* @__PURE__ */ jsx17("h3", { className: "eb:text-[13px] eb:font-medium eb:text-foreground", children: title }),
5658
+ /* @__PURE__ */ jsx17(Button, { variant: "ghost", size: "icon-sm-compact", onClick: onClose, "aria-label": t("Close"), children: /* @__PURE__ */ jsx17(X3, {}) })
5442
5659
  ] }),
5443
- /* @__PURE__ */ jsx16("div", { className: "eb:grid eb:min-h-0 eb:flex-1", children })
5660
+ /* @__PURE__ */ jsx17("div", { className: "eb:grid eb:min-h-0 eb:flex-1", children })
5444
5661
  ]
5445
5662
  }
5446
5663
  )
@@ -5448,10 +5665,10 @@ function Drawer({ open, side = "left", title, onClose, children, testId, header:
5448
5665
  }
5449
5666
 
5450
5667
  // src/ui/useEmailEditorState.js
5451
- import { useCallback as useCallback2, useEffect as useEffect9, useRef as useRef7, useState as useState12 } from "react";
5668
+ import { useCallback as useCallback2, useEffect as useEffect9, useRef as useRef7, useState as useState13 } from "react";
5452
5669
  var DIRTY_CHECK_MS = 150;
5453
5670
  function useEmailEditorState(editor) {
5454
- const [state, setState] = useState12(() => ({
5671
+ const [state, setState] = useState13(() => ({
5455
5672
  status: editor?.status || "idle",
5456
5673
  selection: null,
5457
5674
  canUndo: false,
@@ -5532,7 +5749,7 @@ function useEmailEditorState(editor) {
5532
5749
  }
5533
5750
 
5534
5751
  // src/ui/EmailBuilder.jsx
5535
- import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
5752
+ import { Fragment as Fragment8, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
5536
5753
  var EMPTY_META = { name: "", subject: "", preheader: "", description: "" };
5537
5754
  function metaOf(template) {
5538
5755
  return {
@@ -5570,7 +5787,7 @@ function Builder({
5570
5787
  );
5571
5788
  const storage = useMemo5(() => injectedStorage || createHttpStorage(), [injectedStorage]);
5572
5789
  const systemVariables = useMemo5(() => defineVariables(variablesProp || DEFAULT_VARIABLES), [variablesProp]);
5573
- const [customVariables, setCustomVariables] = useState13([]);
5790
+ const [customVariables, setCustomVariables] = useState14([]);
5574
5791
  const variables = useMemo5(() => mergeVariables(systemVariables, customVariables), [systemVariables, customVariables]);
5575
5792
  const canManageVariables = typeof storage.saveVariable === "function" && typeof storage.listVariables === "function";
5576
5793
  const reloadVariables = useCallback3(async () => {
@@ -5604,20 +5821,20 @@ function Builder({
5604
5821
  const state = useEmailEditorState(editor);
5605
5822
  const autosaveOn = isFeatureEnabled("autosave");
5606
5823
  const autosaveOptions = emailBuilderFeatures.get("autosave")?.options || {};
5607
- const [template, setTemplate] = useState13({ id: initialTemplateId, revision: null, savedAt: null, auto: false });
5608
- const [boot, setBoot] = useState13({ status: "loading", document: null, mjml: null, draft: false, message: null });
5609
- const [meta, setMeta] = useState13(EMPTY_META);
5610
- const [savedMeta, setSavedMeta] = useState13(EMPTY_META);
5611
- const [saving, setSaving] = useState13(null);
5612
- const [showErrors, setShowErrors] = useState13(false);
5613
- const [autosaveBlocked, setAutosaveBlocked] = useState13(null);
5614
- const [engineError, setEngineError] = useState13(null);
5615
- const [attempt, setAttempt] = useState13(0);
5824
+ const [template, setTemplate] = useState14({ id: initialTemplateId, revision: null, savedAt: null, auto: false });
5825
+ const [boot, setBoot] = useState14({ status: "loading", document: null, mjml: null, draft: false, message: null });
5826
+ const [meta, setMeta] = useState14(EMPTY_META);
5827
+ const [savedMeta, setSavedMeta] = useState14(EMPTY_META);
5828
+ const [saving, setSaving] = useState14(null);
5829
+ const [showErrors, setShowErrors] = useState14(false);
5830
+ const [autosaveBlocked, setAutosaveBlocked] = useState14(null);
5831
+ const [engineError, setEngineError] = useState14(null);
5832
+ const [attempt, setAttempt] = useState14(0);
5616
5833
  const rootRef = useRef8(null);
5617
5834
  const layout2 = useLayoutWidth(rootRef);
5618
5835
  const compact = layout2 !== "wide";
5619
5836
  const blocksInDrawer = layout2 === "narrow";
5620
- const [drawer, setDrawer] = useState13(null);
5837
+ const [drawer, setDrawer] = useState14(null);
5621
5838
  const closeDrawer = useCallback3(() => setDrawer(null), []);
5622
5839
  useEffect10(() => {
5623
5840
  if (!compact) setDrawer(null);
@@ -5883,10 +6100,10 @@ function Builder({
5883
6100
  setBrand,
5884
6101
  ai
5885
6102
  };
5886
- const toolbarFeatures = emailBuilderFeatures.list(
5887
- (f) => f.enabled !== false && Boolean(f.toolbar) && (f.isAvailable ? f.isAvailable(featureContext) : true)
5888
- );
5889
- const properties = /* @__PURE__ */ jsx17(
6103
+ const featuresWith = (slot) => emailBuilderFeatures.list((f) => f.enabled !== false && Boolean(f[slot]) && (f.isAvailable ? f.isAvailable(featureContext) : true));
6104
+ const toolbarFeatures = featuresWith("toolbar");
6105
+ const canvasBars = featuresWith("canvasBar");
6106
+ const properties = /* @__PURE__ */ jsx18(
5890
6107
  PropertiesPanel,
5891
6108
  {
5892
6109
  onDismiss: compact ? closeDrawer : null,
@@ -5896,13 +6113,13 @@ function Builder({
5896
6113
  onMetaChange: setMeta,
5897
6114
  changes: state.changes,
5898
6115
  showErrors,
5899
- actions: /* @__PURE__ */ jsx17(ElementActions, { editor, selection: state.selection }),
6116
+ actions: /* @__PURE__ */ jsx18(ElementActions, { editor, selection: state.selection }),
5900
6117
  services,
5901
6118
  ai,
5902
6119
  variables
5903
6120
  }
5904
6121
  );
5905
- return /* @__PURE__ */ jsxs16(
6122
+ return /* @__PURE__ */ jsxs17(
5906
6123
  "div",
5907
6124
  {
5908
6125
  ref: rootRef,
@@ -5912,7 +6129,7 @@ function Builder({
5912
6129
  "data-layout": layout2,
5913
6130
  "data-template-id": template.id || "",
5914
6131
  children: [
5915
- /* @__PURE__ */ jsx17(
6132
+ /* @__PURE__ */ jsx18(
5916
6133
  Toolbar,
5917
6134
  {
5918
6135
  title: meta.name,
@@ -5928,19 +6145,19 @@ function Builder({
5928
6145
  compact,
5929
6146
  actions: [
5930
6147
  // The AI module's button first, when the host gave the module.
5931
- ai?.proofread && /* @__PURE__ */ jsx17(ai.components.Proofread, { context: featureContext, proofread: ai.proofread }, "ai-proofread"),
6148
+ ai?.proofread && /* @__PURE__ */ jsx18(ai.components.Proofread, { context: featureContext, proofread: ai.proofread }, "ai-proofread"),
5932
6149
  ...toolbarFeatures.map((feature) => {
5933
6150
  const Action = feature.toolbar;
5934
- return /* @__PURE__ */ jsx17(Action, { context: featureContext }, feature.id);
6151
+ return /* @__PURE__ */ jsx18(Action, { context: featureContext }, feature.id);
5935
6152
  })
5936
6153
  ].filter(Boolean),
5937
- primary: /* @__PURE__ */ jsxs16(Button, { size: "sm-compact", onClick: () => save(), disabled: !ready || saving !== null, children: [
5938
- saving === "manual" ? /* @__PURE__ */ jsx17(Loader25, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx17(Save, {}),
6154
+ primary: /* @__PURE__ */ jsxs17(Button, { size: "sm-compact", onClick: () => save(), disabled: !ready || saving !== null, children: [
6155
+ saving === "manual" ? /* @__PURE__ */ jsx18(Loader25, { className: "eb:animate-spin" }) : /* @__PURE__ */ jsx18(Save, {}),
5939
6156
  t("Save")
5940
6157
  ] })
5941
6158
  }
5942
6159
  ),
5943
- /* @__PURE__ */ jsxs16(
6160
+ /* @__PURE__ */ jsxs17(
5944
6161
  "div",
5945
6162
  {
5946
6163
  className: cn(
@@ -5950,12 +6167,12 @@ function Builder({
5950
6167
  layout2 === "narrow" && "eb:grid-cols-1"
5951
6168
  ),
5952
6169
  children: [
5953
- !blocksInDrawer && /* @__PURE__ */ jsx17(BlockSidebar, { editor, disabled: !ready, onAdded: compact ? () => setDrawer("properties") : void 0 }),
5954
- /* @__PURE__ */ jsx17("div", { className: "eb:relative eb:min-w-0 eb:bg-surface-75", "aria-busy": !ready, children: boot.status === "error" || engineError ? /* @__PURE__ */ jsxs16("div", { role: "alert", className: "eb:flex eb:h-full eb:flex-col eb:items-center eb:justify-center eb:gap-2 eb:p-6 eb:text-center", children: [
5955
- /* @__PURE__ */ jsx17(AlertTriangle, { className: "eb:size-6 eb:text-destructive", "aria-hidden": "true" }),
5956
- /* @__PURE__ */ jsx17("p", { className: "eb:text-[13px] eb:text-foreground", children: boot.status === "error" ? t("The template could not be loaded.") : t("The builder could not start.") }),
5957
- /* @__PURE__ */ jsx17("p", { className: "eb:max-w-sm eb:text-[12px] eb:text-foreground-muted", children: boot.message || engineError }),
5958
- engineError && /* @__PURE__ */ jsx17(
6170
+ !blocksInDrawer && /* @__PURE__ */ jsx18(BlockSidebar, { editor, disabled: !ready, onAdded: compact ? () => setDrawer("properties") : void 0 }),
6171
+ /* @__PURE__ */ jsx18("div", { className: "eb:relative eb:flex eb:min-w-0 eb:flex-col eb:bg-surface-75", "aria-busy": !ready, children: boot.status === "error" || engineError ? /* @__PURE__ */ jsxs17("div", { role: "alert", className: "eb:flex eb:h-full eb:flex-col eb:items-center eb:justify-center eb:gap-2 eb:p-6 eb:text-center", children: [
6172
+ /* @__PURE__ */ jsx18(AlertTriangle, { className: "eb:size-6 eb:text-destructive", "aria-hidden": "true" }),
6173
+ /* @__PURE__ */ jsx18("p", { className: "eb:text-[13px] eb:text-foreground", children: boot.status === "error" ? t("The template could not be loaded.") : t("The builder could not start.") }),
6174
+ /* @__PURE__ */ jsx18("p", { className: "eb:max-w-sm eb:text-[12px] eb:text-foreground-muted", children: boot.message || engineError }),
6175
+ engineError && /* @__PURE__ */ jsx18(
5959
6176
  Button,
5960
6177
  {
5961
6178
  variant: "outline",
@@ -5967,8 +6184,12 @@ function Builder({
5967
6184
  children: t("Retry")
5968
6185
  }
5969
6186
  )
5970
- ] }) : /* @__PURE__ */ jsxs16(Fragment8, { children: [
5971
- boot.status === "ready" && /* @__PURE__ */ jsx17(
6187
+ ] }) : /* @__PURE__ */ jsxs17(Fragment8, { children: [
6188
+ canvasBars.map((feature) => {
6189
+ const Bar = feature.canvasBar;
6190
+ return /* @__PURE__ */ jsx18(Bar, { context: featureContext }, feature.id);
6191
+ }),
6192
+ /* @__PURE__ */ jsx18("div", { className: "eb:relative eb:min-h-0 eb:flex-1", children: boot.status === "ready" && /* @__PURE__ */ jsx18(
5972
6193
  EmailCanvas,
5973
6194
  {
5974
6195
  editor,
@@ -5977,15 +6198,15 @@ function Builder({
5977
6198
  onError: (error) => setEngineError(error.message)
5978
6199
  },
5979
6200
  attempt
5980
- ),
5981
- !ready && /* @__PURE__ */ jsxs16("div", { className: "eb:absolute eb:inset-0 eb:flex eb:items-center eb:justify-center eb:bg-surface-75", role: "status", "aria-live": "polite", children: [
5982
- /* @__PURE__ */ jsx17(Loader25, { className: "eb:size-6 eb:animate-spin eb:text-foreground-muted", "aria-hidden": "true" }),
5983
- /* @__PURE__ */ jsx17("span", { className: "eb:sr-only", children: t("Loading the builder") })
6201
+ ) }),
6202
+ !ready && /* @__PURE__ */ jsxs17("div", { className: "eb:absolute eb:inset-0 eb:flex eb:items-center eb:justify-center eb:bg-surface-75", role: "status", "aria-live": "polite", children: [
6203
+ /* @__PURE__ */ jsx18(Loader25, { className: "eb:size-6 eb:animate-spin eb:text-foreground-muted", "aria-hidden": "true" }),
6204
+ /* @__PURE__ */ jsx18("span", { className: "eb:sr-only", children: t("Loading the builder") })
5984
6205
  ] })
5985
6206
  ] }) }),
5986
6207
  !compact && properties,
5987
- blocksInDrawer && /* @__PURE__ */ jsx17(Drawer, { open: drawer === "blocks", side: "left", title: t("Add a block"), onClose: closeDrawer, testId: "eb-blocks-drawer", children: /* @__PURE__ */ jsx17(BlockSidebar, { editor, disabled: !ready, onAdded: () => setDrawer("properties") }) }),
5988
- compact && /* @__PURE__ */ jsx17(
6208
+ blocksInDrawer && /* @__PURE__ */ jsx18(Drawer, { open: drawer === "blocks", side: "left", title: t("Add a block"), onClose: closeDrawer, testId: "eb-blocks-drawer", children: /* @__PURE__ */ jsx18(BlockSidebar, { editor, disabled: !ready, onAdded: () => setDrawer("properties") }) }),
6209
+ compact && /* @__PURE__ */ jsx18(
5989
6210
  Drawer,
5990
6211
  {
5991
6212
  open: drawer === "properties",
@@ -6000,12 +6221,12 @@ function Builder({
6000
6221
  ]
6001
6222
  }
6002
6223
  ),
6003
- compact && /* @__PURE__ */ jsxs16("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2 eb:border-t eb:border-border eb:px-2 eb:py-2", "data-testid": "eb-compact-bar", children: [
6004
- blocksInDrawer && /* @__PURE__ */ jsxs16(Button, { variant: "outline", size: "sm-compact", disabled: !ready, onClick: () => setDrawer("blocks"), children: [
6005
- /* @__PURE__ */ jsx17(Plus2, {}),
6224
+ compact && /* @__PURE__ */ jsxs17("div", { className: "eb:flex eb:shrink-0 eb:items-center eb:gap-2 eb:border-t eb:border-border eb:px-2 eb:py-2", "data-testid": "eb-compact-bar", children: [
6225
+ blocksInDrawer && /* @__PURE__ */ jsxs17(Button, { variant: "outline", size: "sm-compact", disabled: !ready, onClick: () => setDrawer("blocks"), children: [
6226
+ /* @__PURE__ */ jsx18(Plus2, {}),
6006
6227
  t("Add a block")
6007
6228
  ] }),
6008
- /* @__PURE__ */ jsxs16(
6229
+ /* @__PURE__ */ jsxs17(
6009
6230
  Button,
6010
6231
  {
6011
6232
  variant: state.selection ? "default" : "outline",
@@ -6014,14 +6235,14 @@ function Builder({
6014
6235
  disabled: !ready,
6015
6236
  onClick: () => setDrawer("properties"),
6016
6237
  children: [
6017
- /* @__PURE__ */ jsx17(SlidersHorizontal, {}),
6018
- /* @__PURE__ */ jsx17("span", { className: "eb:truncate", children: ready && state.selection ? t("Edit: {element}", { element: t(state.selection.label) }) : t("Email settings") })
6238
+ /* @__PURE__ */ jsx18(SlidersHorizontal, {}),
6239
+ /* @__PURE__ */ jsx18("span", { className: "eb:truncate", children: ready && state.selection ? t("Edit: {element}", { element: t(state.selection.label) }) : t("Email settings") })
6019
6240
  ]
6020
6241
  }
6021
6242
  )
6022
6243
  ] }),
6023
- /* @__PURE__ */ jsx17(NoticeArea, {}),
6024
- ai?.key && ai.components.Connect && /* @__PURE__ */ jsx17(ai.components.Connect, { aiKey: ai.key })
6244
+ /* @__PURE__ */ jsx18(NoticeArea, {}),
6245
+ ai?.key && ai.components.Connect && /* @__PURE__ */ jsx18(ai.components.Connect, { aiKey: ai.key })
6025
6246
  ]
6026
6247
  }
6027
6248
  );
@@ -6032,7 +6253,7 @@ function EmailBuilder({ locale = "en", messages, onNotify, ai: aiProp = null, ..
6032
6253
  () => ai ? { ...messagesOf(ai.messages, locale), ...messages } : messages,
6033
6254
  [ai, locale, messages]
6034
6255
  );
6035
- return /* @__PURE__ */ jsx17(UiProvider, { locale, messages: allMessages, onNotify, children: /* @__PURE__ */ jsx17(Builder, { ...props, ai, templateId: props.templateId ?? null }) });
6256
+ return /* @__PURE__ */ jsx18(UiProvider, { locale, messages: allMessages, onNotify, children: /* @__PURE__ */ jsx18(Builder, { ...props, ai, templateId: props.templateId ?? null }) });
6036
6257
  }
6037
6258
  function useAiModule(value) {
6038
6259
  const valid = Boolean(value && typeof value === "object" && /** @type {any} */
@@ -6051,7 +6272,7 @@ function useAiModule(value) {
6051
6272
  // src/ui/EmailBuilderBoundary.jsx
6052
6273
  import { Component } from "react";
6053
6274
  import { AlertTriangle as AlertTriangle2, ArrowLeft as ArrowLeft3, RefreshCw } from "lucide-react";
6054
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
6275
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
6055
6276
  var isChunkError = (error) => error?.name === "ChunkLoadError" || /Loading (CSS )?chunk .* failed/i.test(String(error?.message || ""));
6056
6277
  var english = (text) => text;
6057
6278
  var EmailBuilderBoundary = class extends Component {
@@ -6076,23 +6297,23 @@ var EmailBuilderBoundary = class extends Component {
6076
6297
  const { error, attempt } = this.state;
6077
6298
  const t = this.props.t || english;
6078
6299
  if (!error) {
6079
- return /* @__PURE__ */ jsx18("div", { style: { height: "100%" }, children: this.props.children }, attempt);
6300
+ return /* @__PURE__ */ jsx19("div", { style: { height: "100%" }, children: this.props.children }, attempt);
6080
6301
  }
6081
- return /* @__PURE__ */ jsxs17(
6302
+ return /* @__PURE__ */ jsxs18(
6082
6303
  "div",
6083
6304
  {
6084
6305
  role: "alert",
6085
6306
  className: "eb-root eb:flex eb:h-full eb:flex-col eb:items-center eb:justify-center eb:gap-3 eb:p-6 eb:text-center",
6086
6307
  children: [
6087
- /* @__PURE__ */ jsx18(AlertTriangle2, { className: "eb:size-6 eb:text-destructive", "aria-hidden": "true" }),
6088
- /* @__PURE__ */ jsx18("p", { className: "eb:text-[13px] eb:text-foreground", children: t("The builder could not load.") }),
6089
- /* @__PURE__ */ jsxs17("div", { className: "eb:flex eb:gap-2", children: [
6090
- this.props.onClose && /* @__PURE__ */ jsxs17(Button, { variant: "outline", size: "sm", onClick: this.props.onClose, children: [
6091
- /* @__PURE__ */ jsx18(ArrowLeft3, {}),
6308
+ /* @__PURE__ */ jsx19(AlertTriangle2, { className: "eb:size-6 eb:text-destructive", "aria-hidden": "true" }),
6309
+ /* @__PURE__ */ jsx19("p", { className: "eb:text-[13px] eb:text-foreground", children: t("The builder could not load.") }),
6310
+ /* @__PURE__ */ jsxs18("div", { className: "eb:flex eb:gap-2", children: [
6311
+ this.props.onClose && /* @__PURE__ */ jsxs18(Button, { variant: "outline", size: "sm", onClick: this.props.onClose, children: [
6312
+ /* @__PURE__ */ jsx19(ArrowLeft3, {}),
6092
6313
  t("Back")
6093
6314
  ] }),
6094
- /* @__PURE__ */ jsxs17(Button, { size: "sm", onClick: this.retry, children: [
6095
- /* @__PURE__ */ jsx18(RefreshCw, {}),
6315
+ /* @__PURE__ */ jsxs18(Button, { size: "sm", onClick: this.retry, children: [
6316
+ /* @__PURE__ */ jsx19(RefreshCw, {}),
6096
6317
  t("Retry")
6097
6318
  ] })
6098
6319
  ] })
@@ -6120,4 +6341,4 @@ export {
6120
6341
  EmailBuilder,
6121
6342
  EmailBuilderBoundary
6122
6343
  };
6123
- //# sourceMappingURL=chunk-OACK2DIW.js.map
6344
+ //# sourceMappingURL=chunk-G7G3TRWG.js.map