react-email-studio 2.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -170,7 +170,8 @@ var I18N = {
170
170
  blockPaletteGroupWidgets: "Widgets",
171
171
  closePanel: "Close",
172
172
  canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
173
- emailContentSettings: "Email content"
173
+ emailContentSettings: "Email content",
174
+ loadingDesign: "Loading design\u2026"
174
175
  },
175
176
  fr: {
176
177
  layouts: "Mises en page",
@@ -223,6 +224,7 @@ var I18N = {
223
224
  blockPaletteGroupWidgets: "Widgets",
224
225
  closePanel: "Fermer",
225
226
  canvasEmptyHint: "Glissez des blocs dans la colonne, ou ajoutez un bloc Mise en page pour plusieurs colonnes.",
227
+ loadingDesign: "Chargement du design\u2026",
226
228
  emailContentSettings: "Contenu de l\u2019e-mail",
227
229
  zoomIn: "Zoom +",
228
230
  zoomOut: "Zoom -",
@@ -282,6 +284,7 @@ var I18N = {
282
284
  blockPaletteGroupWidgets: "Widgets",
283
285
  closePanel: "Schlie\xDFen",
284
286
  canvasEmptyHint: "Bl\xF6cke in die Spalte ziehen oder Layout-Block f\xFCr mehrspaltige Bereiche hinzuf\xFCgen.",
287
+ loadingDesign: "Design wird geladen\u2026",
285
288
  emailContentSettings: "E-Mail-Inhalt",
286
289
  zoomIn: "Zoom +",
287
290
  zoomOut: "Zoom -",
@@ -342,6 +345,7 @@ var I18N = {
342
345
  blockPaletteGroupWidgets: "Widgets",
343
346
  closePanel: "Cerrar",
344
347
  canvasEmptyHint: "Arrastra bloques a la columna o a\xF1ade un bloque Dise\xF1o para varias columnas.",
348
+ loadingDesign: "Cargando dise\xF1o\u2026",
345
349
  emailContentSettings: "Contenido del correo",
346
350
  resizeSidebar: "Redimensionar panel"
347
351
  }
@@ -692,6 +696,10 @@ function makeContentBlock(type) {
692
696
  }
693
697
 
694
698
  // src/lib/columnPath.ts
699
+ var MAX_NESTED_LAYOUT_DEPTH = 1;
700
+ function nestedLayoutDepth(nested) {
701
+ return nested != null && nested.parentBlockIdx != null && nested.innerCellIdx != null ? 1 : 0;
702
+ }
695
703
  function isSplitLayoutBlock(b) {
696
704
  return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
697
705
  }
@@ -1283,8 +1291,18 @@ function normalizeEmailDocument(input) {
1283
1291
  settings: doc.settings && typeof doc.settings === "object" && !Array.isArray(doc.settings) ? doc.settings : {},
1284
1292
  rows: rows.map((r, ri) => {
1285
1293
  const columns = Array.isArray(r.columns) ? r.columns : [];
1286
- const colCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : Math.max(1, columns.length || 1);
1287
- const colsNormalized = columns.length > 0 ? columns : Array.from({ length: colCount }).map(() => ({}));
1294
+ const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
1295
+ const colCount = Math.max(1, columns.length, layoutColCount);
1296
+ const colsNormalized = (() => {
1297
+ if (columns.length >= colCount) return columns;
1298
+ if (columns.length > 0) {
1299
+ return [
1300
+ ...columns,
1301
+ ...Array.from({ length: colCount - columns.length }).map(() => ({}))
1302
+ ];
1303
+ }
1304
+ return Array.from({ length: colCount }).map(() => ({}));
1305
+ })();
1288
1306
  return {
1289
1307
  id: ensureId(r.id, `row${ri + 1}`),
1290
1308
  type: "row",
@@ -1295,6 +1313,7 @@ function normalizeEmailDocument(input) {
1295
1313
  align: r.layout?.align || "center"
1296
1314
  },
1297
1315
  styles: r.styles && typeof r.styles === "object" && !Array.isArray(r.styles) ? r.styles : {},
1316
+ ...typeof r._reactEmailStudio === "object" && r._reactEmailStudio !== null && !Array.isArray(r._reactEmailStudio) ? { _reactEmailStudio: r._reactEmailStudio } : {},
1298
1317
  columns: colsNormalized.map((c, ci) => ({
1299
1318
  id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
1300
1319
  layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
@@ -1305,7 +1324,8 @@ function normalizeEmailDocument(input) {
1305
1324
  content: b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
1306
1325
  styles: b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles) ? b.styles : {},
1307
1326
  behavior: b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? b.behavior : {},
1308
- responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {}
1327
+ responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {},
1328
+ ...b?.props && typeof b.props === "object" && !Array.isArray(b.props) ? { props: b.props } : {}
1309
1329
  })) : []
1310
1330
  }))
1311
1331
  };
@@ -1316,6 +1336,14 @@ function normalizeEmailDocument(input) {
1316
1336
  }
1317
1337
 
1318
1338
  // src/lib/emailDesignJson.ts
1339
+ var MAX_LAYOUT_TREE_DEPTH = 32;
1340
+ function cloneJson(v) {
1341
+ try {
1342
+ return JSON.parse(JSON.stringify(v));
1343
+ } catch {
1344
+ return v;
1345
+ }
1346
+ }
1319
1347
  function layoutColumnPaddingForDoc(p) {
1320
1348
  if (typeof p === "number" && Number.isFinite(p)) {
1321
1349
  const n = Math.max(0, p);
@@ -1402,7 +1430,242 @@ function normalizeBoxStyles(props, t) {
1402
1430
  }
1403
1431
  props.padding = boxPad(props.padding, uniformPadDefault(def?.padding));
1404
1432
  }
1405
- function mapBlockToInternal(b) {
1433
+ function internalBlockToEmailDoc(b, depth = 0) {
1434
+ const p = b?.props && typeof b.props === "object" ? b.props : {};
1435
+ const id = typeof b?.id === "string" ? b.id : uid();
1436
+ const type = b?.type === "nestedRow" ? "layout" : b?.type;
1437
+ if (!isKnownBlockType(type)) {
1438
+ return { id, type: String(type || "text"), content: {}, styles: {} };
1439
+ }
1440
+ switch (type) {
1441
+ case "heading": {
1442
+ const fw = typeof p.fontWeight === "number" ? p.fontWeight : typeof p.fontWeight === "string" ? Number.parseInt(String(p.fontWeight), 10) || (p.bold ? 700 : 400) : p.bold ? 700 : 400;
1443
+ const padUniform = typeof p.padding === "number" && Number.isFinite(p.padding) ? p.padding : uniformPadDefault(p.padding);
1444
+ return {
1445
+ id,
1446
+ type,
1447
+ content: { text: typeof p.content === "string" ? p.content : "", tag: p.level || "h2" },
1448
+ styles: {
1449
+ fontSize: p.fontSize,
1450
+ fontWeight: fw,
1451
+ color: p.color,
1452
+ lineHeight: p.lineHeight,
1453
+ textAlign: p.align,
1454
+ letterSpacing: p.letterSpacing,
1455
+ marginBottom: padUniform
1456
+ }
1457
+ };
1458
+ }
1459
+ case "text":
1460
+ return {
1461
+ id,
1462
+ type,
1463
+ content: { html: typeof p.content === "string" ? p.content : "" },
1464
+ styles: {
1465
+ fontSize: p.fontSize,
1466
+ color: p.color,
1467
+ lineHeight: p.lineHeight,
1468
+ textAlign: p.align,
1469
+ ...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
1470
+ }
1471
+ };
1472
+ case "html":
1473
+ return {
1474
+ id,
1475
+ type,
1476
+ content: { html: typeof p.content === "string" ? p.content : "" },
1477
+ styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
1478
+ };
1479
+ case "image": {
1480
+ const br = p.borderRadius;
1481
+ const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
1482
+ (numOr(br.tl, 0) + numOr(br.tr, numOr(br.tl, 0)) + numOr(br.br, numOr(br.tl, 0)) + numOr(br.bl, numOr(br.tr, 0))) / 4
1483
+ ) : 0;
1484
+ return {
1485
+ id,
1486
+ type,
1487
+ content: {
1488
+ src: p.src || "",
1489
+ alt: p.alt || "",
1490
+ link: p.link || ""
1491
+ },
1492
+ styles: {
1493
+ width: p.width,
1494
+ align: p.align,
1495
+ borderRadius
1496
+ },
1497
+ ...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
1498
+ };
1499
+ }
1500
+ case "button": {
1501
+ let borderRadiusOut;
1502
+ if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
1503
+ borderRadiusOut = p.borderRadius;
1504
+ } else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
1505
+ const br = p.borderRadius;
1506
+ const tl = numOr(br.tl, 0);
1507
+ const tr2 = numOr(br.tr, tl);
1508
+ const brc = numOr(br.br, tl);
1509
+ const bl = numOr(br.bl, tr2);
1510
+ borderRadiusOut = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
1511
+ }
1512
+ return {
1513
+ id,
1514
+ type,
1515
+ content: { text: p.label || "", href: p.href || "#" },
1516
+ styles: {
1517
+ backgroundColor: p.bgColor,
1518
+ color: p.textColor,
1519
+ fontSize: p.fontSize,
1520
+ fontWeight: p.fontWeight,
1521
+ ...borderRadiusOut !== void 0 ? { borderRadius: borderRadiusOut } : {},
1522
+ textAlign: p.align,
1523
+ padding: {
1524
+ top: p.paddingV ?? 11,
1525
+ right: p.paddingH ?? 24,
1526
+ bottom: p.paddingV ?? 11,
1527
+ left: p.paddingH ?? 24
1528
+ }
1529
+ },
1530
+ ...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
1531
+ };
1532
+ }
1533
+ case "divider": {
1534
+ const padY = typeof p.padding === "number" && Number.isFinite(p.padding) ? p.padding : p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? numOr(p.padding.top, 12) : 12;
1535
+ return {
1536
+ id,
1537
+ type,
1538
+ content: {},
1539
+ styles: { color: p.color, thickness: p.thickness, paddingY: padY }
1540
+ };
1541
+ }
1542
+ case "spacer":
1543
+ return { id, type, content: {}, styles: { height: p.height } };
1544
+ case "social": {
1545
+ const urls = p.socialUrls && typeof p.socialUrls === "object" && !Array.isArray(p.socialUrls) ? p.socialUrls : {};
1546
+ const networks = {};
1547
+ const ordered = Array.isArray(p.networks) ? p.networks.filter((n) => typeof n === "string" && !!n) : [];
1548
+ const seen = /* @__PURE__ */ new Set();
1549
+ for (const n of ordered) {
1550
+ const u = urls[n];
1551
+ networks[n] = typeof u === "string" && u.trim() ? u : "";
1552
+ seen.add(n);
1553
+ }
1554
+ for (const key of Object.keys(urls)) {
1555
+ if (seen.has(key)) continue;
1556
+ const v = urls[key];
1557
+ if (typeof v === "string" && v.trim()) {
1558
+ networks[key] = v;
1559
+ seen.add(key);
1560
+ }
1561
+ }
1562
+ const padStyles = p.padding != null ? typeof p.padding === "number" && Number.isFinite(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {} : {};
1563
+ return {
1564
+ id,
1565
+ type,
1566
+ content: { networks },
1567
+ styles: {
1568
+ iconSize: p.iconSize,
1569
+ gap: p.gap,
1570
+ shape: p.shape,
1571
+ align: p.align,
1572
+ ...padStyles
1573
+ }
1574
+ };
1575
+ }
1576
+ case "table":
1577
+ return {
1578
+ id,
1579
+ type,
1580
+ content: { data: Array.isArray(p.data) ? p.data : [] },
1581
+ styles: {
1582
+ borderColor: p.cellBorder,
1583
+ cellPadding: p.cellPadding,
1584
+ headerBg: p.headerBg,
1585
+ striped: p.striped
1586
+ }
1587
+ };
1588
+ case "video":
1589
+ return {
1590
+ id,
1591
+ type,
1592
+ content: { url: p.src || "" },
1593
+ styles: { height: p.height }
1594
+ };
1595
+ case "timer": {
1596
+ let timerBr;
1597
+ if (typeof p.borderRadius === "number" && Number.isFinite(p.borderRadius)) {
1598
+ timerBr = p.borderRadius;
1599
+ } else if (p.borderRadius && typeof p.borderRadius === "object" && !Array.isArray(p.borderRadius)) {
1600
+ const br = p.borderRadius;
1601
+ const tl = numOr(br.tl, 0);
1602
+ const tr2 = numOr(br.tr, tl);
1603
+ const brc = numOr(br.br, tl);
1604
+ const bl = numOr(br.bl, tr2);
1605
+ timerBr = tl === tr2 && tr2 === brc && brc === bl ? tl : Math.round((tl + tr2 + brc + bl) / 4);
1606
+ }
1607
+ return {
1608
+ id,
1609
+ type,
1610
+ content: { endDate: p.endDate || "" },
1611
+ styles: {
1612
+ backgroundColor: p.bgColor,
1613
+ color: p.textColor,
1614
+ padding: typeof p.padding === "number" ? p.padding : uniformPadDefault(p.padding),
1615
+ ...timerBr !== void 0 ? { borderRadius: timerBr } : {},
1616
+ textAlign: p.align
1617
+ }
1618
+ };
1619
+ }
1620
+ case "menu":
1621
+ return {
1622
+ id,
1623
+ type,
1624
+ content: { items: Array.isArray(p.items) ? p.items : [] },
1625
+ styles: { fontSize: p.fontSize, color: p.color }
1626
+ };
1627
+ case "link":
1628
+ return {
1629
+ id,
1630
+ type,
1631
+ content: { text: p.label || "", href: p.href || "#" },
1632
+ styles: { color: p.color, fontSize: p.fontSize, textAlign: p.align },
1633
+ ...p.linkTarget === "_blank" ? { behavior: { openInNewTab: true } } : {}
1634
+ };
1635
+ case "layout": {
1636
+ const ratios = Array.isArray(p.ratios) ? [...p.ratios] : [1];
1637
+ const emptyCells = ratios.map(() => []);
1638
+ const rawCells = Array.isArray(p.cells) ? p.cells : [];
1639
+ const cellsOut = depth >= MAX_LAYOUT_TREE_DEPTH ? emptyCells : ratios.map((_, i) => {
1640
+ const col = rawCells[i];
1641
+ return Array.isArray(col) ? col.map((child) => internalBlockToEmailDoc(child, depth + 1)) : [];
1642
+ });
1643
+ return {
1644
+ id,
1645
+ type,
1646
+ content: {
1647
+ preset: p.preset,
1648
+ cols: p.cols,
1649
+ ratios,
1650
+ gap: p.gap,
1651
+ padding: p.padding,
1652
+ bgColor: p.bgColor,
1653
+ bgImage: p.bgImage,
1654
+ bgSize: p.bgSize,
1655
+ bgRepeat: p.bgRepeat,
1656
+ bgPosition: p.bgPosition,
1657
+ bgGradient: p.bgGradient ?? null,
1658
+ columnStyles: p.columnStyles,
1659
+ cells: cellsOut
1660
+ },
1661
+ styles: {}
1662
+ };
1663
+ }
1664
+ default:
1665
+ return { id, type, content: {}, styles: {} };
1666
+ }
1667
+ }
1668
+ function mapBlockToInternal(b, layoutDepth = 0) {
1406
1669
  const t = b.type;
1407
1670
  if (!isKnownBlockType(t)) return null;
1408
1671
  const block = makeContentBlock(t);
@@ -1473,8 +1736,28 @@ function mapBlockToInternal(b) {
1473
1736
  if (asNum(s.height) != null) block.props.height = s.height;
1474
1737
  break;
1475
1738
  case "social": {
1476
- const networksObj = c.networks && typeof c.networks === "object" ? c.networks : {};
1477
- const keys = Object.keys(networksObj || {});
1739
+ const networksRaw = c.networks;
1740
+ let networksObj = {};
1741
+ if (Array.isArray(networksRaw)) {
1742
+ const urlMap = c.socialUrls && typeof c.socialUrls === "object" && !Array.isArray(c.socialUrls) ? c.socialUrls : {};
1743
+ for (const n of networksRaw) {
1744
+ if (typeof n !== "string" || !n) continue;
1745
+ const u = urlMap[n];
1746
+ networksObj[n] = typeof u === "string" ? u : "";
1747
+ }
1748
+ for (const key of Object.keys(urlMap)) {
1749
+ if (key in networksObj) continue;
1750
+ const u = urlMap[key];
1751
+ if (typeof u === "string" && u.trim()) networksObj[key] = u;
1752
+ }
1753
+ } else if (networksRaw && typeof networksRaw === "object" && !Array.isArray(networksRaw)) {
1754
+ networksObj = { ...networksRaw };
1755
+ for (const k of Object.keys(networksObj)) {
1756
+ const v = networksObj[k];
1757
+ networksObj[k] = typeof v === "string" ? v : "";
1758
+ }
1759
+ }
1760
+ const keys = Object.keys(networksObj);
1478
1761
  if (keys.length) {
1479
1762
  block.props.networks = keys;
1480
1763
  block.props.socialUrls = networksObj;
@@ -1483,6 +1766,13 @@ function mapBlockToInternal(b) {
1483
1766
  if (asNum(s.gap) != null) block.props.gap = s.gap;
1484
1767
  if (asStr(s.shape)) block.props.shape = s.shape;
1485
1768
  if (asStr(s.align)) block.props.align = s.align;
1769
+ if (s.padding != null) {
1770
+ if (typeof s.padding === "number" && Number.isFinite(s.padding)) {
1771
+ block.props.padding = s.padding;
1772
+ } else if (s.padding && typeof s.padding === "object" && !Array.isArray(s.padding)) {
1773
+ block.props.padding = layoutColumnPaddingForDoc(s.padding);
1774
+ }
1775
+ }
1486
1776
  break;
1487
1777
  }
1488
1778
  case "table":
@@ -1523,15 +1813,81 @@ function mapBlockToInternal(b) {
1523
1813
  if (asNum(s.fontSize) != null) block.props.fontSize = s.fontSize;
1524
1814
  if (asStr(s.textAlign)) block.props.align = s.textAlign;
1525
1815
  break;
1526
- case "layout":
1816
+ case "layout": {
1817
+ if (Array.isArray(c.cells)) {
1818
+ const d = DEFAULT_BLOCK_PROPS.layout;
1819
+ block.props.preset = asStr(c.preset) ?? block.props.preset;
1820
+ if (typeof c.cols === "number") block.props.cols = c.cols;
1821
+ if (Array.isArray(c.ratios)) block.props.ratios = [...c.ratios];
1822
+ if (typeof c.gap === "number") block.props.gap = c.gap;
1823
+ if (c.padding !== void 0 && c.padding !== null) block.props.padding = c.padding;
1824
+ if (typeof c.bgColor === "string") block.props.bgColor = c.bgColor;
1825
+ if (typeof c.bgImage === "string") block.props.bgImage = c.bgImage;
1826
+ if (typeof c.bgSize === "string") block.props.bgSize = c.bgSize;
1827
+ if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
1828
+ if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
1829
+ if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
1830
+ if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
1831
+ block.props.columnStyles = c.columnStyles;
1832
+ }
1833
+ block.props.cells = c.cells.map(
1834
+ (col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
1835
+ );
1836
+ } else {
1837
+ block.props.preset = asStr(c.preset) ?? block.props.preset;
1838
+ }
1527
1839
  break;
1840
+ }
1528
1841
  default:
1529
1842
  break;
1530
1843
  }
1844
+ const styleObj = s && typeof s === "object" && !Array.isArray(s) ? s : {};
1845
+ const hasDocStyles = Object.keys(styleObj).some((k) => {
1846
+ const v = styleObj[k];
1847
+ return v !== void 0 && v !== null && v !== "";
1848
+ });
1849
+ const skipContentMergeKeys = {
1850
+ heading: /* @__PURE__ */ new Set(["text", "tag"]),
1851
+ text: /* @__PURE__ */ new Set(["html", "text"]),
1852
+ html: /* @__PURE__ */ new Set(["html"]),
1853
+ image: /* @__PURE__ */ new Set(["src", "alt", "link"]),
1854
+ button: /* @__PURE__ */ new Set(["text", "href"]),
1855
+ link: /* @__PURE__ */ new Set(["text", "href"]),
1856
+ social: /* @__PURE__ */ new Set(["networks", "socialUrls"]),
1857
+ table: /* @__PURE__ */ new Set(["data"]),
1858
+ menu: /* @__PURE__ */ new Set(["items"]),
1859
+ video: /* @__PURE__ */ new Set(["url"]),
1860
+ timer: /* @__PURE__ */ new Set(["endDate"])
1861
+ };
1862
+ if (!hasDocStyles && c && typeof c === "object" && t !== "layout") {
1863
+ const known = DEFAULT_BLOCK_PROPS[t];
1864
+ const skip = skipContentMergeKeys[t];
1865
+ if (known && typeof known === "object" && !Array.isArray(known)) {
1866
+ for (const key of Object.keys(known)) {
1867
+ if (skip?.has(key)) continue;
1868
+ if (key in c && c[key] !== void 0) {
1869
+ block.props[key] = c[key];
1870
+ }
1871
+ }
1872
+ }
1873
+ }
1874
+ applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
1531
1875
  return block;
1532
1876
  }
1533
1877
  function rowToInternal(r) {
1534
- const cols = Math.max(1, r.layout?.columns || r.columns?.length || 1);
1878
+ const columns = Array.isArray(r.columns) ? r.columns : [];
1879
+ const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
1880
+ const cols = Math.max(1, columns.length, layoutColCount);
1881
+ const colList = (() => {
1882
+ if (columns.length >= cols) return columns;
1883
+ if (columns.length > 0) {
1884
+ return [
1885
+ ...columns,
1886
+ ...Array.from({ length: cols - columns.length }).map(() => ({}))
1887
+ ];
1888
+ }
1889
+ return Array.from({ length: cols }).map(() => ({}));
1890
+ })();
1535
1891
  const preset = pickPresetByColCount(cols) || LAYOUT_PRESETS[0];
1536
1892
  const row = makeLayoutRow(preset);
1537
1893
  row.id = typeof r.id === "string" ? r.id : uid();
@@ -1544,14 +1900,14 @@ function rowToInternal(r) {
1544
1900
  row.bgSize = r.styles?.backgroundSize || row.bgSize;
1545
1901
  row.bgPosition = r.styles?.backgroundPosition || row.bgPosition || "center";
1546
1902
  row.bgGradient = r.styles?.backgroundGradient || row.bgGradient || null;
1547
- const columns = Array.isArray(r.columns) ? r.columns : [];
1548
- const colList = columns.length ? columns : Array.from({ length: cols }).map(() => ({}));
1549
- row.cells = colList.slice(0, cols).map((c) => {
1903
+ row.cells = colList.map((c) => {
1550
1904
  const blocks = Array.isArray(c.blocks) ? c.blocks : [];
1551
- return blocks.map(mapBlockToInternal).filter((x) => x != null);
1905
+ return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
1552
1906
  });
1553
- const columnStyles = {};
1554
- colList.slice(0, cols).forEach((c, i) => {
1907
+ const studio = r._reactEmailStudio;
1908
+ const snap = studio?.row;
1909
+ const columnStylesFromDoc = {};
1910
+ colList.forEach((c, i) => {
1555
1911
  const bgColor = typeof c.styles?.backgroundColor === "string" ? c.styles.backgroundColor : "";
1556
1912
  const padding = layoutColumnPaddingForDoc(c.styles?.padding);
1557
1913
  const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
@@ -1563,17 +1919,30 @@ function rowToInternal(r) {
1563
1919
  const hasPad = columnPaddingNonZero(padding);
1564
1920
  const hasBr = columnRadiusNonZero(borderRadius);
1565
1921
  if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
1566
- columnStyles[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
1922
+ columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
1567
1923
  }
1568
1924
  });
1569
- if (Object.keys(columnStyles).length) row.columnStyles = columnStyles;
1925
+ if (snap && typeof snap === "object" && !Array.isArray(snap)) {
1926
+ const { cells: _omitCells, columnStyles: snapCs, ...rest } = snap;
1927
+ Object.assign(row, rest);
1928
+ const snapColumn = snapCs && typeof snapCs === "object" && !Array.isArray(snapCs) ? snapCs : {};
1929
+ row.columnStyles = { ...snapColumn, ...columnStylesFromDoc };
1930
+ if (!Object.keys(row.columnStyles).length) delete row.columnStyles;
1931
+ } else if (Object.keys(columnStylesFromDoc).length) {
1932
+ row.columnStyles = columnStylesFromDoc;
1933
+ }
1570
1934
  return row;
1571
1935
  }
1572
1936
  function normalizeEmailDesignInput(input) {
1573
1937
  const doc = normalizeEmailDocument(input);
1574
1938
  if (!doc) return null;
1575
1939
  const s = doc.settings || {};
1576
- const settings = {
1940
+ const studioSettings = s._reactEmailStudio;
1941
+ const settings = {};
1942
+ if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
1943
+ Object.assign(settings, cloneJson(studioSettings.editorSettings));
1944
+ }
1945
+ Object.assign(settings, {
1577
1946
  bgColor: s.backgroundColor || "#f1f5f9",
1578
1947
  bgImage: s.backgroundImage || "",
1579
1948
  bgRepeat: s.backgroundRepeat || "no-repeat",
@@ -1587,15 +1956,15 @@ function normalizeEmailDesignInput(input) {
1587
1956
  contentBgPosition: s.contentBackgroundPosition || "center",
1588
1957
  contentBgGradient: s.contentBackgroundGradient || null,
1589
1958
  contentWidth: typeof s.width === "number" ? s.width : 600,
1590
- padding: 24,
1591
- borderRadius: 8,
1959
+ padding: typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : typeof settings.padding === "number" ? settings.padding : 24,
1960
+ borderRadius: typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : typeof settings.borderRadius === "number" ? settings.borderRadius : 8,
1592
1961
  pageFontFamily: s.fontFamily,
1593
1962
  pageTextColor: s.color,
1594
1963
  pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
1595
1964
  pageResponsive: s.responsive,
1596
1965
  pageRtl: s.rtl,
1597
1966
  fontFamily: s.fontFamily
1598
- };
1967
+ });
1599
1968
  const rows = (doc.rows || []).map(rowToInternal);
1600
1969
  return withHydratedRows({ rows, settings, __emailDocument: doc });
1601
1970
  }
@@ -1619,14 +1988,29 @@ function designToEmailDocument(rows, settings) {
1619
1988
  fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
1620
1989
  lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
1621
1990
  color: settings.pageTextColor || "#111827",
1622
- responsive: true,
1623
- rtl: false
1991
+ responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
1992
+ rtl: !!settings.pageRtl,
1993
+ _reactEmailStudio: {
1994
+ contentPadding: settings.padding ?? 24,
1995
+ contentBorderRadius: settings.borderRadius ?? 8,
1996
+ editorSettings: cloneJson(settings)
1997
+ }
1624
1998
  },
1625
1999
  rows: rows.map((r, ri) => {
1626
- const cols = Math.max(1, r.cols || r.cells?.length || 1);
2000
+ const cellArrays = Array.isArray(r.cells) ? r.cells : [];
2001
+ const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
2002
+ const cols = Math.max(1, cellArrays.length, declaredCols);
2003
+ const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
2004
+ const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
2005
+ return { top: n, right: n, bottom: n, left: n };
2006
+ })();
2007
+ const { cells: _rowCells, ...rowEditorSnap } = r;
1627
2008
  return {
1628
2009
  id: r.id || `row_${ri + 1}`,
1629
2010
  type: "row",
2011
+ _reactEmailStudio: {
2012
+ row: cloneJson(rowEditorSnap)
2013
+ },
1630
2014
  layout: {
1631
2015
  columns: cols,
1632
2016
  gap: r.gap ?? 0,
@@ -1640,13 +2024,14 @@ function designToEmailDocument(rows, settings) {
1640
2024
  backgroundSize: r.bgSize || "cover",
1641
2025
  backgroundPosition: r.bgPosition || "center",
1642
2026
  backgroundGradient: r.bgGradient || null,
1643
- padding: { top: r.padding ?? 0, right: r.padding ?? 0, bottom: r.padding ?? 0, left: r.padding ?? 0 },
2027
+ padding: rowPad,
1644
2028
  borderRadius: 0,
1645
2029
  borderWidth: 0,
1646
2030
  borderColor: "#e5e7eb",
1647
2031
  textAlign: "left"
1648
2032
  },
1649
- columns: (r.cells || []).slice(0, cols).map((cellBlocks, ci) => {
2033
+ columns: Array.from({ length: cols }, (_, ci) => {
2034
+ const cellBlocks = cellArrays[ci] ?? [];
1650
2035
  const cs = r.columnStyles && r.columnStyles[ci] || {};
1651
2036
  return {
1652
2037
  id: `col_${ri + 1}_${ci + 1}`,
@@ -1661,12 +2046,7 @@ function designToEmailDocument(rows, settings) {
1661
2046
  backgroundGradient: cs.bgGradient || null,
1662
2047
  borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
1663
2048
  },
1664
- blocks: (cellBlocks || []).map((b) => ({
1665
- id: b.id,
1666
- type: b.type,
1667
- content: b.props,
1668
- styles: {}
1669
- }))
2049
+ blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
1670
2050
  };
1671
2051
  })
1672
2052
  };
@@ -1674,14 +2054,29 @@ function designToEmailDocument(rows, settings) {
1674
2054
  };
1675
2055
  return doc;
1676
2056
  }
1677
- function hydrateBlock(b) {
2057
+ function hydrateBlock(b, depth = 0) {
1678
2058
  if (!b || typeof b !== "object") return null;
1679
2059
  const t = b.type === "nestedRow" ? "layout" : b.type;
1680
2060
  if (!isKnownBlockType(t)) return null;
1681
2061
  if (t === "layout" && b.props && Array.isArray(b.props.cells)) {
1682
2062
  const defaults2 = DEFAULT_BLOCK_PROPS.layout;
2063
+ if (depth >= MAX_LAYOUT_TREE_DEPTH) {
2064
+ const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? [...b.props.ratios] : [1];
2065
+ return {
2066
+ ...b,
2067
+ type: "layout",
2068
+ id: typeof b.id === "string" && b.id ? b.id : uid(),
2069
+ props: {
2070
+ ...defaults2,
2071
+ ...b.props,
2072
+ bgSize: b.props.bgSize ?? defaults2.bgSize,
2073
+ bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
2074
+ cells: ratios.map(() => [])
2075
+ }
2076
+ };
2077
+ }
1683
2078
  const cells = b.props.cells.map(
1684
- (col) => Array.isArray(col) ? col.map(hydrateBlock).filter((x) => x != null) : []
2079
+ (col) => Array.isArray(col) ? col.map((x) => hydrateBlock(x, depth + 1)).filter((x) => x != null) : []
1685
2080
  );
1686
2081
  return {
1687
2082
  ...b,
@@ -1712,6 +2107,78 @@ function hydrateBlock(b) {
1712
2107
  props: merged
1713
2108
  };
1714
2109
  }
2110
+ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
2111
+ if (!raw || typeof raw !== "object") return null;
2112
+ const r = raw;
2113
+ if (r.props && typeof r.props === "object" && typeof r.type === "string" && !("styles" in r)) {
2114
+ return hydrateBlock(r, layoutDepth);
2115
+ }
2116
+ return mapBlockToInternal(r, layoutDepth);
2117
+ }
2118
+ function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
2119
+ const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
2120
+ if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
2121
+ const a = isObj(fromContent) ? fromContent : {};
2122
+ const b = isObj(fromHydrated) ? fromHydrated : {};
2123
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
2124
+ const out = {};
2125
+ for (const k of keys) {
2126
+ const va = a[k];
2127
+ const vb = b[k];
2128
+ if (isObj(va) && isObj(vb)) {
2129
+ out[k] = { ...va, ...vb };
2130
+ } else if (vb !== void 0) {
2131
+ out[k] = vb;
2132
+ } else {
2133
+ out[k] = va;
2134
+ }
2135
+ }
2136
+ return Object.keys(out).length ? out : void 0;
2137
+ }
2138
+ function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
2139
+ const exp = b.props;
2140
+ if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
2141
+ if (t === "layout" && Array.isArray(exp.cells)) {
2142
+ const columnStylesFromContent = block.props.columnStyles;
2143
+ const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
2144
+ if (hb) {
2145
+ block.props = hb.props;
2146
+ const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
2147
+ if (merged) block.props.columnStyles = merged;
2148
+ else delete block.props.columnStyles;
2149
+ if (typeof hb.id === "string" && hb.id) block.id = hb.id;
2150
+ }
2151
+ return;
2152
+ }
2153
+ Object.assign(block.props, exp);
2154
+ normalizeBoxStyles(block.props, t);
2155
+ if (t === "html" || t === "text") {
2156
+ block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
2157
+ }
2158
+ }
2159
+ function exportEmailDocBlock(b, depth = 0) {
2160
+ if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
2161
+ const doc = internalBlockToEmailDoc(b, depth);
2162
+ const out = { ...doc };
2163
+ if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
2164
+ out.props = cloneJson(b.props);
2165
+ }
2166
+ if (b.type === "layout" && Array.isArray(b.props?.cells)) {
2167
+ const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
2168
+ if (depth >= MAX_LAYOUT_TREE_DEPTH) {
2169
+ const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
2170
+ out.content = { ...baseContent, cells: ratios.map(() => []) };
2171
+ } else {
2172
+ out.content = {
2173
+ ...baseContent,
2174
+ cells: b.props.cells.map(
2175
+ (col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
2176
+ )
2177
+ };
2178
+ }
2179
+ }
2180
+ return out;
2181
+ }
1715
2182
  function hydrateLayoutRow(row) {
1716
2183
  if (!row || typeof row !== "object") return row;
1717
2184
  const cells = (row.cells || []).map(
@@ -4443,32 +4910,42 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4443
4910
  ] });
4444
4911
  case "text":
4445
4912
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4446
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react4.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4447
- TextRichEditor,
4913
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content (HTML)", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4914
+ "textarea",
4448
4915
  {
4916
+ style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
4449
4917
  value: normalizeRichHtmlForStorage(p.content),
4450
- onChange: (html) => set("content", html),
4451
- typography: {
4452
- fontSize: p.fontSize,
4453
- color: p.color,
4454
- align: p.align,
4455
- fontFamily: p.fontFamily,
4456
- lineHeight: p.lineHeight,
4457
- letterSpacing: p.letterSpacing,
4458
- padding: p.padding
4918
+ onChange: (e) => set("content", normalizeRichHtmlForStorage(e.target.value)),
4919
+ spellCheck: false,
4920
+ placeholder: "e.g. <p>Your message</p>"
4921
+ },
4922
+ block.id
4923
+ ) }),
4924
+ mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4925
+ "select",
4926
+ {
4927
+ style: IS,
4928
+ defaultValue: "",
4929
+ onChange: (e) => {
4930
+ const v = e.target.value;
4931
+ if (v) {
4932
+ set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
4933
+ e.target.value = "";
4934
+ }
4459
4935
  },
4460
- mergeTags,
4461
- onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
4462
- placeholder: "Write your message\u2026",
4463
- headerTitle: "Rich text",
4464
- C
4936
+ children: [
4937
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Choose\u2026" }),
4938
+ mergeTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t.value, children: t.name }, t.name))
4939
+ ]
4465
4940
  }
4466
- ) }, block.id) }),
4941
+ ) }) : null,
4467
4942
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
4468
4943
  Col("color", "Color"),
4469
4944
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
4470
4945
  Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
4471
4946
  Sel("fontFamily", "Font", FONTS2),
4947
+ Tog("italic", "Italic"),
4948
+ Tog("underline", "Underline"),
4472
4949
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Line height", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", style: useIS(C).IS, min: 1, max: 4, step: 0.05, value: p.lineHeight ?? 1.65, onChange: (e) => set("lineHeight", +e.target.value) }) }),
4473
4950
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
4474
4951
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
@@ -6039,6 +6516,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6039
6516
  const [previewDevice, setPreviewDevice] = (0, import_react8.useState)("desktop");
6040
6517
  const [activeTab, setActiveTab] = (0, import_react8.useState)("blocks");
6041
6518
  const [templatesOpen, setTemplatesOpen] = (0, import_react8.useState)(false);
6519
+ const [jsonLoading, setJsonLoading] = (0, import_react8.useState)(false);
6042
6520
  const [autoSaveMsg, setAutoSaveMsg] = (0, import_react8.useState)("");
6043
6521
  const [locale, setLocale] = (0, import_react8.useState)(
6044
6522
  typeof opt.locale === "string" ? opt.locale : "en"
@@ -6181,31 +6659,40 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6181
6659
  }, [rows, history, future, selContentMeta]);
6182
6660
  const buildApi = (0, import_react8.useCallback)(() => ({
6183
6661
  loadJson(input) {
6184
- let parsed = input;
6185
- if (typeof input === "string") {
6186
- try {
6187
- parsed = JSON.parse(input);
6188
- } catch {
6189
- return;
6190
- }
6191
- }
6192
- const norm = normalizeEmailDesignInput(parsed);
6193
- if (!norm) return;
6194
- setRows(norm.rows);
6195
- const ns = norm.settings;
6196
- setSettings((s) => ({ ...s, ...ns }));
6197
- const inferPage = () => {
6198
- if (ns?.bgGradient) return "gradient";
6199
- if (String(ns?.bgImage ?? "").trim()) return "image";
6200
- return "solid";
6201
- };
6202
- const inferContent = () => {
6203
- if (ns?.contentBgGradient) return "gradient";
6204
- if (String(ns?.contentBgImage ?? "").trim()) return "image";
6205
- return "solid";
6206
- };
6207
- setPageBgUiStep(inferPage());
6208
- setContentBgUiStep(inferContent());
6662
+ setJsonLoading(true);
6663
+ requestAnimationFrame(() => {
6664
+ requestAnimationFrame(() => {
6665
+ try {
6666
+ let parsed = input;
6667
+ if (typeof input === "string") {
6668
+ try {
6669
+ parsed = JSON.parse(input);
6670
+ } catch {
6671
+ return;
6672
+ }
6673
+ }
6674
+ const norm = normalizeEmailDesignInput(parsed);
6675
+ if (!norm) return;
6676
+ setRows(norm.rows);
6677
+ const ns = norm.settings;
6678
+ setSettings((s) => ({ ...s, ...ns }));
6679
+ const inferPage = () => {
6680
+ if (ns?.bgGradient) return "gradient";
6681
+ if (String(ns?.bgImage ?? "").trim()) return "image";
6682
+ return "solid";
6683
+ };
6684
+ const inferContent = () => {
6685
+ if (ns?.contentBgGradient) return "gradient";
6686
+ if (String(ns?.contentBgImage ?? "").trim()) return "image";
6687
+ return "solid";
6688
+ };
6689
+ setPageBgUiStep(inferPage());
6690
+ setContentBgUiStep(inferContent());
6691
+ } finally {
6692
+ setJsonLoading(false);
6693
+ }
6694
+ });
6695
+ });
6209
6696
  },
6210
6697
  exportJson(cb, pretty) {
6211
6698
  const design = designToEmailDocument(rowsRef.current, settingsRef.current);
@@ -6304,6 +6791,9 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6304
6791
  const nested = action.nested;
6305
6792
  const toLoc = toColumnLoc(rowId, cellIdx, nested);
6306
6793
  if (action.kind === "new") {
6794
+ if ((action.contentType === "layout" || action.contentType === "nestedRow") && nestedLayoutDepth(nested) >= MAX_NESTED_LAYOUT_DEPTH) {
6795
+ return;
6796
+ }
6307
6797
  let nb;
6308
6798
  if (action.preset && (action.contentType === "layout" || action.contentType === "nestedRow")) {
6309
6799
  nb = makeNestedRowBlock(action.preset);
@@ -6589,6 +7079,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6589
7079
  ::-webkit-scrollbar-track{background:transparent;}
6590
7080
  ::-webkit-scrollbar-thumb{background:${C.border};border-radius:3px;}
6591
7081
  ::-webkit-scrollbar-thumb:hover{background:${C.muted};}
7082
+ @keyframes email-editor-json-spin{to{transform:rotate(360deg);}}
6592
7083
  ` }),
6593
7084
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { id: eid("workspace"), style: {
6594
7085
  display: "flex",
@@ -7517,7 +8008,47 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7517
8008
  ) })
7518
8009
  ]
7519
8010
  }
7520
- )
8011
+ ),
8012
+ jsonLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
8013
+ "div",
8014
+ {
8015
+ id: eid("json-loading"),
8016
+ role: "status",
8017
+ "aria-live": "polite",
8018
+ "aria-busy": true,
8019
+ style: {
8020
+ position: "absolute",
8021
+ inset: 0,
8022
+ zIndex: 180,
8023
+ background: `${C.bg}f2`,
8024
+ backdropFilter: "blur(2px)",
8025
+ WebkitBackdropFilter: "blur(2px)",
8026
+ display: "flex",
8027
+ flexDirection: "column",
8028
+ alignItems: "center",
8029
+ justifyContent: "center",
8030
+ gap: 14,
8031
+ pointerEvents: "auto"
8032
+ },
8033
+ children: [
8034
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
8035
+ "div",
8036
+ {
8037
+ "aria-hidden": true,
8038
+ style: {
8039
+ width: 40,
8040
+ height: 40,
8041
+ borderRadius: "50%",
8042
+ border: `3px solid ${C.border}`,
8043
+ borderTopColor: C.accent,
8044
+ animation: "email-editor-json-spin 0.65s linear infinite"
8045
+ }
8046
+ }
8047
+ ),
8048
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, fontWeight: 600, color: C.text }, children: L("loadingDesign") })
8049
+ ]
8050
+ }
8051
+ ) : null
7521
8052
  ] }),
7522
8053
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { id: eid("status-bar"), style: { display: "flex", alignItems: "center", gap: 12, padding: "3px 14px", background: C.toolbarBg, borderTop: `1px solid ${C.border}`, fontSize: 10, color: C.muted, flexShrink: 0, position: "sticky", bottom: 0, zIndex: 100 }, children: [
7523
8054
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [