react-email-studio 3.4.0 → 3.8.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.js CHANGED
@@ -464,9 +464,9 @@ var DEFAULT_BLOCK_PROPS = {
464
464
  fontFamily: "Georgia,serif",
465
465
  margin: { ...BOX0 }
466
466
  },
467
+ /** Rich HTML body lives on the block root as `content` (string), not in `props`. */
467
468
  html: {
468
469
  ...BLOCK_BG,
469
- content: "<p>Rich HTML content. Edit with the rich editor.</p>",
470
470
  fontSize: 15,
471
471
  color: "#1e293b",
472
472
  align: "left",
@@ -486,6 +486,7 @@ var DEFAULT_BLOCK_PROPS = {
486
486
  align: "center",
487
487
  padding: boxAll(8),
488
488
  link: "",
489
+ linkEnabled: false,
489
490
  linkTarget: "_blank",
490
491
  borderRadius: { tl: 0, tr: 0, br: 0, bl: 0 },
491
492
  margin: { top: 0, right: 0, bottom: 0, left: 0 }
@@ -607,10 +608,82 @@ var DEFAULT_BLOCK_PROPS = {
607
608
  margin: { ...BOX0 }
608
609
  }
609
610
  };
611
+ var DEFAULT_HTML_BLOCK_MARKUP = "<p>Rich HTML content. Edit with the rich editor.</p>";
612
+ function imageLinkActive(p) {
613
+ const url = typeof p.link === "string" ? p.link.trim() : "";
614
+ const enabled = p.linkEnabled ?? !!url;
615
+ return enabled && !!url;
616
+ }
610
617
  function isKnownBlockType(type) {
611
618
  return typeof type === "string" && type in DEFAULT_BLOCK_PROPS;
612
619
  }
613
620
 
621
+ // src/lib/columnProps.ts
622
+ var BOX02 = { top: 0, right: 0, bottom: 0, left: 0 };
623
+ var DEFAULT_COLUMN_PROPS = {
624
+ bgColor: "",
625
+ bgImage: "",
626
+ bgGradient: null,
627
+ bgSize: "cover",
628
+ bgRepeat: "no-repeat",
629
+ bgPosition: "center",
630
+ padding: { ...BOX02 },
631
+ borderRadius: 0
632
+ };
633
+ function isObj(x) {
634
+ return x !== null && typeof x === "object" && !Array.isArray(x);
635
+ }
636
+ function colCountFromContainer(c) {
637
+ const cells = Array.isArray(c.cells) ? c.cells.length : 0;
638
+ const ratios = Array.isArray(c.ratios) ? c.ratios.length : 0;
639
+ const cols = typeof c.cols === "number" && c.cols > 0 ? c.cols : 0;
640
+ return Math.max(1, cells, ratios, cols);
641
+ }
642
+ function columnsFromLegacyMap(map, count) {
643
+ const n = Math.max(1, count);
644
+ const src = isObj(map) ? map : {};
645
+ return Array.from({ length: n }, (_, i) => {
646
+ const raw = src[i] ?? src[String(i)];
647
+ return { props: { ...DEFAULT_COLUMN_PROPS, ...isObj(raw) ? raw : {} } };
648
+ });
649
+ }
650
+ function getColumns(container) {
651
+ const count = colCountFromContainer(container);
652
+ if (Array.isArray(container.columns)) {
653
+ const cols = container.columns;
654
+ return Array.from({ length: count }, (_, i) => {
655
+ const entry = cols[i];
656
+ if (isObj(entry) && isObj(entry.props)) {
657
+ return { props: { ...DEFAULT_COLUMN_PROPS, ...entry.props } };
658
+ }
659
+ if (isObj(entry)) {
660
+ return { props: { ...DEFAULT_COLUMN_PROPS, ...entry } };
661
+ }
662
+ return { props: { ...DEFAULT_COLUMN_PROPS } };
663
+ });
664
+ }
665
+ if (container.columnStyles) {
666
+ return columnsFromLegacyMap(container.columnStyles, count);
667
+ }
668
+ return columnsFromLegacyMap({}, count);
669
+ }
670
+ function getColumnPropsAt(container, index) {
671
+ const cols = getColumns(container);
672
+ const i = Math.max(0, Math.min(index, cols.length - 1));
673
+ return cols[i]?.props ?? { ...DEFAULT_COLUMN_PROPS };
674
+ }
675
+ function patchColumnPropsAt(container, index, patch) {
676
+ const cols = getColumns(container);
677
+ const i = Math.max(0, index);
678
+ while (cols.length <= i) cols.push({ props: { ...DEFAULT_COLUMN_PROPS } });
679
+ cols[i] = { props: { ...cols[i].props, ...patch } };
680
+ return cols;
681
+ }
682
+ function withColumnsOnly(row, columns) {
683
+ const { columnStyles: _drop, ...rest } = row;
684
+ return { ...rest, columns };
685
+ }
686
+
614
687
  // src/lib/factories.ts
615
688
  function uid() {
616
689
  return Math.random().toString(36).slice(2, 10);
@@ -650,14 +723,30 @@ function makeNestedRowBlock(preset) {
650
723
  bgRepeat: row.bgRepeat ?? "no-repeat",
651
724
  bgPosition: row.bgPosition ?? "center",
652
725
  bgGradient: row.bgGradient ?? null,
653
- columnStyles: row.columnStyles,
726
+ columns: row.columns ?? row.cells?.map(() => ({ props: { ...DEFAULT_COLUMN_PROPS } })),
654
727
  cells: row.cells
655
728
  }
656
729
  };
657
730
  }
658
731
  function makeContentBlock(type) {
732
+ if (type === "html") {
733
+ return {
734
+ id: uid(),
735
+ type,
736
+ content: DEFAULT_HTML_BLOCK_MARKUP,
737
+ props: { ...DEFAULT_BLOCK_PROPS.html, content: DEFAULT_HTML_BLOCK_MARKUP }
738
+ };
739
+ }
659
740
  return { id: uid(), type, props: { ...DEFAULT_BLOCK_PROPS[type] } };
660
741
  }
742
+ function makeRootContentRow(contentType, preset) {
743
+ const row = makeLayoutRow(preset);
744
+ row.gap = 0;
745
+ row.padding = 0;
746
+ const block = makeContentBlock(contentType);
747
+ row.cells[0] = [block];
748
+ return { row, block };
749
+ }
661
750
 
662
751
  // src/lib/columnPath.ts
663
752
  var MAX_NESTED_LAYOUT_DEPTH = 1;
@@ -667,6 +756,13 @@ function nestedLayoutDepth(nested) {
667
756
  function isSplitLayoutBlock(b) {
668
757
  return b && b.type === "layout" && b.props && Array.isArray(b.props.cells);
669
758
  }
759
+ function isRootContentRow(row) {
760
+ if (!row?.cells || row.cells.length !== 1) return false;
761
+ const col = row.cells[0];
762
+ if (!Array.isArray(col) || col.length !== 1) return false;
763
+ const b = col[0];
764
+ return !!b && typeof b.type === "string" && b.type !== "layout" && b.type !== "nestedRow";
765
+ }
670
766
  function getColumnBlocks(rows, loc) {
671
767
  const r = rows.find((x) => x.id === loc.rowId);
672
768
  if (!r) return [];
@@ -732,56 +828,6 @@ function countBlocksInDesign(rows) {
732
828
  return rows.reduce((sum, r) => sum + r.cells.reduce((s, c) => s + colBlocks(c), 0), 0);
733
829
  }
734
830
 
735
- // src/socialIcons.tsx
736
- import { jsx as jsx2 } from "react/jsx-runtime";
737
- var GLYPHS = {
738
- facebook: "M24 12.073C24 5.446 18.627 0 12 0S0 5.446 0 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",
739
- /** X (formerly Twitter) — Simple Icons “X” */
740
- twitter: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
741
- instagram: "M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.98-6.98.072-1.28.07-1.689.07-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z",
742
- linkedin: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",
743
- youtube: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
744
- pinterest: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.966-2.901 2.165-2.901 1.021 0 1.512.765 1.512 1.682 0 1.025-.653 2.555-.99 3.978-.281 1.189.597 2.159 1.775 2.159 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.698-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026z",
745
- tiktok: "M19.59 6.69a4.83 4.83 0 01-3.77-4.245V2h-3.45v13.672a2.896 2.896 0 01-5.201 1.743 2.895 2.895 0 012.31-4.629 2.89 2.89 0 01.878.14V9.4a6.84 6.84 0 00-1-.05A6.33 6.33 0 005 20.1a6.34 6.34 0 0010.86-4.43v-7.36a8.16 8.16 0 004.77 1.52v-3.4a4.85 4.85 0 01-1-.1z",
746
- snapchat: "M12 0C5.373 0 0 5.372 0 12c0 4.991 3.657 9.128 8.438 9.879-.118-.949-.227-2.422.045-3.461.222-.95 1.444-6.037 1.444-6.037s-.367-.744-.367-1.844c0-1.726.999-3.012 2.243-3.012 1.058 0 1.569.795 1.569 1.748 0 1.065-.679 2.653-1.029 4.13-.292 1.237.618 2.246 1.835 2.246 2.203 0 3.895-2.323 3.895-5.67 0-2.965-2.129-5.038-5.167-5.038-3.52 0-5.585 2.643-5.585 5.589 0 1.106.425 2.295.956 2.943a.77.77 0 01.194.569c-.21.997-.679 3.135-.769 3.568-.121.588-.402.712-.929.431-3.485-1.624-5.667-6.731-5.667-10.839C2.5 6.506 6.847 2.344 12.001 2.344c4.391 0 7.809 2.776 7.809 6.586 0 4.308-2.694 7.635-6.369 7.635-1.271 0-2.464-.677-2.871-1.475 0 0-.669 2.562-.828 3.193-.301 1.301-1.12 2.929-1.668 3.922.125.047.256.07.395.07 1 0 1.826-.827 2.13-1.856.169-.617.982-3.857.982-3.857z",
747
- github: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",
748
- discord: "M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"
749
- };
750
- function iconFillForNetwork(network) {
751
- return network === "snapchat" ? "#000000" : "#ffffff";
752
- }
753
- function glyphPath(network) {
754
- return GLYPHS[network] || null;
755
- }
756
- function socialIconSvgString(network, pixelSize) {
757
- const d = glyphPath(network);
758
- const fill = iconFillForNetwork(network);
759
- const s = Math.max(8, Math.round(pixelSize));
760
- if (!d) {
761
- const letter = (network && network[0] ? network[0] : "?").toUpperCase();
762
- return `<span style="font-size:${Math.floor(s * 0.44)}px;font-weight:800;line-height:1;">${letter}</span>`;
763
- }
764
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="${s}" height="${s}" style="display:block;flex-shrink:0;" aria-hidden="true"><path fill="${fill}" d="${d}"/></svg>`;
765
- }
766
- function SocialGlyph({ network, size }) {
767
- const d = glyphPath(network);
768
- const s = Math.max(8, Math.round(size));
769
- if (!d) {
770
- return /* @__PURE__ */ jsx2("span", { style: { fontSize: Math.floor(s * 0.44), fontWeight: 800, lineHeight: 1 }, children: (network && network[0] ? network[0] : "?").toUpperCase() });
771
- }
772
- return /* @__PURE__ */ jsx2(
773
- "svg",
774
- {
775
- width: s,
776
- height: s,
777
- viewBox: "0 0 24 24",
778
- style: { display: "block", flexShrink: 0 },
779
- "aria-hidden": true,
780
- children: /* @__PURE__ */ jsx2("path", { fill: "currentColor", d })
781
- }
782
- );
783
- }
784
-
785
831
  // src/lib/htmlUtils.ts
786
832
  function escHtmlAttr(s) {
787
833
  return String(s ?? "").replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -823,7 +869,7 @@ function videoUrlToEmbedSrc(url) {
823
869
  if (vi) return `https://player.vimeo.com/video/${vi[1]}`;
824
870
  return url;
825
871
  }
826
- var EMPTY_RICH_HTML = "<p></p>";
872
+ var EMPTY_RICH_HTML = "";
827
873
  function unwrapAllDivElements(container) {
828
874
  let el;
829
875
  while (el = container.querySelector("div")) {
@@ -850,6 +896,161 @@ function normalizeRichHtmlForStorage(html) {
850
896
  function isEffectivelyEmptyRichHtml(html) {
851
897
  return normalizeRichHtmlForStorage(html) === EMPTY_RICH_HTML;
852
898
  }
899
+ var RICH_HTML_CONTENT_CSS = `
900
+ .email-rich-html-content ul,
901
+ .email-rich-html-content ol {
902
+ margin: 0.35em 0;
903
+ padding-left: 1.35rem;
904
+ list-style-position: outside;
905
+ }
906
+ .email-rich-html-content ul { list-style-type: disc; }
907
+ .email-rich-html-content ol { list-style-type: decimal; }
908
+ .email-rich-html-content li { margin: 0.2em 0; }
909
+ .email-rich-html-content li > p { margin: 0; }
910
+ .email-rich-html-content p { margin: 0.35em 0; }
911
+ .email-rich-html-content h2,
912
+ .email-rich-html-content h3,
913
+ .email-rich-html-content h4 {
914
+ margin: 0.5em 0 0.25em;
915
+ line-height: 1.25;
916
+ font-weight: 700;
917
+ }
918
+ .email-rich-html-content blockquote {
919
+ margin: 0.35em 0;
920
+ padding-left: 0.75rem;
921
+ border-left: 3px solid #cbd5e1;
922
+ }
923
+ .email-rich-html-content hr {
924
+ border: none;
925
+ border-top: 1px solid #e2e8f0;
926
+ margin: 0.75em 0;
927
+ }
928
+ .email-rich-html-content code {
929
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
930
+ font-size: 0.92em;
931
+ background: rgba(0, 0, 0, 0.06);
932
+ padding: 0.1em 0.35em;
933
+ border-radius: 3px;
934
+ }
935
+ .email-rich-html-inner ul,
936
+ .email-rich-html-inner ol {
937
+ margin: 0.35em 0;
938
+ padding-left: 1.35rem;
939
+ list-style-position: outside;
940
+ }
941
+ .email-rich-html-inner ul { list-style-type: disc; }
942
+ .email-rich-html-inner ol { list-style-type: decimal; }
943
+ .email-rich-html-inner li { margin: 0.2em 0; }
944
+ .email-rich-html-inner li > p { margin: 0; }
945
+ .email-rich-html-inner p { margin: 0.35em 0; }
946
+ .email-rich-html-inner h2,
947
+ .email-rich-html-inner h3,
948
+ .email-rich-html-inner h4 {
949
+ margin: 0.5em 0 0.25em;
950
+ line-height: 1.25;
951
+ font-weight: 700;
952
+ }
953
+ .email-rich-html-inner blockquote {
954
+ margin: 0.35em 0;
955
+ padding-left: 0.75rem;
956
+ border-left: 3px solid #cbd5e1;
957
+ }
958
+ .email-rich-html-inner hr {
959
+ border: none;
960
+ border-top: 1px solid #e2e8f0;
961
+ margin: 0.75em 0;
962
+ }
963
+ `.trim();
964
+ function applyInlineListStyles(root) {
965
+ root.querySelectorAll("ul").forEach((ul) => {
966
+ const el = ul;
967
+ el.style.margin = el.style.margin || "0.35em 0";
968
+ el.style.paddingLeft = el.style.paddingLeft || "1.35rem";
969
+ el.style.listStyleType = el.style.listStyleType || "disc";
970
+ el.style.listStylePosition = el.style.listStylePosition || "outside";
971
+ });
972
+ root.querySelectorAll("ol").forEach((ol) => {
973
+ const el = ol;
974
+ el.style.margin = el.style.margin || "0.35em 0";
975
+ el.style.paddingLeft = el.style.paddingLeft || "1.35rem";
976
+ el.style.listStyleType = el.style.listStyleType || "decimal";
977
+ el.style.listStylePosition = el.style.listStylePosition || "outside";
978
+ });
979
+ root.querySelectorAll("li").forEach((li) => {
980
+ const el = li;
981
+ if (!el.style.margin) el.style.margin = "0.2em 0";
982
+ });
983
+ root.querySelectorAll("li > p").forEach((p) => {
984
+ const el = p;
985
+ if (!el.style.margin) el.style.margin = "0";
986
+ });
987
+ }
988
+ function enhanceRichHtmlForEmail(html) {
989
+ const raw = String(html ?? "").trim();
990
+ if (!raw || raw === EMPTY_RICH_HTML) return raw || EMPTY_RICH_HTML;
991
+ if (typeof document === "undefined") return raw;
992
+ const tmp = document.createElement("div");
993
+ tmp.innerHTML = raw;
994
+ if (!tmp.querySelector("ul,ol,blockquote,h2,h3,h4,hr")) return raw;
995
+ applyInlineListStyles(tmp);
996
+ tmp.querySelectorAll("blockquote").forEach((bq) => {
997
+ const el = bq;
998
+ if (!el.style.margin) el.style.margin = "0.35em 0";
999
+ if (!el.style.paddingLeft) el.style.paddingLeft = "0.75rem";
1000
+ if (!el.style.borderLeft) el.style.borderLeft = "3px solid #cbd5e1";
1001
+ });
1002
+ return tmp.innerHTML.trim();
1003
+ }
1004
+
1005
+ // src/socialIcons.tsx
1006
+ import { jsx as jsx2 } from "react/jsx-runtime";
1007
+ var GLYPHS = {
1008
+ facebook: "M24 12.073C24 5.446 18.627 0 12 0S0 5.446 0 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",
1009
+ /** X (formerly Twitter) — Simple Icons “X” */
1010
+ twitter: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
1011
+ instagram: "M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.98-6.98.072-1.28.07-1.689.07-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z",
1012
+ linkedin: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",
1013
+ youtube: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z",
1014
+ pinterest: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.966-2.901 2.165-2.901 1.021 0 1.512.765 1.512 1.682 0 1.025-.653 2.555-.99 3.978-.281 1.189.597 2.159 1.775 2.159 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.402.165-1.495-.698-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026z",
1015
+ tiktok: "M19.59 6.69a4.83 4.83 0 01-3.77-4.245V2h-3.45v13.672a2.896 2.896 0 01-5.201 1.743 2.895 2.895 0 012.31-4.629 2.89 2.89 0 01.878.14V9.4a6.84 6.84 0 00-1-.05A6.33 6.33 0 005 20.1a6.34 6.34 0 0010.86-4.43v-7.36a8.16 8.16 0 004.77 1.52v-3.4a4.85 4.85 0 01-1-.1z",
1016
+ snapchat: "M12 0C5.373 0 0 5.372 0 12c0 4.991 3.657 9.128 8.438 9.879-.118-.949-.227-2.422.045-3.461.222-.95 1.444-6.037 1.444-6.037s-.367-.744-.367-1.844c0-1.726.999-3.012 2.243-3.012 1.058 0 1.569.795 1.569 1.748 0 1.065-.679 2.653-1.029 4.13-.292 1.237.618 2.246 1.835 2.246 2.203 0 3.895-2.323 3.895-5.67 0-2.965-2.129-5.038-5.167-5.038-3.52 0-5.585 2.643-5.585 5.589 0 1.106.425 2.295.956 2.943a.77.77 0 01.194.569c-.21.997-.679 3.135-.769 3.568-.121.588-.402.712-.929.431-3.485-1.624-5.667-6.731-5.667-10.839C2.5 6.506 6.847 2.344 12.001 2.344c4.391 0 7.809 2.776 7.809 6.586 0 4.308-2.694 7.635-6.369 7.635-1.271 0-2.464-.677-2.871-1.475 0 0-.669 2.562-.828 3.193-.301 1.301-1.12 2.929-1.668 3.922.125.047.256.07.395.07 1 0 1.826-.827 2.13-1.856.169-.617.982-3.857.982-3.857z",
1017
+ github: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",
1018
+ discord: "M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"
1019
+ };
1020
+ function iconFillForNetwork(network) {
1021
+ return network === "snapchat" ? "#000000" : "#ffffff";
1022
+ }
1023
+ function glyphPath(network) {
1024
+ return GLYPHS[network] || null;
1025
+ }
1026
+ function socialIconSvgString(network, pixelSize) {
1027
+ const d = glyphPath(network);
1028
+ const fill = iconFillForNetwork(network);
1029
+ const s = Math.max(8, Math.round(pixelSize));
1030
+ if (!d) {
1031
+ const letter = (network && network[0] ? network[0] : "?").toUpperCase();
1032
+ return `<span style="font-size:${Math.floor(s * 0.44)}px;font-weight:800;line-height:1;">${letter}</span>`;
1033
+ }
1034
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="${s}" height="${s}" style="display:block;flex-shrink:0;" aria-hidden="true"><path fill="${fill}" d="${d}"/></svg>`;
1035
+ }
1036
+ function SocialGlyph({ network, size }) {
1037
+ const d = glyphPath(network);
1038
+ const s = Math.max(8, Math.round(size));
1039
+ if (!d) {
1040
+ return /* @__PURE__ */ jsx2("span", { style: { fontSize: Math.floor(s * 0.44), fontWeight: 800, lineHeight: 1 }, children: (network && network[0] ? network[0] : "?").toUpperCase() });
1041
+ }
1042
+ return /* @__PURE__ */ jsx2(
1043
+ "svg",
1044
+ {
1045
+ width: s,
1046
+ height: s,
1047
+ viewBox: "0 0 24 24",
1048
+ style: { display: "block", flexShrink: 0 },
1049
+ "aria-hidden": true,
1050
+ children: /* @__PURE__ */ jsx2("path", { fill: "currentColor", d })
1051
+ }
1052
+ );
1053
+ }
853
1054
 
854
1055
  // src/lib/blockBackground.ts
855
1056
  function linearGradientCssInner(g) {
@@ -974,19 +1175,23 @@ function blockToHtml(cb) {
974
1175
  case "text": {
975
1176
  const shell = emailSurfaceBgCss(p);
976
1177
  const inner = `font-size:${lenPx(p.fontSize)};color:${p.color};text-align:${p.align};font-weight:${p.fontWeight || 400};font-style:${p.italic ? "italic" : "normal"};text-decoration:${p.underline ? "underline" : "none"};line-height:${lh(p.lineHeight)};letter-spacing:${lenPx(p.letterSpacing)};font-family:${p.fontFamily || "Georgia,serif"}`;
977
- return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div style="${inner}">${p.content}</div></div>`;
1178
+ const content = typeof p.content === "string" ? p.content : "";
1179
+ const body = /^\s*<[^>]+>/.test(content) ? content : escHtml(content).replace(/\r?\n/g, "<br/>");
1180
+ return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div style="${inner}">${body}</div></div>`;
978
1181
  }
979
1182
  case "html": {
980
1183
  const shell = emailSurfaceBgCss(p);
981
1184
  const inner = `font-size:${lenPx(p.fontSize)};color:${p.color};text-align:${p.align};line-height:${lh(p.lineHeight)};letter-spacing:${lenPx(p.letterSpacing)};font-family:${p.fontFamily || "Georgia,serif"}`;
982
- return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div style="${inner}">${p.content || "<p></p>"}</div></div>`;
1185
+ const bodyRaw = typeof cb.content === "string" ? cb.content : typeof p.content === "string" ? p.content : "";
1186
+ const body = enhanceRichHtmlForEmail(bodyRaw || "");
1187
+ return `<div style="${pd(p.padding)};${marginCss()}${shell}"><div class="email-rich-html-inner" style="${inner}">${body}</div></div>`;
983
1188
  }
984
1189
  case "image": {
985
1190
  const of = typeof p.objectFit === "string" && p.objectFit.trim() ? p.objectFit.trim() : "cover";
986
1191
  const op = typeof p.objectPosition === "string" && p.objectPosition.trim() ? p.objectPosition.trim() : "center";
987
1192
  const img = `<img src="${p.src}" alt="${p.alt || ""}" style="max-width:${p.width};width:${p.width || "100%"};display:inline-block;border-radius:${radiusPx(p.borderRadius)};object-fit:${of};object-position:${op};"/>`;
988
1193
  const shell = emailSurfaceBgCss(p);
989
- return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${p.link ? `<a href="${p.link}" target="${p.linkTarget || "_blank"}">${img}</a>` : img}</div>`;
1194
+ return `<div style="${pd(p.padding)};text-align:${p.align};${marginCss()}${shell}">${imageLinkActive(p) ? `<a href="${escHtmlAttr(p.link)}" target="${escHtmlAttr(p.linkTarget || "_blank")}">${img}</a>` : img}</div>`;
990
1195
  }
991
1196
  case "button": {
992
1197
  const bd = emailBackdropBgCss(p);
@@ -1075,7 +1280,7 @@ function blockToHtml(cb) {
1075
1280
  bgRepeat: p.bgRepeat || "no-repeat",
1076
1281
  bgPosition: typeof p.bgPosition === "string" ? p.bgPosition : "center",
1077
1282
  bgGradient: p.bgGradient ?? null,
1078
- columnStyles: p.columnStyles,
1283
+ columns: getColumns(p),
1079
1284
  cells: p.cells || []
1080
1285
  };
1081
1286
  return rowToHtml(pseudo);
@@ -1089,7 +1294,7 @@ function rowToHtml(row) {
1089
1294
  const shellBg = emailSurfaceBgCss(row);
1090
1295
  const inner = row.cells.length === 1 ? row.cells[0].map(blockToHtml).join("") : `<div class="email-row-flex" style="display:flex;flex-direction:row;flex-wrap:nowrap;align-items:flex-start;gap:${row.gap ?? 12}px;width:100%;max-width:100%;box-sizing:border-box;">${row.cells.map((cell, i) => {
1091
1296
  const r = row.ratios[i] ?? 1;
1092
- const cs = row.columnStyles && row.columnStyles[i] ? row.columnStyles[i] : {};
1297
+ const cs = getColumnPropsAt(row, i);
1093
1298
  const colShell = emailSurfaceBgCss(cs);
1094
1299
  const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
1095
1300
  const o = cs.padding;
@@ -1137,6 +1342,7 @@ table[role="presentation"] td{min-width:0!important;word-break:break-word;}
1137
1342
  @media screen and (max-width:480px){
1138
1343
  .email-content-td{padding-left:max(10px,env(safe-area-inset-left))!important;padding-right:max(10px,env(safe-area-inset-right))!important;}
1139
1344
  }
1345
+ ${RICH_HTML_CONTENT_CSS}
1140
1346
  </style>`;
1141
1347
  function previewEmailSrcDoc(html, viewportWidthPx) {
1142
1348
  const viewportTag = `<meta name="viewport" content="width=${viewportWidthPx},initial-scale=1"/>`;
@@ -1188,6 +1394,8 @@ function designToHtml(rows, settings, opts = {}) {
1188
1394
  return `background-image:linear-gradient(${angle}deg, ${pairs.join(", ")});background-repeat:no-repeat;background-position:${pagePos};background-size:cover;`;
1189
1395
  })();
1190
1396
  const pageBgImgRaw = typeof settings.bgImage === "string" ? String(settings.bgImage).trim() : "";
1397
+ const pageBgColorRaw = typeof settings.bgColor === "string" ? String(settings.bgColor).trim() : "";
1398
+ const pageBgColor = pageBgColorRaw ? pageBgColorRaw : "transparent";
1191
1399
  const pageBgImgCss = !pageGradCss && pageBgImgRaw ? `background-image:url('${pageBgImgRaw.replace(/'/g, "%27")}');background-size:${settings.bgSize || "cover"};background-repeat:${settings.bgRepeat || "no-repeat"};background-position:${pagePos};` : "";
1192
1400
  const contentPos = typeof settings.contentBgPosition === "string" && settings.contentBgPosition.trim() ? settings.contentBgPosition.trim() : "center";
1193
1401
  const contentGrad = settings.contentBgGradient && typeof settings.contentBgGradient === "object" ? settings.contentBgGradient : null;
@@ -1210,8 +1418,8 @@ function designToHtml(rows, settings, opts = {}) {
1210
1418
  <head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/>
1211
1419
  <title>Email</title>${RESPONSIVE_EMAIL_CSS}${css}
1212
1420
  </head>
1213
- <body style="margin:0;padding:0;background:${settings.bgColor || "#f1f5f9"};${pageGradCss || pageBgImgCss}">
1214
- <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${settings.bgColor || "#f1f5f9"};${pageGradCss || pageBgImgCss}">
1421
+ <body style="margin:0;padding:0;background:${pageBgColor};${pageGradCss || pageBgImgCss}">
1422
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="width:100%;max-width:100%;background-color:${pageBgColor};${pageGradCss || pageBgImgCss}">
1215
1423
  <tr><td align="center" style="padding:0;max-width:100%;">
1216
1424
  <table class="email-shell-table" role="presentation" width="100%" cellpadding="0" cellspacing="0"
1217
1425
  style="${contentShellStyle.join(";")}">
@@ -1245,14 +1453,49 @@ function paddingToUniform(p, fallback = 0) {
1245
1453
  function ensureId(id, prefix) {
1246
1454
  return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
1247
1455
  }
1456
+ function normalizeDocBlock(b, prefix) {
1457
+ const raw = b && typeof b === "object" && !Array.isArray(b) ? b : {};
1458
+ const base = {
1459
+ id: ensureId(raw.id, prefix),
1460
+ type: typeof raw.type === "string" ? raw.type : "text",
1461
+ content: typeof raw.content === "string" ? raw.content : raw.content && typeof raw.content === "object" && !Array.isArray(raw.content) ? raw.content : {},
1462
+ ...raw.behavior && typeof raw.behavior === "object" && !Array.isArray(raw.behavior) ? { behavior: raw.behavior } : {},
1463
+ ...raw.responsive && typeof raw.responsive === "object" && !Array.isArray(raw.responsive) ? { responsive: raw.responsive } : {}
1464
+ };
1465
+ if (raw.props && typeof raw.props === "object" && !Array.isArray(raw.props)) {
1466
+ base.props = { ...raw.props };
1467
+ } else if (raw.styles && typeof raw.styles === "object" && !Array.isArray(raw.styles)) {
1468
+ base.styles = { ...raw.styles };
1469
+ }
1470
+ return base;
1471
+ }
1472
+ function normalizeDocSettings(settings) {
1473
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) return {};
1474
+ const s = { ...settings };
1475
+ delete s._reactEmailStudio;
1476
+ return s;
1477
+ }
1248
1478
  function normalizeEmailDocument(input) {
1249
1479
  if (input == null || typeof input !== "object" || Array.isArray(input)) return null;
1250
1480
  const doc = input;
1251
1481
  if (doc.type !== "email_document") return null;
1482
+ const rawBlocks = Array.isArray(doc.blocks) ? doc.blocks : [];
1483
+ const useBlocks = rawBlocks.length > 0;
1252
1484
  const rows = Array.isArray(doc.rows) ? doc.rows : [];
1253
- return {
1485
+ const base = {
1254
1486
  type: "email_document",
1255
- settings: doc.settings && typeof doc.settings === "object" && !Array.isArray(doc.settings) ? doc.settings : {},
1487
+ settings: normalizeDocSettings(doc.settings),
1488
+ globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
1489
+ responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
1490
+ };
1491
+ if (useBlocks) {
1492
+ return {
1493
+ ...base,
1494
+ blocks: rawBlocks.map((b, bi) => normalizeDocBlock(b, `block${bi + 1}`))
1495
+ };
1496
+ }
1497
+ return {
1498
+ ...base,
1256
1499
  rows: rows.map((r, ri) => {
1257
1500
  const columns = Array.isArray(r.columns) ? r.columns : [];
1258
1501
  const layoutColCount = typeof r.layout?.columns === "number" && r.layout.columns > 0 ? Math.floor(r.layout.columns) : 0;
@@ -1274,28 +1517,76 @@ function normalizeEmailDocument(input) {
1274
1517
  columns: colCount,
1275
1518
  gap: typeof r.layout?.gap === "number" ? r.layout.gap : 0,
1276
1519
  stackOnMobile: r.layout?.stackOnMobile !== false,
1277
- align: r.layout?.align || "center"
1520
+ align: r.layout?.align || "center",
1521
+ ...typeof r.layout?.preset === "string" && r.layout.preset.trim() ? { preset: r.layout.preset.trim() } : {},
1522
+ ...Array.isArray(r.layout?.ratios) && r.layout.ratios.length ? {
1523
+ ratios: r.layout.ratios.filter(
1524
+ (x) => typeof x === "number" && Number.isFinite(x)
1525
+ )
1526
+ } : {}
1278
1527
  },
1279
- styles: r.styles && typeof r.styles === "object" && !Array.isArray(r.styles) ? r.styles : {},
1280
- ...typeof r._reactEmailStudio === "object" && r._reactEmailStudio !== null && !Array.isArray(r._reactEmailStudio) ? { _reactEmailStudio: r._reactEmailStudio } : {},
1528
+ props: (() => {
1529
+ const p = r.props;
1530
+ if (p && typeof p === "object" && !Array.isArray(p)) return { ...p };
1531
+ const s = r.styles;
1532
+ if (s && typeof s === "object" && !Array.isArray(s)) {
1533
+ return {
1534
+ bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
1535
+ bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
1536
+ bgRepeat: s.backgroundRepeat || "no-repeat",
1537
+ bgSize: s.backgroundSize || "cover",
1538
+ bgPosition: s.backgroundPosition || "center",
1539
+ bgGradient: s.backgroundGradient ?? null,
1540
+ padding: s.padding,
1541
+ borderRadius: s.borderRadius,
1542
+ borderWidth: s.borderWidth,
1543
+ borderColor: s.borderColor,
1544
+ textAlign: s.textAlign
1545
+ };
1546
+ }
1547
+ return {};
1548
+ })(),
1281
1549
  columns: colsNormalized.map((c, ci) => ({
1282
1550
  id: ensureId(c.id, `col${ri + 1}_${ci + 1}`),
1283
1551
  layout: c.layout && typeof c.layout === "object" && !Array.isArray(c.layout) ? c.layout : {},
1284
- styles: c.styles && typeof c.styles === "object" && !Array.isArray(c.styles) ? c.styles : {},
1285
- blocks: Array.isArray(c.blocks) ? c.blocks.map((b, bi) => ({
1286
- ...b,
1287
- id: ensureId(b?.id, `b${ri + 1}_${ci + 1}_${bi + 1}`),
1288
- content: b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
1289
- styles: b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles) ? b.styles : {},
1290
- behavior: b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? b.behavior : {},
1291
- responsive: b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? b.responsive : {},
1292
- ...b?.props && typeof b.props === "object" && !Array.isArray(b.props) ? { props: b.props } : {}
1293
- })) : []
1552
+ ...(() => {
1553
+ const p = c.props;
1554
+ if (p && typeof p === "object" && !Array.isArray(p)) return { props: { ...p } };
1555
+ const s = c.styles;
1556
+ if (s && typeof s === "object" && !Array.isArray(s)) {
1557
+ return {
1558
+ props: {
1559
+ bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
1560
+ bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
1561
+ bgRepeat: s.backgroundRepeat || "no-repeat",
1562
+ bgSize: s.backgroundSize || "cover",
1563
+ bgPosition: s.backgroundPosition || "center",
1564
+ bgGradient: s.backgroundGradient ?? null,
1565
+ padding: s.padding,
1566
+ borderRadius: s.borderRadius
1567
+ }
1568
+ };
1569
+ }
1570
+ return {};
1571
+ })(),
1572
+ blocks: Array.isArray(c.blocks) ? c.blocks.map((b, bi) => {
1573
+ const base2 = {
1574
+ id: ensureId(b?.id, `b${ri + 1}_${ci + 1}_${bi + 1}`),
1575
+ type: typeof b?.type === "string" ? b.type : "text",
1576
+ content: typeof b?.content === "string" ? b.content : b?.content && typeof b.content === "object" && !Array.isArray(b.content) ? b.content : {},
1577
+ ...b?.behavior && typeof b.behavior === "object" && !Array.isArray(b.behavior) ? { behavior: b.behavior } : {},
1578
+ ...b?.responsive && typeof b.responsive === "object" && !Array.isArray(b.responsive) ? { responsive: b.responsive } : {}
1579
+ };
1580
+ if (b?.props && typeof b.props === "object" && !Array.isArray(b.props)) {
1581
+ base2.props = { ...b.props };
1582
+ } else if (b?.styles && typeof b.styles === "object" && !Array.isArray(b.styles)) {
1583
+ base2.styles = { ...b.styles };
1584
+ }
1585
+ return base2;
1586
+ }) : []
1294
1587
  }))
1295
1588
  };
1296
- }),
1297
- globalStyles: doc.globalStyles && typeof doc.globalStyles === "object" && !Array.isArray(doc.globalStyles) ? doc.globalStyles : void 0,
1298
- responsive: doc.responsive && typeof doc.responsive === "object" && !Array.isArray(doc.responsive) ? doc.responsive : void 0
1589
+ })
1299
1590
  };
1300
1591
  }
1301
1592
 
@@ -1308,6 +1599,60 @@ function cloneJson(v) {
1308
1599
  return v;
1309
1600
  }
1310
1601
  }
1602
+ function legacyRowStylesToProps(s) {
1603
+ if (!s || typeof s !== "object" || Array.isArray(s)) return {};
1604
+ return {
1605
+ bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
1606
+ bgImage: typeof s.backgroundImage === "string" ? s.backgroundImage : "",
1607
+ bgRepeat: s.backgroundRepeat || "no-repeat",
1608
+ bgSize: s.backgroundSize || "cover",
1609
+ bgPosition: s.backgroundPosition || "center",
1610
+ bgGradient: s.backgroundGradient ?? null,
1611
+ padding: s.padding,
1612
+ borderRadius: s.borderRadius,
1613
+ borderWidth: s.borderWidth,
1614
+ borderColor: s.borderColor,
1615
+ textAlign: s.textAlign
1616
+ };
1617
+ }
1618
+ function rowPropsFromDocRow(r) {
1619
+ const p = r.props;
1620
+ if (p && typeof p === "object" && !Array.isArray(p)) {
1621
+ return { ...p };
1622
+ }
1623
+ return legacyRowStylesToProps(r.styles);
1624
+ }
1625
+ function applyRowPropsToEditorRow(row, r) {
1626
+ const p = rowPropsFromDocRow(r);
1627
+ if (p.padding !== void 0 && p.padding !== null) {
1628
+ row.padding = paddingToUniform(p.padding, row.padding);
1629
+ }
1630
+ if (typeof p.bgColor === "string") row.bgColor = p.bgColor.trim();
1631
+ if (typeof p.bgImage === "string") row.bgImage = p.bgImage.trim();
1632
+ if (typeof p.bgRepeat === "string") row.bgRepeat = p.bgRepeat;
1633
+ if (typeof p.bgSize === "string") row.bgSize = p.bgSize;
1634
+ if (typeof p.bgPosition === "string") row.bgPosition = p.bgPosition;
1635
+ if (p.bgGradient !== void 0) row.bgGradient = p.bgGradient;
1636
+ }
1637
+ function editorRowToDocProps(r) {
1638
+ const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
1639
+ const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
1640
+ return { top: n, right: n, bottom: n, left: n };
1641
+ })();
1642
+ return {
1643
+ bgColor: typeof r.bgColor === "string" ? r.bgColor : "",
1644
+ bgImage: typeof r.bgImage === "string" ? r.bgImage : "",
1645
+ bgRepeat: r.bgRepeat || "no-repeat",
1646
+ bgSize: r.bgSize || "cover",
1647
+ bgPosition: r.bgPosition || "center",
1648
+ bgGradient: r.bgGradient ?? null,
1649
+ padding: rowPad,
1650
+ borderRadius: 0,
1651
+ borderWidth: 0,
1652
+ borderColor: "#e5e7eb",
1653
+ textAlign: "left"
1654
+ };
1655
+ }
1311
1656
  function layoutColumnPaddingForDoc(p) {
1312
1657
  if (typeof p === "number" && Number.isFinite(p)) {
1313
1658
  const n = Math.max(0, p);
@@ -1327,16 +1672,58 @@ function layoutColumnBorderRadiusForDoc(r) {
1327
1672
  }
1328
1673
  return 0;
1329
1674
  }
1330
- function columnPaddingNonZero(pad3) {
1331
- return pad3.top !== 0 || pad3.right !== 0 || pad3.bottom !== 0 || pad3.left !== 0;
1675
+ function normalizeLayoutContainerProps(props) {
1676
+ const columns = getColumns(props);
1677
+ const { columnStyles: _drop, ...rest } = props;
1678
+ return { ...rest, columns };
1679
+ }
1680
+ function mergeLayoutColumns(fromContent, fromHydrated) {
1681
+ const norm = (x) => {
1682
+ if (Array.isArray(x)) {
1683
+ return x.map((entry) => {
1684
+ const e = entry;
1685
+ const raw = e?.props && typeof e.props === "object" ? e.props : entry;
1686
+ return { props: { ...DEFAULT_COLUMN_PROPS, ...raw } };
1687
+ });
1688
+ }
1689
+ if (x && typeof x === "object" && !Array.isArray(x)) {
1690
+ return columnsFromLegacyMap(x, Object.keys(x).length);
1691
+ }
1692
+ return [];
1693
+ };
1694
+ const a = norm(fromContent);
1695
+ const b = norm(fromHydrated);
1696
+ const len = Math.max(a.length, b.length, 1);
1697
+ const out = [];
1698
+ for (let i = 0; i < len; i++) {
1699
+ out.push({
1700
+ props: { ...DEFAULT_COLUMN_PROPS, ...a[i]?.props ?? {}, ...b[i]?.props ?? {} }
1701
+ });
1702
+ }
1703
+ return out.length ? out : void 0;
1332
1704
  }
1333
- function columnRadiusNonZero(r) {
1334
- if (typeof r === "number") return r !== 0;
1335
- if (r && typeof r === "object" && !Array.isArray(r)) {
1336
- const o = r;
1337
- return !!(o.tl || o.tr || o.br || o.bl);
1705
+ function columnPropsFromDocColumn(c) {
1706
+ const p = c?.props;
1707
+ if (p && typeof p === "object" && !Array.isArray(p)) {
1708
+ return { ...DEFAULT_COLUMN_PROPS, ...p };
1338
1709
  }
1339
- return false;
1710
+ const s = c?.styles;
1711
+ if (!s || typeof s !== "object" || Array.isArray(s)) {
1712
+ return { ...DEFAULT_COLUMN_PROPS };
1713
+ }
1714
+ const padding = layoutColumnPaddingForDoc(s.padding);
1715
+ const borderRadius = layoutColumnBorderRadiusForDoc(s.borderRadius);
1716
+ return {
1717
+ ...DEFAULT_COLUMN_PROPS,
1718
+ bgColor: typeof s.backgroundColor === "string" ? s.backgroundColor : "",
1719
+ bgImage: typeof s.backgroundImage === "string" ? String(s.backgroundImage).trim() : "",
1720
+ bgRepeat: s.backgroundRepeat || "no-repeat",
1721
+ bgSize: s.backgroundSize || "cover",
1722
+ bgPosition: s.backgroundPosition || "center",
1723
+ bgGradient: s.backgroundGradient ?? null,
1724
+ padding,
1725
+ borderRadius
1726
+ };
1340
1727
  }
1341
1728
  function asNum(v) {
1342
1729
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
@@ -1433,13 +1820,15 @@ function internalBlockToEmailDoc(b, depth = 0) {
1433
1820
  ...p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
1434
1821
  }
1435
1822
  };
1436
- case "html":
1823
+ case "html": {
1824
+ const body = typeof b.content === "string" ? b.content : typeof p.content === "string" ? p.content : "";
1437
1825
  return {
1438
1826
  id,
1439
1827
  type,
1440
- content: { html: typeof p.content === "string" ? p.content : "" },
1828
+ content: { html: normalizeRichHtmlForStorage(body) },
1441
1829
  styles: p.padding && typeof p.padding === "object" && !Array.isArray(p.padding) ? { padding: layoutColumnPaddingForDoc(p.padding) } : {}
1442
1830
  };
1831
+ }
1443
1832
  case "image": {
1444
1833
  const br = p.borderRadius;
1445
1834
  const borderRadius = typeof br === "number" && Number.isFinite(br) ? br : br && typeof br === "object" && !Array.isArray(br) ? Math.round(
@@ -1619,7 +2008,7 @@ function internalBlockToEmailDoc(b, depth = 0) {
1619
2008
  bgRepeat: p.bgRepeat,
1620
2009
  bgPosition: p.bgPosition,
1621
2010
  bgGradient: p.bgGradient ?? null,
1622
- columnStyles: p.columnStyles,
2011
+ columns: getColumns(p).map((col) => ({ props: cloneJson(col.props) })),
1623
2012
  cells: cellsOut
1624
2013
  },
1625
2014
  styles: {}
@@ -1659,17 +2048,26 @@ function mapBlockToInternal(b, layoutDepth = 0) {
1659
2048
  block.props.padding = paddingToUniform(s.padding, block.props.padding);
1660
2049
  }
1661
2050
  break;
1662
- case "html":
1663
- block.props.content = asStr(c.html) ?? block.props.content;
1664
- block.props.content = normalizeRichHtmlForStorage(block.props.content);
2051
+ case "html": {
2052
+ const br = b;
2053
+ const fromDoc = asStr(c.html);
2054
+ const fromRoot = typeof br.content === "string" ? String(br.content) : void 0;
2055
+ const fromLegacyProps = br.props && typeof br.props.content === "string" ? String(br.props.content) : void 0;
2056
+ const htmlNorm = normalizeRichHtmlForStorage(fromDoc ?? fromRoot ?? fromLegacyProps ?? "");
2057
+ block.content = htmlNorm;
2058
+ block.props.content = htmlNorm;
1665
2059
  if (s.padding && typeof s.padding === "object") {
1666
2060
  block.props.padding = paddingToUniform(s.padding, block.props.padding);
1667
2061
  }
1668
2062
  break;
2063
+ }
1669
2064
  case "image":
1670
2065
  block.props.src = asStr(c.src) ?? block.props.src;
1671
2066
  if (asStr(c.alt)) block.props.alt = c.alt;
1672
- if (asStr(c.link)) block.props.link = c.link;
2067
+ if (asStr(c.link)) {
2068
+ block.props.link = c.link;
2069
+ block.props.linkEnabled = true;
2070
+ }
1673
2071
  if (beh.openInNewTab === true) block.props.linkTarget = "_blank";
1674
2072
  if (asStr(s.width)) block.props.width = s.width;
1675
2073
  if (asStr(s.align)) block.props.align = s.align;
@@ -1791,8 +2189,17 @@ function mapBlockToInternal(b, layoutDepth = 0) {
1791
2189
  if (typeof c.bgRepeat === "string") block.props.bgRepeat = c.bgRepeat;
1792
2190
  if (typeof c.bgPosition === "string") block.props.bgPosition = c.bgPosition;
1793
2191
  if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
1794
- if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
1795
- block.props.columnStyles = c.columnStyles;
2192
+ if (c.bgGradient !== void 0) block.props.bgGradient = c.bgGradient;
2193
+ if (Array.isArray(c.columns)) {
2194
+ block.props.columns = c.columns.map((col) => ({
2195
+ props: {
2196
+ ...DEFAULT_COLUMN_PROPS,
2197
+ ...col?.props && typeof col.props === "object" ? col.props : {}
2198
+ }
2199
+ }));
2200
+ } else if (c.columnStyles && typeof c.columnStyles === "object" && !Array.isArray(c.columnStyles)) {
2201
+ const ratios = Array.isArray(c.ratios) ? c.ratios : [1];
2202
+ block.props.columns = columnsFromLegacyMap(c.columnStyles, ratios.length);
1796
2203
  }
1797
2204
  block.props.cells = c.cells.map(
1798
2205
  (col) => Array.isArray(col) ? col.map((raw) => mapEmbeddedLayoutCellBlock(raw, layoutDepth + 1)).filter((x) => x != null) : []
@@ -1836,6 +2243,10 @@ function mapBlockToInternal(b, layoutDepth = 0) {
1836
2243
  }
1837
2244
  }
1838
2245
  applyImportedEditorPropsFromDoc(block, t, b, layoutDepth);
2246
+ if (t === "image") {
2247
+ const link = asStr(block.props.link);
2248
+ if (link && block.props.linkEnabled == null) block.props.linkEnabled = true;
2249
+ }
1839
2250
  return block;
1840
2251
  }
1841
2252
  function rowToInternal(r) {
@@ -1857,55 +2268,315 @@ function rowToInternal(r) {
1857
2268
  row.id = typeof r.id === "string" ? r.id : uid();
1858
2269
  row.cols = cols;
1859
2270
  row.gap = typeof r.layout?.gap === "number" ? r.layout.gap : row.gap;
1860
- row.padding = paddingToUniform(r.styles?.padding, row.padding);
1861
- row.bgColor = (r.styles?.backgroundColor || "").trim();
1862
- row.bgImage = (r.styles?.backgroundImage || "").trim();
1863
- row.bgRepeat = r.styles?.backgroundRepeat || row.bgRepeat;
1864
- row.bgSize = r.styles?.backgroundSize || row.bgSize;
1865
- row.bgPosition = r.styles?.backgroundPosition || row.bgPosition || "center";
1866
- row.bgGradient = r.styles?.backgroundGradient || row.bgGradient || null;
2271
+ applyRowPropsToEditorRow(row, r);
2272
+ const lay = r.layout;
2273
+ if (lay && typeof lay === "object" && !Array.isArray(lay)) {
2274
+ if (typeof lay.preset === "string" && lay.preset.trim()) row.preset = lay.preset.trim();
2275
+ if (Array.isArray(lay.ratios) && lay.ratios.length) {
2276
+ const rr = lay.ratios.filter((x) => typeof x === "number" && Number.isFinite(x));
2277
+ if (rr.length) row.ratios = rr;
2278
+ }
2279
+ }
1867
2280
  row.cells = colList.map((c) => {
1868
2281
  const blocks = Array.isArray(c.blocks) ? c.blocks : [];
1869
2282
  return blocks.map((blk) => mapBlockToInternal(blk, 0)).filter((x) => x != null);
1870
2283
  });
1871
2284
  const studio = r._reactEmailStudio;
1872
2285
  const snap = studio?.row;
1873
- const columnStylesFromDoc = {};
1874
- colList.forEach((c, i) => {
1875
- const bgColor = typeof c.styles?.backgroundColor === "string" ? c.styles.backgroundColor : "";
1876
- const padding = layoutColumnPaddingForDoc(c.styles?.padding);
1877
- const borderRadius = layoutColumnBorderRadiusForDoc(c.styles?.borderRadius);
1878
- const bgImage = typeof c.styles?.backgroundImage === "string" ? String(c.styles.backgroundImage).trim() : "";
1879
- const bgRepeat = typeof c.styles?.backgroundRepeat === "string" ? c.styles.backgroundRepeat : void 0;
1880
- const bgSize = typeof c.styles?.backgroundSize === "string" ? c.styles.backgroundSize : void 0;
1881
- const bgPosition = typeof c.styles?.backgroundPosition === "string" ? c.styles.backgroundPosition : void 0;
1882
- const bgGradient = c.styles?.backgroundGradient || null;
1883
- const hasPad = columnPaddingNonZero(padding);
1884
- const hasBr = columnRadiusNonZero(borderRadius);
1885
- if (bgColor || hasPad || hasBr || bgImage || bgRepeat || bgSize || bgPosition || bgGradient) {
1886
- columnStylesFromDoc[i] = { bgColor, padding, borderRadius, bgImage, bgRepeat, bgSize, bgPosition, bgGradient };
1887
- }
1888
- });
2286
+ const columnsFromDoc = colList.map((c) => ({
2287
+ props: columnPropsFromDocColumn(c)
2288
+ }));
1889
2289
  if (snap && typeof snap === "object" && !Array.isArray(snap)) {
1890
- const { cells: _omitCells, columnStyles: snapCs, ...rest } = snap;
2290
+ const {
2291
+ cells: _omitCells,
2292
+ columnStyles: legacyCs,
2293
+ columns: snapCols,
2294
+ ...rest
2295
+ } = snap;
1891
2296
  Object.assign(row, rest);
1892
- const snapColumn = snapCs && typeof snapCs === "object" && !Array.isArray(snapCs) ? snapCs : {};
1893
- row.columnStyles = { ...snapColumn, ...columnStylesFromDoc };
1894
- if (!Object.keys(row.columnStyles).length) delete row.columnStyles;
1895
- } else if (Object.keys(columnStylesFromDoc).length) {
1896
- row.columnStyles = columnStylesFromDoc;
2297
+ const fromSnap = Array.isArray(snapCols) ? snapCols.map((col) => ({
2298
+ props: {
2299
+ ...DEFAULT_COLUMN_PROPS,
2300
+ ...col?.props && typeof col.props === "object" ? col.props : {}
2301
+ }
2302
+ })) : columnsFromLegacyMap(legacyCs, cols);
2303
+ row.columns = mergeLayoutColumns(fromSnap, columnsFromDoc) ?? columnsFromDoc;
2304
+ delete row.columnStyles;
2305
+ } else {
2306
+ row.columns = columnsFromDoc;
1897
2307
  }
1898
2308
  return row;
1899
2309
  }
1900
- function normalizeEmailDesignInput(input) {
1901
- const doc = normalizeEmailDocument(input);
1902
- if (!doc) return null;
1903
- const s = doc.settings || {};
1904
- const studioSettings = s._reactEmailStudio;
2310
+ function coerceEmailDocumentInput(input) {
2311
+ if (input == null) return input;
2312
+ if (Array.isArray(input)) {
2313
+ if (input.length === 0) return input;
2314
+ return { type: "email_document", settings: {}, blocks: input };
2315
+ }
2316
+ if (typeof input !== "object") return input;
2317
+ const o = input;
2318
+ if (o.type === "email_document") return input;
2319
+ if (Array.isArray(o.blocks)) {
2320
+ return {
2321
+ type: "email_document",
2322
+ settings: o.settings && typeof o.settings === "object" ? o.settings : {},
2323
+ blocks: o.blocks
2324
+ };
2325
+ }
2326
+ return input;
2327
+ }
2328
+ function rootLayoutBlockToDocRow(block) {
2329
+ const p = block.props && typeof block.props === "object" ? block.props : {};
2330
+ const c = block.content && typeof block.content === "object" && !Array.isArray(block.content) ? block.content : {};
2331
+ const cols = Math.max(
2332
+ 1,
2333
+ typeof p.cols === "number" && p.cols > 0 ? Math.floor(p.cols) : 0,
2334
+ typeof c.cols === "number" && c.cols > 0 ? Math.floor(c.cols) : 0,
2335
+ Array.isArray(p.cells) ? p.cells.length : 0,
2336
+ Array.isArray(c.cells) ? c.cells.length : 0
2337
+ );
2338
+ const rawCells = Array.isArray(c.cells) ? c.cells : Array.isArray(p.cells) ? p.cells : [];
2339
+ const contentColumns = Array.isArray(c.columns) ? c.columns : [];
2340
+ const propsColumns = getColumns(p);
2341
+ const columns = Array.from({ length: cols }, (_, ci) => {
2342
+ const cellRaw = rawCells[ci];
2343
+ const blocks = Array.isArray(cellRaw) ? cellRaw.filter((x) => x && typeof x === "object").map((x, bi) => {
2344
+ const blk = x;
2345
+ return {
2346
+ id: ensureBlockId(blk.id, `b_${ci}_${bi}`),
2347
+ type: typeof blk.type === "string" ? blk.type : "text",
2348
+ content: blk.content,
2349
+ ...blk.props ? { props: blk.props } : {},
2350
+ ...blk.styles ? { styles: blk.styles } : {},
2351
+ ...blk.behavior ? { behavior: blk.behavior } : {}
2352
+ };
2353
+ }) : [];
2354
+ const colProps = {
2355
+ ...DEFAULT_COLUMN_PROPS,
2356
+ ...propsColumns[ci]?.props ?? contentColumns[ci]?.props ?? {}
2357
+ };
2358
+ return {
2359
+ id: `col_${ci + 1}`,
2360
+ layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
2361
+ props: cloneJson(colProps),
2362
+ blocks
2363
+ };
2364
+ });
2365
+ const rowProps = {
2366
+ bgColor: typeof p.bgColor === "string" ? p.bgColor : "",
2367
+ bgImage: typeof p.bgImage === "string" ? p.bgImage : "",
2368
+ bgRepeat: p.bgRepeat || "no-repeat",
2369
+ bgSize: p.bgSize || "cover",
2370
+ bgPosition: p.bgPosition || "center",
2371
+ bgGradient: p.bgGradient ?? null,
2372
+ padding: typeof p.padding === "number" ? { top: p.padding, right: p.padding, bottom: p.padding, left: p.padding } : normalizePadding(p.padding)
2373
+ };
2374
+ const ratios = Array.isArray(p.ratios) ? p.ratios : Array.isArray(c.ratios) ? c.ratios : void 0;
2375
+ return {
2376
+ id: typeof block.id === "string" ? block.id : uid(),
2377
+ type: "row",
2378
+ layout: {
2379
+ columns: cols,
2380
+ gap: typeof p.gap === "number" ? p.gap : typeof c.gap === "number" ? c.gap : 0,
2381
+ stackOnMobile: true,
2382
+ align: "center",
2383
+ ...typeof p.preset === "string" && p.preset.trim() ? { preset: p.preset.trim() } : typeof c.preset === "string" && c.preset.trim() ? { preset: c.preset.trim() } : {},
2384
+ ...ratios?.length ? { ratios: [...ratios] } : {}
2385
+ },
2386
+ props: rowProps,
2387
+ columns
2388
+ };
2389
+ }
2390
+ function ensureBlockId(id, prefix) {
2391
+ return typeof id === "string" && id.trim() ? id : `${prefix}_${uid()}`;
2392
+ }
2393
+ function wrapRootContentBlock(block) {
2394
+ const row = makeLayoutRow(pickPresetByColCount(1));
2395
+ const internal = mapBlockToInternal(block, 0);
2396
+ if (internal) row.cells[0] = [internal];
2397
+ return row;
2398
+ }
2399
+ function rootBlockToEditorRow(block) {
2400
+ const t = block.type === "nestedRow" ? "layout" : block.type;
2401
+ if (t === "layout") {
2402
+ return rowToInternal(rootLayoutBlockToDocRow(block));
2403
+ }
2404
+ return wrapRootContentBlock(block);
2405
+ }
2406
+ function blocksToEditorRows(blocks) {
2407
+ return blocks.map(rootBlockToEditorRow);
2408
+ }
2409
+ function rowPropsToLegacyStyles(p) {
2410
+ return {
2411
+ backgroundColor: p.bgColor ?? "",
2412
+ backgroundImage: p.bgImage ?? "",
2413
+ backgroundRepeat: p.bgRepeat ?? "no-repeat",
2414
+ backgroundSize: p.bgSize ?? "cover",
2415
+ backgroundPosition: p.bgPosition ?? "center",
2416
+ backgroundGradient: p.bgGradient ?? null,
2417
+ padding: p.padding,
2418
+ borderRadius: p.borderRadius ?? 0,
2419
+ borderWidth: p.borderWidth ?? 0,
2420
+ borderColor: p.borderColor ?? "#e5e7eb",
2421
+ textAlign: p.textAlign ?? "left"
2422
+ };
2423
+ }
2424
+ function columnPropsToLegacyStyles(cp) {
2425
+ return {
2426
+ padding: layoutColumnPaddingForDoc(cp.padding),
2427
+ backgroundColor: cp.bgColor ?? "",
2428
+ backgroundImage: cp.bgImage ?? "",
2429
+ backgroundRepeat: cp.bgRepeat ?? "no-repeat",
2430
+ backgroundSize: cp.bgSize ?? "cover",
2431
+ backgroundPosition: cp.bgPosition ?? "center",
2432
+ backgroundGradient: cp.bgGradient ?? null,
2433
+ borderRadius: layoutColumnBorderRadiusForDoc(cp.borderRadius)
2434
+ };
2435
+ }
2436
+ function editorRowStudioSnapshot(r) {
2437
+ const p = editorRowToDocProps(r);
2438
+ const columns = getColumns(r).map((col) => ({
2439
+ props: cloneJson(col.props ?? DEFAULT_COLUMN_PROPS)
2440
+ }));
2441
+ return {
2442
+ id: r.id,
2443
+ type: "layout",
2444
+ preset: r.preset,
2445
+ cols: r.cols,
2446
+ ratios: Array.isArray(r.ratios) ? [...r.ratios] : [1],
2447
+ gap: typeof r.gap === "number" ? r.gap : 0,
2448
+ padding: p.padding,
2449
+ bgColor: p.bgColor ?? "",
2450
+ bgImage: p.bgImage ?? "",
2451
+ bgSize: p.bgSize ?? "cover",
2452
+ bgRepeat: p.bgRepeat ?? "no-repeat",
2453
+ bgPosition: p.bgPosition ?? "center",
2454
+ bgGradient: p.bgGradient ?? null,
2455
+ columns
2456
+ };
2457
+ }
2458
+ function exportBlockForLegacyRow(b, depth = 0) {
2459
+ if (!b || typeof b !== "object") {
2460
+ return { id: uid(), type: "text", content: {}, props: {} };
2461
+ }
2462
+ const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? cloneJson(b.props) : {};
2463
+ if (b.type === "layout" && Array.isArray(b.props?.cells)) {
2464
+ rawProps.cells = b.props.cells.map(
2465
+ (col) => Array.isArray(col) ? col.map((child) => exportBlockForLegacyRow(child, depth + 1)) : []
2466
+ );
2467
+ }
2468
+ if (b.type === "html") {
2469
+ const body = typeof b.content === "string" ? b.content : typeof rawProps.content === "string" ? rawProps.content : "";
2470
+ const html = normalizeRichHtmlForStorage(body);
2471
+ rawProps.content = html;
2472
+ return {
2473
+ id: typeof b.id === "string" ? b.id : uid(),
2474
+ type: "html",
2475
+ content: { html },
2476
+ props: rawProps
2477
+ };
2478
+ }
2479
+ const doc = internalBlockToEmailDoc(b, depth);
2480
+ const out = {
2481
+ id: doc.id,
2482
+ type: doc.type,
2483
+ content: doc.content ?? {},
2484
+ props: rawProps
2485
+ };
2486
+ if (doc.behavior) out.behavior = doc.behavior;
2487
+ return out;
2488
+ }
2489
+ function editorRowToEmailDocumentRow(r, rowIndex) {
2490
+ const rowProps = editorRowToDocProps(r);
2491
+ const cellArrays = Array.isArray(r.cells) ? r.cells : [];
2492
+ const declaredCols = typeof r.cols === "number" && r.cols > 0 ? Math.floor(r.cols) : 0;
2493
+ const colCount = Math.max(1, cellArrays.length, declaredCols);
2494
+ const rowColumns = getColumns(r);
2495
+ const columns = Array.from({ length: colCount }, (_, ci) => {
2496
+ const cp = {
2497
+ ...DEFAULT_COLUMN_PROPS,
2498
+ ...rowColumns[ci]?.props ?? {}
2499
+ };
2500
+ const cellBlocks = cellArrays[ci];
2501
+ const blocks = Array.isArray(cellBlocks) ? cellBlocks.map((blk) => exportBlockForLegacyRow(blk)).filter((x) => x != null) : [];
2502
+ return {
2503
+ id: `col_${rowIndex + 1}_${ci + 1}`,
2504
+ layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
2505
+ styles: columnPropsToLegacyStyles(cp),
2506
+ props: cloneJson(cp),
2507
+ blocks
2508
+ };
2509
+ });
2510
+ const ratios = Array.isArray(r.ratios) ? [...r.ratios] : void 0;
2511
+ return {
2512
+ id: typeof r.id === "string" ? r.id : uid(),
2513
+ type: "row",
2514
+ _reactEmailStudio: { row: editorRowStudioSnapshot(r) },
2515
+ layout: {
2516
+ columns: colCount,
2517
+ gap: typeof r.gap === "number" ? r.gap : 0,
2518
+ stackOnMobile: true,
2519
+ align: "center",
2520
+ ...typeof r.preset === "string" && r.preset.trim() ? { preset: r.preset.trim() } : {},
2521
+ ...ratios?.length ? { ratios } : {}
2522
+ },
2523
+ props: rowProps,
2524
+ styles: rowPropsToLegacyStyles(rowProps),
2525
+ columns
2526
+ };
2527
+ }
2528
+ function buildExportSettingsWithStudio(settings) {
2529
+ const base = buildExportSettings(settings) ?? {};
2530
+ const editorSettings = cloneJson(settings);
2531
+ return {
2532
+ ...base,
2533
+ _reactEmailStudio: {
2534
+ contentPadding: base.contentPadding ?? 24,
2535
+ contentBorderRadius: base.contentBorderRadius ?? 8,
2536
+ editorSettings
2537
+ }
2538
+ };
2539
+ }
2540
+ function editorSettingStr(settings, key, fallback = "") {
2541
+ const v = settings[key];
2542
+ return typeof v === "string" ? v : fallback;
2543
+ }
2544
+ function buildExportSettings(settings) {
2545
+ const contentWidth = typeof settings.contentWidth === "number" ? settings.contentWidth : 600;
2546
+ const bgGradient = settings.bgGradient;
2547
+ const contentBgGradient = settings.contentBgGradient;
2548
+ return {
2549
+ width: contentWidth,
2550
+ backgroundColor: editorSettingStr(settings, "bgColor", "#f1f5f9"),
2551
+ backgroundImage: editorSettingStr(settings, "bgImage"),
2552
+ backgroundRepeat: editorSettingStr(settings, "bgRepeat", "no-repeat"),
2553
+ backgroundSize: editorSettingStr(settings, "bgSize", "cover"),
2554
+ backgroundPosition: editorSettingStr(settings, "bgPosition", "center"),
2555
+ backgroundGradient: bgGradient && typeof bgGradient === "object" && !Array.isArray(bgGradient) ? bgGradient : void 0,
2556
+ contentBackgroundColor: editorSettingStr(settings, "contentBg", "#ffffff"),
2557
+ contentBackgroundImage: editorSettingStr(settings, "contentBgImage"),
2558
+ contentBackgroundRepeat: editorSettingStr(settings, "contentBgRepeat", "no-repeat"),
2559
+ contentBackgroundSize: editorSettingStr(settings, "contentBgSize", "cover"),
2560
+ contentBackgroundPosition: editorSettingStr(settings, "contentBgPosition", "center"),
2561
+ contentBackgroundGradient: contentBgGradient && typeof contentBgGradient === "object" && !Array.isArray(contentBgGradient) ? contentBgGradient : void 0,
2562
+ fontFamily: editorSettingStr(settings, "fontFamily") || editorSettingStr(settings, "pageFontFamily", "Arial, Helvetica, sans-serif"),
2563
+ lineHeightBase: settings.pageLineHeight != null && settings.pageLineHeight !== "" ? Number(settings.pageLineHeight) : 1.6,
2564
+ color: editorSettingStr(settings, "pageTextColor", "#111827"),
2565
+ responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
2566
+ rtl: !!settings.pageRtl,
2567
+ contentPadding: typeof settings.padding === "number" ? settings.padding : 24,
2568
+ contentBorderRadius: typeof settings.borderRadius === "number" ? settings.borderRadius : 8
2569
+ };
2570
+ }
2571
+ function editorSettingsFromDoc(s, rawSettings) {
2572
+ const studioSettings = rawSettings?._reactEmailStudio;
1905
2573
  const settings = {};
1906
2574
  if (studioSettings?.editorSettings && typeof studioSettings.editorSettings === "object" && !Array.isArray(studioSettings.editorSettings)) {
1907
2575
  Object.assign(settings, cloneJson(studioSettings.editorSettings));
1908
2576
  }
2577
+ const pageRtlFromEditor = settings.pageRtl;
2578
+ const contentPadding = typeof s.contentPadding === "number" ? s.contentPadding : typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : void 0;
2579
+ const contentBorderRadius = typeof s.contentBorderRadius === "number" ? s.contentBorderRadius : typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : void 0;
1909
2580
  Object.assign(settings, {
1910
2581
  bgColor: s.backgroundColor || "#f1f5f9",
1911
2582
  bgImage: s.backgroundImage || "",
@@ -1920,103 +2591,67 @@ function normalizeEmailDesignInput(input) {
1920
2591
  contentBgPosition: s.contentBackgroundPosition || "center",
1921
2592
  contentBgGradient: s.contentBackgroundGradient || null,
1922
2593
  contentWidth: typeof s.width === "number" ? s.width : 600,
1923
- padding: typeof studioSettings?.contentPadding === "number" ? studioSettings.contentPadding : typeof settings.padding === "number" ? settings.padding : 24,
1924
- borderRadius: typeof studioSettings?.contentBorderRadius === "number" ? studioSettings.contentBorderRadius : typeof settings.borderRadius === "number" ? settings.borderRadius : 8,
2594
+ padding: contentPadding ?? (typeof settings.padding === "number" ? settings.padding : 24),
2595
+ borderRadius: contentBorderRadius ?? (typeof settings.borderRadius === "number" ? settings.borderRadius : 8),
1925
2596
  pageFontFamily: s.fontFamily,
1926
2597
  pageTextColor: s.color,
1927
2598
  pageLineHeight: s.lineHeightBase != null ? String(s.lineHeightBase) : void 0,
1928
2599
  pageResponsive: s.responsive,
1929
- pageRtl: s.rtl,
2600
+ pageRtl: typeof s.rtl === "boolean" ? s.rtl : pageRtlFromEditor,
1930
2601
  fontFamily: s.fontFamily
1931
2602
  });
1932
- const rows = (doc.rows || []).map(rowToInternal);
2603
+ return settings;
2604
+ }
2605
+ function normalizeEmailDesignInput(input) {
2606
+ const coerced = coerceEmailDocumentInput(input);
2607
+ const doc = normalizeEmailDocument(coerced);
2608
+ if (!doc) return null;
2609
+ const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
2610
+ const rawSettings = rawDoc?.settings && typeof rawDoc.settings === "object" && !Array.isArray(rawDoc.settings) ? rawDoc.settings : null;
2611
+ const settings = editorSettingsFromDoc(doc.settings || {}, rawSettings);
2612
+ let rows;
2613
+ if (doc.blocks?.length) {
2614
+ rows = blocksToEditorRows(doc.blocks);
2615
+ } else {
2616
+ const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
2617
+ rows = (doc.rows || []).map((r, i) => {
2618
+ const legacy = rawRows[i]?._reactEmailStudio;
2619
+ if (legacy) {
2620
+ return rowToInternal({ ...r, _reactEmailStudio: legacy });
2621
+ }
2622
+ return rowToInternal(r);
2623
+ });
2624
+ }
1933
2625
  return withHydratedRows({ rows, settings, __emailDocument: doc });
1934
2626
  }
2627
+ function emailDocumentToEditorRows(input) {
2628
+ const coerced = coerceEmailDocumentInput(input);
2629
+ const doc = normalizeEmailDocument(coerced);
2630
+ if (!doc) return null;
2631
+ const rawDoc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
2632
+ if (doc.blocks?.length) {
2633
+ return blocksToEditorRows(doc.blocks);
2634
+ }
2635
+ const rawRows = rawDoc && Array.isArray(rawDoc.rows) ? rawDoc.rows : [];
2636
+ return (doc.rows || []).map((r, i) => {
2637
+ const legacy = rawRows[i]?._reactEmailStudio;
2638
+ if (legacy) {
2639
+ return rowToInternal({ ...r, _reactEmailStudio: legacy });
2640
+ }
2641
+ return rowToInternal(r);
2642
+ });
2643
+ }
1935
2644
  function designToEmailDocument(rows, settings) {
1936
- const doc = {
2645
+ return {
1937
2646
  type: "email_document",
1938
- settings: {
1939
- width: settings.contentWidth || 600,
1940
- backgroundColor: settings.bgColor || "#f1f5f9",
1941
- backgroundImage: settings.bgImage || "",
1942
- backgroundRepeat: settings.bgRepeat || "no-repeat",
1943
- backgroundSize: settings.bgSize || "cover",
1944
- backgroundPosition: settings.bgPosition || "center",
1945
- backgroundGradient: settings.bgGradient || null,
1946
- contentBackgroundColor: settings.contentBg || "#ffffff",
1947
- contentBackgroundImage: settings.contentBgImage || "",
1948
- contentBackgroundRepeat: settings.contentBgRepeat || "no-repeat",
1949
- contentBackgroundSize: settings.contentBgSize || "cover",
1950
- contentBackgroundPosition: settings.contentBgPosition || "center",
1951
- contentBackgroundGradient: settings.contentBgGradient || null,
1952
- fontFamily: settings.fontFamily || settings.pageFontFamily || "Arial, Helvetica, sans-serif",
1953
- lineHeightBase: settings.pageLineHeight ? Number(settings.pageLineHeight) : 1.6,
1954
- color: settings.pageTextColor || "#111827",
1955
- responsive: typeof settings.pageResponsive === "boolean" ? settings.pageResponsive : true,
1956
- rtl: !!settings.pageRtl,
1957
- _reactEmailStudio: {
1958
- contentPadding: settings.padding ?? 24,
1959
- contentBorderRadius: settings.borderRadius ?? 8,
1960
- editorSettings: cloneJson(settings)
1961
- }
1962
- },
1963
- rows: rows.map((r, ri) => {
1964
- const cellArrays = Array.isArray(r.cells) ? r.cells : [];
1965
- const declaredCols = typeof r.cols === "number" && r.cols > 0 ? r.cols : 0;
1966
- const cols = Math.max(1, cellArrays.length, declaredCols);
1967
- const rowPad = r.padding && typeof r.padding === "object" && !Array.isArray(r.padding) ? layoutColumnPaddingForDoc(r.padding) : (() => {
1968
- const n = typeof r.padding === "number" && Number.isFinite(r.padding) ? r.padding : 0;
1969
- return { top: n, right: n, bottom: n, left: n };
1970
- })();
1971
- const { cells: _rowCells, ...rowEditorSnap } = r;
1972
- return {
1973
- id: r.id || `row_${ri + 1}`,
1974
- type: "row",
1975
- _reactEmailStudio: {
1976
- row: cloneJson(rowEditorSnap)
1977
- },
1978
- layout: {
1979
- columns: cols,
1980
- gap: r.gap ?? 0,
1981
- stackOnMobile: true,
1982
- align: "center"
1983
- },
1984
- styles: {
1985
- backgroundColor: r.bgColor || "",
1986
- backgroundImage: r.bgImage || "",
1987
- backgroundRepeat: r.bgRepeat || "no-repeat",
1988
- backgroundSize: r.bgSize || "cover",
1989
- backgroundPosition: r.bgPosition || "center",
1990
- backgroundGradient: r.bgGradient || null,
1991
- padding: rowPad,
1992
- borderRadius: 0,
1993
- borderWidth: 0,
1994
- borderColor: "#e5e7eb",
1995
- textAlign: "left"
1996
- },
1997
- columns: Array.from({ length: cols }, (_, ci) => {
1998
- const cellBlocks = cellArrays[ci] ?? [];
1999
- const cs = r.columnStyles && r.columnStyles[ci] || {};
2000
- return {
2001
- id: `col_${ri + 1}_${ci + 1}`,
2002
- layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
2003
- styles: {
2004
- padding: layoutColumnPaddingForDoc(cs.padding),
2005
- backgroundColor: cs.bgColor || "",
2006
- backgroundImage: cs.bgImage || "",
2007
- backgroundRepeat: cs.bgRepeat || "no-repeat",
2008
- backgroundSize: cs.bgSize || "cover",
2009
- backgroundPosition: cs.bgPosition || "center",
2010
- backgroundGradient: cs.bgGradient || null,
2011
- borderRadius: layoutColumnBorderRadiusForDoc(cs.borderRadius)
2012
- },
2013
- blocks: (cellBlocks || []).map((b) => exportEmailDocBlock(b))
2014
- };
2015
- })
2016
- };
2017
- })
2647
+ settings: buildExportSettingsWithStudio(settings),
2648
+ rows: rows.map((r, i) => editorRowToEmailDocumentRow(r, i))
2018
2649
  };
2019
- return doc;
2650
+ }
2651
+ function canonicalizeEmailDocument(input) {
2652
+ const loaded = normalizeEmailDesignInput(coerceEmailDocumentInput(input) ?? input);
2653
+ if (!loaded?.rows) return null;
2654
+ return designToEmailDocument(loaded.rows, loaded.settings);
2020
2655
  }
2021
2656
  function hydrateBlock(b, depth = 0) {
2022
2657
  if (!b || typeof b !== "object") return null;
@@ -2030,13 +2665,13 @@ function hydrateBlock(b, depth = 0) {
2030
2665
  ...b,
2031
2666
  type: "layout",
2032
2667
  id: typeof b.id === "string" && b.id ? b.id : uid(),
2033
- props: {
2668
+ props: normalizeLayoutContainerProps({
2034
2669
  ...defaults2,
2035
2670
  ...b.props,
2036
2671
  bgSize: b.props.bgSize ?? defaults2.bgSize,
2037
2672
  bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
2038
2673
  cells: ratios.map(() => [])
2039
- }
2674
+ })
2040
2675
  };
2041
2676
  }
2042
2677
  const cells = b.props.cells.map(
@@ -2046,24 +2681,33 @@ function hydrateBlock(b, depth = 0) {
2046
2681
  ...b,
2047
2682
  type: "layout",
2048
2683
  id: typeof b.id === "string" && b.id ? b.id : uid(),
2049
- props: {
2684
+ props: normalizeLayoutContainerProps({
2050
2685
  ...defaults2,
2051
2686
  ...b.props,
2052
2687
  bgSize: b.props.bgSize ?? defaults2.bgSize,
2053
2688
  bgRepeat: b.props.bgRepeat ?? defaults2.bgRepeat,
2054
2689
  cells
2055
- }
2690
+ })
2056
2691
  };
2057
2692
  }
2058
2693
  const defaults = DEFAULT_BLOCK_PROPS[t];
2059
2694
  if (typeof defaults !== "object" || defaults == null || Array.isArray(defaults)) return null;
2060
- const merged = t === "html" ? {
2061
- ...defaults,
2062
- ...b.props && typeof b.props === "object" ? b.props : {},
2063
- content: normalizeRichHtmlForStorage(
2064
- b.props && typeof b.props === "object" ? b.props.content : void 0
2065
- )
2066
- } : { ...defaults, ...b.props && typeof b.props === "object" ? b.props : {} };
2695
+ if (t === "html") {
2696
+ const rawProps = b.props && typeof b.props === "object" && !Array.isArray(b.props) ? b.props : {};
2697
+ const { content: legacyPropContent, ...restProps } = rawProps;
2698
+ const merged2 = { ...defaults, ...restProps };
2699
+ normalizeBoxStyles(merged2, t);
2700
+ const src = typeof b.content === "string" ? String(b.content) : typeof legacyPropContent === "string" ? legacyPropContent : "";
2701
+ const htmlNorm = normalizeRichHtmlForStorage(src);
2702
+ merged2.content = htmlNorm;
2703
+ return {
2704
+ ...b,
2705
+ id: typeof b.id === "string" && b.id ? b.id : uid(),
2706
+ content: htmlNorm,
2707
+ props: merged2
2708
+ };
2709
+ }
2710
+ const merged = { ...defaults, ...b.props && typeof b.props === "object" ? b.props : {} };
2067
2711
  normalizeBoxStyles(merged, t);
2068
2712
  return {
2069
2713
  ...b,
@@ -2079,70 +2723,50 @@ function mapEmbeddedLayoutCellBlock(raw, layoutDepth = 0) {
2079
2723
  }
2080
2724
  return mapBlockToInternal(r, layoutDepth);
2081
2725
  }
2082
- function mergeLayoutColumnStylesMaps(fromContent, fromHydrated) {
2083
- const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
2084
- if (!isObj(fromContent) && !isObj(fromHydrated)) return void 0;
2085
- const a = isObj(fromContent) ? fromContent : {};
2086
- const b = isObj(fromHydrated) ? fromHydrated : {};
2087
- const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
2088
- const out = {};
2089
- for (const k of keys) {
2090
- const va = a[k];
2091
- const vb = b[k];
2092
- if (isObj(va) && isObj(vb)) {
2093
- out[k] = { ...va, ...vb };
2094
- } else if (vb !== void 0) {
2095
- out[k] = vb;
2096
- } else {
2097
- out[k] = va;
2098
- }
2099
- }
2100
- return Object.keys(out).length ? out : void 0;
2101
- }
2102
2726
  function applyImportedEditorPropsFromDoc(block, t, b, layoutDepth = 0) {
2103
2727
  const exp = b.props;
2104
- if (!exp || typeof exp !== "object" || Array.isArray(exp)) return;
2728
+ if (!exp || typeof exp !== "object" || Array.isArray(exp)) {
2729
+ if (t === "html" && typeof block.content === "string") {
2730
+ block.content = normalizeRichHtmlForStorage(block.content);
2731
+ block.props.content = block.content;
2732
+ }
2733
+ return;
2734
+ }
2105
2735
  if (t === "layout" && Array.isArray(exp.cells)) {
2106
- const columnStylesFromContent = block.props.columnStyles;
2736
+ const columnsFromContent = block.props.columns ?? block.props.columnStyles;
2737
+ const expHasColumns = Array.isArray(exp.columns) || exp.columnStyles && typeof exp.columnStyles === "object" && !Array.isArray(exp.columnStyles);
2107
2738
  const hb = hydrateBlock({ type: "layout", id: block.id, props: exp }, layoutDepth);
2108
2739
  if (hb) {
2109
2740
  block.props = hb.props;
2110
- const merged = mergeLayoutColumnStylesMaps(columnStylesFromContent, block.props.columnStyles);
2111
- if (merged) block.props.columnStyles = merged;
2112
- else delete block.props.columnStyles;
2741
+ const merged = mergeLayoutColumns(
2742
+ columnsFromContent,
2743
+ expHasColumns ? block.props.columns : void 0
2744
+ );
2745
+ if (merged) block.props.columns = merged;
2746
+ else delete block.props.columns;
2747
+ delete block.props.columnStyles;
2113
2748
  if (typeof hb.id === "string" && hb.id) block.id = hb.id;
2114
2749
  }
2115
2750
  return;
2116
2751
  }
2752
+ if (t === "html") {
2753
+ const { content: expContent, ...expRest } = exp;
2754
+ Object.assign(block.props, expRest);
2755
+ normalizeBoxStyles(block.props, t);
2756
+ if (typeof expContent === "string") {
2757
+ block.content = normalizeRichHtmlForStorage(expContent);
2758
+ } else if (typeof block.content === "string") {
2759
+ block.content = normalizeRichHtmlForStorage(block.content);
2760
+ }
2761
+ block.props.content = typeof block.content === "string" ? block.content : "";
2762
+ return;
2763
+ }
2117
2764
  Object.assign(block.props, exp);
2118
2765
  normalizeBoxStyles(block.props, t);
2119
- if (t === "html" || t === "text") {
2766
+ if (t === "text") {
2120
2767
  block.props.content = normalizeRichHtmlForStorage(String(block.props.content ?? ""));
2121
2768
  }
2122
2769
  }
2123
- function exportEmailDocBlock(b, depth = 0) {
2124
- if (!b || typeof b !== "object") return { id: uid(), type: "text", content: {}, styles: {} };
2125
- const doc = internalBlockToEmailDoc(b, depth);
2126
- const out = { ...doc };
2127
- if (b.props && typeof b.props === "object" && !Array.isArray(b.props)) {
2128
- out.props = cloneJson(b.props);
2129
- }
2130
- if (b.type === "layout" && Array.isArray(b.props?.cells)) {
2131
- const baseContent = typeof doc.content === "object" && doc.content ? doc.content : {};
2132
- if (depth >= MAX_LAYOUT_TREE_DEPTH) {
2133
- const ratios = Array.isArray(b.props.ratios) && b.props.ratios.length ? b.props.ratios : [1];
2134
- out.content = { ...baseContent, cells: ratios.map(() => []) };
2135
- } else {
2136
- out.content = {
2137
- ...baseContent,
2138
- cells: b.props.cells.map(
2139
- (col) => Array.isArray(col) ? col.map((child) => exportEmailDocBlock(child, depth + 1)) : []
2140
- )
2141
- };
2142
- }
2143
- }
2144
- return out;
2145
- }
2146
2770
  function hydrateLayoutRow(row) {
2147
2771
  if (!row || typeof row !== "object") return row;
2148
2772
  const cells = (row.cells || []).map(
@@ -2185,8 +2809,85 @@ function jsonToHtml(designInput, opts = {}) {
2185
2809
  return designToHtml(d.rows, d.settings, opts);
2186
2810
  }
2187
2811
 
2812
+ // src/lib/htmlToEmailDesign.ts
2813
+ var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
2814
+ function extractHtmlForDesign(html) {
2815
+ const t = String(html ?? "").trim();
2816
+ if (!t) return "";
2817
+ const head = t.slice(0, 800).toLowerCase();
2818
+ if (!head.includes("<html") && !head.includes("<!doctype")) {
2819
+ return normalizeRichHtmlForStorage(t);
2820
+ }
2821
+ try {
2822
+ const doc = new DOMParser().parseFromString(t, "text/html");
2823
+ const body = doc.body;
2824
+ if (!body) return normalizeRichHtmlForStorage(t);
2825
+ return normalizeRichHtmlForStorage(body.innerHTML);
2826
+ } catch {
2827
+ return normalizeRichHtmlForStorage(t);
2828
+ }
2829
+ }
2830
+ function htmlToEmailDesignTemplate(html) {
2831
+ const inner = extractHtmlForDesign(html);
2832
+ if (!inner) return null;
2833
+ const doc = {
2834
+ type: "email_document",
2835
+ settings: {
2836
+ width: 600,
2837
+ backgroundColor: "#f1f5f9",
2838
+ contentBackgroundColor: "#ffffff",
2839
+ fontFamily: "Arial, Helvetica, sans-serif",
2840
+ lineHeightBase: 1.6,
2841
+ color: "#111827",
2842
+ responsive: true,
2843
+ rtl: false
2844
+ },
2845
+ rows: [
2846
+ {
2847
+ id: "row_html_import",
2848
+ type: "row",
2849
+ layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
2850
+ styles: {
2851
+ backgroundColor: "#ffffff",
2852
+ backgroundRepeat: "no-repeat",
2853
+ backgroundSize: "cover",
2854
+ padding: pad(24),
2855
+ textAlign: "left"
2856
+ },
2857
+ columns: [
2858
+ {
2859
+ id: "col_html_import",
2860
+ layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
2861
+ styles: { padding: pad(0), backgroundColor: "" },
2862
+ blocks: [
2863
+ {
2864
+ id: "block_html_import",
2865
+ type: "html",
2866
+ content: inner,
2867
+ props: { ...DEFAULT_BLOCK_PROPS.html }
2868
+ }
2869
+ ]
2870
+ }
2871
+ ]
2872
+ }
2873
+ ]
2874
+ };
2875
+ return doc;
2876
+ }
2877
+ function htmlToJson(html, pretty = false) {
2878
+ const doc = htmlToEmailDesignTemplate(html);
2879
+ if (!doc) return "";
2880
+ return pretty ? JSON.stringify(doc, null, 2) : JSON.stringify(doc);
2881
+ }
2882
+
2188
2883
  // src/editor/canvas/DesignCanvas.tsx
2189
- import { useState, useRef, useCallback, useEffect, Fragment } from "react";
2884
+ import {
2885
+ useState,
2886
+ useRef,
2887
+ useCallback,
2888
+ useEffect,
2889
+ Fragment
2890
+ } from "react";
2190
2891
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2191
2892
  function useLiveCountdown(endDate) {
2192
2893
  const [diff, setDiff] = useState(0);
@@ -2325,13 +3026,15 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
2325
3026
  )
2326
3027
  );
2327
3028
  if (type === "html") {
2328
- const emptyRich = isEffectivelyEmptyRichHtml(p.content);
3029
+ const richHtml = typeof block.content === "string" ? block.content : p.content;
3030
+ const emptyRich = isEffectivelyEmptyRichHtml(richHtml);
2329
3031
  return wrap(
2330
3032
  /* @__PURE__ */ jsxs2("div", { style: { position: "relative", minHeight: emptyRich && !preview ? "2.75em" : void 0 }, children: [
3033
+ /* @__PURE__ */ jsx3("style", { children: RICH_HTML_CONTENT_CSS }),
2331
3034
  /* @__PURE__ */ jsx3(
2332
3035
  "div",
2333
3036
  {
2334
- className: "email-editor-rich-canvas",
3037
+ className: "email-editor-rich-canvas email-rich-html-content",
2335
3038
  style: {
2336
3039
  fontSize: p.fontSize,
2337
3040
  color: p.color,
@@ -2342,7 +3045,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
2342
3045
  wordBreak: "break-word",
2343
3046
  minHeight: emptyRich && !preview ? "2.75em" : void 0
2344
3047
  },
2345
- dangerouslySetInnerHTML: { __html: p.content || "<p></p>" }
3048
+ dangerouslySetInnerHTML: { __html: richHtml || "" }
2346
3049
  }
2347
3050
  ),
2348
3051
  emptyRich && !preview ? /* @__PURE__ */ jsx3(
@@ -2390,7 +3093,7 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
2390
3093
  }
2391
3094
  }
2392
3095
  );
2393
- return wrap(/* @__PURE__ */ jsx3("div", { style: { textAlign: p.align }, children: p.link ? /* @__PURE__ */ jsx3("a", { href: p.link, target: p.linkTarget || "_blank", rel: "noopener", children: img }) : img }));
3096
+ return wrap(/* @__PURE__ */ jsx3("div", { style: { textAlign: p.align }, children: imageLinkActive(p) ? /* @__PURE__ */ jsx3("a", { href: p.link, target: p.linkTarget || "_blank", rel: "noopener", children: img }) : img }));
2394
3097
  }
2395
3098
  if (type === "button") return wrap(
2396
3099
  /* @__PURE__ */ jsx3("div", { style: { textAlign: p.fullWidth ? "center" : p.align }, children: /* @__PURE__ */ jsx3(
@@ -2563,7 +3266,7 @@ function NestedRowBlock({
2563
3266
  );
2564
3267
  },
2565
3268
  children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
2566
- const cS = p.columnStyles && p.columnStyles[ici] || {};
3269
+ const cS = getColumnPropsAt(p, ici);
2567
3270
  return /* @__PURE__ */ jsx3(
2568
3271
  "div",
2569
3272
  {
@@ -2621,7 +3324,9 @@ function BlockItem({
2621
3324
  onContextMenu,
2622
3325
  preview,
2623
3326
  C,
2624
- Line
3327
+ Line,
3328
+ dragAsRow = false,
3329
+ beginRowDrag
2625
3330
  }) {
2626
3331
  const wrapperRef = useRef(null);
2627
3332
  const bid = editorId ? `${editorId}-block-${cb.id}` : void 0;
@@ -2669,7 +3374,7 @@ function BlockItem({
2669
3374
  {
2670
3375
  "data-bidx": ci,
2671
3376
  draggable: !preview,
2672
- title: preview ? void 0 : "Click to select \xB7 drag to reorder or move",
3377
+ title: preview ? void 0 : dragAsRow ? "Click to select \xB7 drag to reorder section" : "Click to select \xB7 drag to reorder or move",
2673
3378
  onPointerDown: (e) => {
2674
3379
  if (preview || e.button !== 0) return;
2675
3380
  selectThisBlock(e);
@@ -2677,6 +3382,10 @@ function BlockItem({
2677
3382
  onDragStart: (e) => {
2678
3383
  if (preview) return;
2679
3384
  e.stopPropagation();
3385
+ if (dragAsRow && beginRowDrag) {
3386
+ beginRowDrag(e);
3387
+ return;
3388
+ }
2680
3389
  e.dataTransfer.effectAllowed = "move";
2681
3390
  e.dataTransfer.setData(
2682
3391
  "application/json",
@@ -2748,7 +3457,9 @@ function Cell({
2748
3457
  onDeleteContent,
2749
3458
  onContextMenu,
2750
3459
  preview,
2751
- C
3460
+ C,
3461
+ dragAsRow = false,
3462
+ beginRowDrag
2752
3463
  }) {
2753
3464
  const [insertAt, setInsertAt] = useState(null);
2754
3465
  const cellRef = useRef(null);
@@ -2763,6 +3474,14 @@ function Cell({
2763
3474
  return els.length;
2764
3475
  }, [blocks.length]);
2765
3476
  const handleDragOver = (e) => {
3477
+ try {
3478
+ const raw = e.dataTransfer.getData("application/json");
3479
+ if (raw) {
3480
+ const data = JSON.parse(raw);
3481
+ if (data.moveRowId) return;
3482
+ }
3483
+ } catch {
3484
+ }
2766
3485
  e.preventDefault();
2767
3486
  e.stopPropagation();
2768
3487
  setInsertAt(calcIdx(e.clientY));
@@ -2778,6 +3497,7 @@ function Cell({
2778
3497
  setInsertAt(null);
2779
3498
  try {
2780
3499
  const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
3500
+ if (data.moveRowId) return;
2781
3501
  if (data.contentType) {
2782
3502
  onDropContent(rowId, cellIdx, { kind: "new", contentType: data.contentType, insertAt: idx, nested: nestedPayload });
2783
3503
  } else if (data.moveContent) {
@@ -2857,7 +3577,9 @@ function Cell({
2857
3577
  onContextMenu,
2858
3578
  preview,
2859
3579
  C,
2860
- Line
3580
+ Line,
3581
+ dragAsRow: dragAsRow && blocks.length === 1 && ci === 0,
3582
+ beginRowDrag
2861
3583
  }
2862
3584
  ) }, cb.id))
2863
3585
  ]
@@ -2878,7 +3600,8 @@ function LayoutRow({
2878
3600
  onLayoutContextMenu,
2879
3601
  setSelectedRowId,
2880
3602
  preview = false,
2881
- rowDrag,
3603
+ rootContentRow = false,
3604
+ beginRowDrag,
2882
3605
  C
2883
3606
  }) {
2884
3607
  const rowStyle = {
@@ -2910,16 +3633,9 @@ function LayoutRow({
2910
3633
  onContextMenu: preview || !onLayoutContextMenu ? void 0 : (e) => {
2911
3634
  onLayoutContextMenu(e, row.id);
2912
3635
  },
2913
- draggable: rowDrag?.draggable === true,
2914
- onDragStart: rowDrag?.onDragStart,
2915
- onDragEnd: rowDrag?.onDragEnd,
2916
- title: rowDrag?.draggable ? "Click row \xB7 drag to reorder" : void 0,
2917
- style: {
2918
- ...rowStyle,
2919
- ...rowDrag?.draggable ? { cursor: "grab" } : {}
2920
- },
3636
+ style: rowStyle,
2921
3637
  children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: row.gap }, children: row.cells.map((cellBlocks, ci) => {
2922
- const cS = row.columnStyles && row.columnStyles[ci] || {};
3638
+ const cS = getColumnPropsAt(row, ci);
2923
3639
  return /* @__PURE__ */ jsx3(
2924
3640
  "div",
2925
3641
  {
@@ -2955,7 +3671,9 @@ function LayoutRow({
2955
3671
  onDeleteContent,
2956
3672
  onContextMenu,
2957
3673
  preview,
2958
- C
3674
+ C,
3675
+ dragAsRow: rootContentRow && ci === 0,
3676
+ beginRowDrag
2959
3677
  }
2960
3678
  )
2961
3679
  },
@@ -2972,10 +3690,11 @@ var CanvasRow = ({
2972
3690
  selectedRowId,
2973
3691
  selContentMeta: rowSelContentMeta,
2974
3692
  dragOver,
3693
+ draggingRowId,
2975
3694
  C,
2976
3695
  setDragOver,
2977
3696
  handleRowDrop,
2978
- setDraggingRowId,
3697
+ setRowDrag,
2979
3698
  setSelectedRowId,
2980
3699
  setSelContentId,
2981
3700
  setSelMeta,
@@ -2988,61 +3707,168 @@ var CanvasRow = ({
2988
3707
  }) => {
2989
3708
  const rowSelected = selectedRowId === row.id;
2990
3709
  const rid = (s) => editorId ? `${editorId}-${s}` : void 0;
2991
- return /* @__PURE__ */ jsxs2("div", { id: rid(`canvas-row-${row.id}`), children: [
2992
- /* @__PURE__ */ jsx3(
2993
- "div",
2994
- {
2995
- id: rid(`row-drop-before-${ri}`),
2996
- onDragOver: (e) => {
2997
- e.preventDefault();
2998
- setDragOver(ri);
2999
- },
3000
- onDragLeave: () => setDragOver(null),
3001
- onDrop: (e) => {
3002
- e.preventDefault();
3003
- handleRowDrop(ri, e);
3004
- },
3005
- style: { height: dragOver === ri ? 24 : 3, background: dragOver === ri ? `${C.accent}25` : "transparent", border: dragOver === ri ? `2px dashed ${C.accent}` : "2px solid transparent", borderRadius: 4, transition: "all .12s", margin: "2px 0" }
3710
+ const dropH = dragOver === ri || dragOver === ri + 1 ? 28 : 10;
3711
+ const rowBodyRef = useRef(null);
3712
+ const rootContentRow = isRootContentRow(row);
3713
+ const beginRowDrag = useCallback(
3714
+ (e) => {
3715
+ e.stopPropagation();
3716
+ e.dataTransfer.effectAllowed = "move";
3717
+ e.dataTransfer.setData(
3718
+ "application/json",
3719
+ JSON.stringify({ moveRowId: row.id })
3720
+ );
3721
+ setRowDrag(row.id);
3722
+ },
3723
+ [row.id, setRowDrag]
3724
+ );
3725
+ const rowDragOver = useCallback(
3726
+ (e) => {
3727
+ if (!draggingRowId || draggingRowId === row.id) return;
3728
+ e.preventDefault();
3729
+ e.stopPropagation();
3730
+ const el = rowBodyRef.current;
3731
+ if (!el) {
3732
+ setDragOver(ri);
3733
+ return;
3006
3734
  }
3007
- ),
3008
- /* @__PURE__ */ jsx3("div", { id: rid(`row-body-${row.id}`), style: { position: "relative", width: "100%", minWidth: 0, paddingLeft: 0, boxSizing: "border-box" }, children: /* @__PURE__ */ jsx3("div", { id: rid(`row-layout-wrap-${row.id}`), style: { width: "100%", minWidth: 0 }, children: /* @__PURE__ */ jsx3(
3009
- LayoutRow,
3010
- {
3011
- editorId,
3012
- row,
3013
- selected: rowSelected,
3014
- setSelectedRowId,
3015
- onClick: () => {
3016
- setSelectedRowId(row.id);
3017
- setSelContentId(null);
3018
- setSelMeta(null);
3019
- },
3020
- selectedContentKey: selContentId,
3021
- selContentMeta: rowSelContentMeta,
3022
- onSelectContent: selectContent,
3023
- onDropContent: dropContent,
3024
- onDeleteContent: deleteContent,
3025
- onContextMenu: handleContextMenu,
3026
- onLayoutContextMenu: handleLayoutContextMenu,
3027
- rowDrag: {
3028
- draggable: true,
3029
- onDragStart: (e) => {
3030
- e.stopPropagation();
3031
- setDraggingRowId(row.id);
3032
- },
3033
- onDragEnd: () => setDraggingRowId(null)
3034
- },
3035
- C
3735
+ const rect = el.getBoundingClientRect();
3736
+ setDragOver(e.clientY < rect.top + rect.height / 2 ? ri : ri + 1);
3737
+ },
3738
+ [draggingRowId, row.id, ri, setDragOver]
3739
+ );
3740
+ const rowDrop = useCallback(
3741
+ (e) => {
3742
+ if (!draggingRowId) return;
3743
+ e.preventDefault();
3744
+ e.stopPropagation();
3745
+ const el = rowBodyRef.current;
3746
+ let targetIdx = ri;
3747
+ if (el) {
3748
+ const rect = el.getBoundingClientRect();
3749
+ targetIdx = e.clientY < rect.top + rect.height / 2 ? ri : ri + 1;
3036
3750
  }
3037
- ) }) })
3038
- ] });
3751
+ handleRowDrop(targetIdx, e);
3752
+ },
3753
+ [draggingRowId, ri, handleRowDrop]
3754
+ );
3755
+ return /* @__PURE__ */ jsxs2(
3756
+ "div",
3757
+ {
3758
+ id: rid(`canvas-row-${row.id}`),
3759
+ style: { display: "flex", alignItems: "stretch", gap: 0, margin: "2px 0" },
3760
+ children: [
3761
+ /* @__PURE__ */ jsx3(
3762
+ "div",
3763
+ {
3764
+ draggable: true,
3765
+ title: "Drag to reorder section",
3766
+ onDragStart: beginRowDrag,
3767
+ onDragEnd: () => setRowDrag(null),
3768
+ onPointerDown: (e) => e.stopPropagation(),
3769
+ onClick: (e) => {
3770
+ e.stopPropagation();
3771
+ setSelectedRowId(row.id);
3772
+ setSelContentId(null);
3773
+ setSelMeta(null);
3774
+ },
3775
+ onContextMenu: (e) => {
3776
+ e.preventDefault();
3777
+ e.stopPropagation();
3778
+ handleLayoutContextMenu(e, row.id);
3779
+ },
3780
+ style: {
3781
+ width: 20,
3782
+ flexShrink: 0,
3783
+ cursor: "grab",
3784
+ display: "flex",
3785
+ alignItems: "center",
3786
+ justifyContent: "center",
3787
+ color: rowSelected ? C.accent : C.muted,
3788
+ fontSize: 12,
3789
+ fontWeight: 700,
3790
+ letterSpacing: -3,
3791
+ userSelect: "none",
3792
+ opacity: rowSelected ? 1 : 0.5
3793
+ },
3794
+ children: "\u22EE\u22EE"
3795
+ }
3796
+ ),
3797
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0 }, children: [
3798
+ /* @__PURE__ */ jsx3(
3799
+ "div",
3800
+ {
3801
+ id: rid(`row-drop-before-${ri}`),
3802
+ onDragOver: (e) => {
3803
+ e.preventDefault();
3804
+ e.stopPropagation();
3805
+ setDragOver(ri);
3806
+ },
3807
+ onDragLeave: () => setDragOver(null),
3808
+ onDrop: (e) => handleRowDrop(ri, e),
3809
+ style: {
3810
+ height: dropH,
3811
+ background: dragOver === ri ? `${C.accent}25` : "transparent",
3812
+ border: dragOver === ri ? `2px dashed ${C.accent}` : "2px solid transparent",
3813
+ borderRadius: 4,
3814
+ transition: "all .12s",
3815
+ boxSizing: "border-box"
3816
+ }
3817
+ }
3818
+ ),
3819
+ /* @__PURE__ */ jsx3(
3820
+ "div",
3821
+ {
3822
+ ref: rowBodyRef,
3823
+ id: rid(`row-body-${row.id}`),
3824
+ onDragOver: rowDragOver,
3825
+ onDrop: rowDrop,
3826
+ style: {
3827
+ position: "relative",
3828
+ width: "100%",
3829
+ minWidth: 0,
3830
+ boxSizing: "border-box",
3831
+ outline: draggingRowId && draggingRowId !== row.id && (dragOver === ri || dragOver === ri + 1) ? `2px dashed ${C.accent}` : void 0,
3832
+ outlineOffset: 2,
3833
+ borderRadius: 6
3834
+ },
3835
+ children: /* @__PURE__ */ jsx3("div", { id: rid(`row-layout-wrap-${row.id}`), style: { width: "100%", minWidth: 0 }, children: /* @__PURE__ */ jsx3(
3836
+ LayoutRow,
3837
+ {
3838
+ editorId,
3839
+ row,
3840
+ selected: rowSelected,
3841
+ setSelectedRowId,
3842
+ onClick: () => {
3843
+ setSelectedRowId(row.id);
3844
+ setSelContentId(null);
3845
+ setSelMeta(null);
3846
+ },
3847
+ selectedContentKey: selContentId,
3848
+ selContentMeta: rowSelContentMeta,
3849
+ onSelectContent: selectContent,
3850
+ onDropContent: dropContent,
3851
+ onDeleteContent: deleteContent,
3852
+ onContextMenu: handleContextMenu,
3853
+ onLayoutContextMenu: handleLayoutContextMenu,
3854
+ rootContentRow,
3855
+ beginRowDrag,
3856
+ C
3857
+ }
3858
+ ) })
3859
+ }
3860
+ )
3861
+ ] })
3862
+ ]
3863
+ }
3864
+ );
3039
3865
  };
3040
3866
 
3041
3867
  // src/ReactEmailEditor.tsx
3042
3868
  import { Image as ImageIcon2, Repeat as RepeatIcon, Scaling as Scaling2, Crosshair as Crosshair2, Blend as GradientIcon2, Droplets as ColorIcon } from "lucide-react";
3043
3869
 
3044
3870
  // src/editor/properties/PropertyEditors.tsx
3045
- import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo, Fragment as Fragment3 } from "react";
3871
+ import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo } from "react";
3046
3872
  import {
3047
3873
  AlignCenter as AlignCenter2,
3048
3874
  AlignJustify as AlignJustify2,
@@ -3059,7 +3885,7 @@ import {
3059
3885
  // src/editor/properties/TextRichEditor.tsx
3060
3886
  import { useEffect as useEffect2, useId, useState as useState2 } from "react";
3061
3887
  import { createPortal } from "react-dom";
3062
- import { useEditor, EditorContent } from "@tiptap/react";
3888
+ import { useEditor, EditorContent, useEditorState } from "@tiptap/react";
3063
3889
  import StarterKit from "@tiptap/starter-kit";
3064
3890
  import Placeholder from "@tiptap/extension-placeholder";
3065
3891
  import TextAlign from "@tiptap/extension-text-align";
@@ -3157,6 +3983,10 @@ function paddingToCss(pad3, fallback) {
3157
3983
  var FONT_SIZE_PX = [10, 11, 12, 13, 14, 15, 16, 18, 20, 24, 28, 32, 36, 42, 48, 56, 64, 72];
3158
3984
  var LINE_HEIGHT_OPTS = ["1", "1.15", "1.25", "1.5", "1.65", "1.75", "2", "2.5"];
3159
3985
  var LETTER_SPACING_OPTS = ["-0.5px", "0px", "0.5px", "1px", "1.5px", "2px", "3px", "4px"];
3986
+ function rgbChannelToHex(n) {
3987
+ const v = Math.max(0, Math.min(255, Math.round(n)));
3988
+ return v.toString(16).padStart(2, "0");
3989
+ }
3160
3990
  function hexForColorInput(color) {
3161
3991
  if (!color || typeof color !== "string") return "#000000";
3162
3992
  const c = color.trim();
@@ -3169,8 +3999,34 @@ function hexForColorInput(color) {
3169
3999
  }
3170
4000
  return c.slice(0, 7);
3171
4001
  }
4002
+ const rgb = c.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i);
4003
+ if (rgb) {
4004
+ return `#${rgbChannelToHex(+rgb[1])}${rgbChannelToHex(+rgb[2])}${rgbChannelToHex(+rgb[3])}`;
4005
+ }
3172
4006
  return "#000000";
3173
4007
  }
4008
+ function textStyleColorFromEditor(editor) {
4009
+ const ts = editor.getAttributes("textStyle");
4010
+ if (ts.color && String(ts.color).trim()) return String(ts.color).trim();
4011
+ const stored = editor.state.storedMarks;
4012
+ if (stored) {
4013
+ for (const m of stored) {
4014
+ if (m.type.name === "textStyle" && m.attrs.color) {
4015
+ return String(m.attrs.color).trim();
4016
+ }
4017
+ }
4018
+ }
4019
+ const { from, to } = editor.state.selection;
4020
+ if (from === to) return null;
4021
+ const domAt = editor.view.domAtPos(from);
4022
+ let el = domAt.node.nodeType === Node.TEXT_NODE ? domAt.node.parentElement : domAt.node;
4023
+ while (el && el !== editor.view.dom) {
4024
+ const inline = el.style?.color?.trim();
4025
+ if (inline) return inline;
4026
+ el = el.parentElement;
4027
+ }
4028
+ return null;
4029
+ }
3174
4030
  function parsePxSize(raw) {
3175
4031
  if (!raw) return "";
3176
4032
  const s = String(raw).trim();
@@ -3237,13 +4093,22 @@ function toolbarIconBtn(C, active) {
3237
4093
  }
3238
4094
  function ToolbarTypographyControls({ editor, C }) {
3239
4095
  const sel = toolbarSelect(C);
3240
- const ts = editor.getAttributes("textStyle");
4096
+ const { ts, align, textColor } = useEditorState({
4097
+ editor,
4098
+ selector: ({ editor: ed }) => {
4099
+ const attrs = ed.getAttributes("textStyle");
4100
+ return {
4101
+ ts: attrs,
4102
+ align: currentBlockTextAlign(ed),
4103
+ textColor: textStyleColorFromEditor(ed)
4104
+ };
4105
+ }
4106
+ });
3241
4107
  const fontFamily = ts.fontFamily || "";
3242
4108
  const sizeVal = parsePxSize(ts.fontSize);
3243
4109
  const lhVal = normalizeLineHeightAttr(ts.lineHeight);
3244
4110
  const lsVal = normalizeLetterSpacingAttr(ts.letterSpacing);
3245
4111
  const lsSelectValue = LETTER_SPACING_OPTS.includes(lsVal) ? lsVal : lsVal || "";
3246
- const align = currentBlockTextAlign(editor);
3247
4112
  return /* @__PURE__ */ jsxs3(Fragment2, { children: [
3248
4113
  /* @__PURE__ */ jsxs3(
3249
4114
  "select",
@@ -3326,9 +4191,10 @@ function ToolbarTypographyControls({ editor, C }) {
3326
4191
  {
3327
4192
  type: "color",
3328
4193
  title: "Text color",
3329
- value: hexForColorInput(ts.color),
4194
+ value: hexForColorInput(textColor ?? ts.color),
3330
4195
  onChange: (e) => {
3331
- void editor.chain().focus().setColor(e.target.value).run();
4196
+ const hex = e.target.value;
4197
+ void editor.chain().focus().setColor(hex).run();
3332
4198
  },
3333
4199
  style: {
3334
4200
  width: 28,
@@ -3701,7 +4567,7 @@ function TextRichEditor({
3701
4567
  }
3702
4568
  }),
3703
4569
  TextStyle,
3704
- Color,
4570
+ Color.configure({ types: ["textStyle"] }),
3705
4571
  FontFamily,
3706
4572
  FontSize,
3707
4573
  LineHeight,
@@ -4878,11 +5744,141 @@ function BlockSurfaceBgInspector({
4878
5744
  ] }) : null
4879
5745
  ] });
4880
5746
  }
5747
+ function HtmlBlockRichPanel({
5748
+ block,
5749
+ onChange,
5750
+ C
5751
+ }) {
5752
+ const p = block.props;
5753
+ const { IS } = useIS(C);
5754
+ const [mode, setMode] = useState3("visual");
5755
+ const [htmlDraft, setHtmlDraft] = useState3("");
5756
+ useEffect3(() => {
5757
+ setMode("visual");
5758
+ }, [block.id]);
5759
+ const body = normalizeRichHtmlForStorage(typeof block.content === "string" ? block.content : "");
5760
+ const applyHtml = (html) => {
5761
+ const norm = normalizeRichHtmlForStorage(html);
5762
+ onChange({ ...block, content: norm, props: { ...p, content: norm } });
5763
+ };
5764
+ const tabBtn = (active) => ({
5765
+ flex: 1,
5766
+ padding: "7px 10px",
5767
+ borderRadius: 6,
5768
+ border: `1px solid ${C.border}`,
5769
+ background: active ? C.accent : C.surface,
5770
+ color: active ? "#ffffff" : C.text,
5771
+ fontSize: 12,
5772
+ fontWeight: 700,
5773
+ cursor: "pointer"
5774
+ });
5775
+ return /* @__PURE__ */ jsx6(PR, { label: "Content", C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
5776
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8 }, children: [
5777
+ /* @__PURE__ */ jsx6(
5778
+ "button",
5779
+ {
5780
+ type: "button",
5781
+ style: tabBtn(mode === "visual"),
5782
+ onClick: () => {
5783
+ if (mode === "html") applyHtml(htmlDraft);
5784
+ setMode("visual");
5785
+ },
5786
+ children: "Rich text"
5787
+ }
5788
+ ),
5789
+ /* @__PURE__ */ jsx6(
5790
+ "button",
5791
+ {
5792
+ type: "button",
5793
+ style: tabBtn(mode === "html"),
5794
+ onClick: () => {
5795
+ setHtmlDraft(body);
5796
+ setMode("html");
5797
+ },
5798
+ children: "HTML"
5799
+ }
5800
+ )
5801
+ ] }),
5802
+ mode === "visual" ? /* @__PURE__ */ jsx6(
5803
+ TextRichEditor,
5804
+ {
5805
+ value: body,
5806
+ onChange: applyHtml,
5807
+ typography: {
5808
+ fontSize: p.fontSize,
5809
+ color: p.color,
5810
+ align: p.align,
5811
+ fontFamily: p.fontFamily,
5812
+ lineHeight: p.lineHeight,
5813
+ letterSpacing: p.letterSpacing,
5814
+ padding: p.padding
5815
+ },
5816
+ placeholder: "Write your rich content\u2026",
5817
+ headerTitle: "Rich editor",
5818
+ C
5819
+ },
5820
+ block.id
5821
+ ) : /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
5822
+ /* @__PURE__ */ jsx6(
5823
+ "textarea",
5824
+ {
5825
+ spellCheck: false,
5826
+ value: htmlDraft,
5827
+ onChange: (e) => setHtmlDraft(e.target.value),
5828
+ placeholder: "<p>Your HTML\u2026</p>",
5829
+ style: {
5830
+ ...IS,
5831
+ minHeight: 220,
5832
+ resize: "vertical",
5833
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
5834
+ fontSize: 12,
5835
+ lineHeight: 1.45
5836
+ }
5837
+ }
5838
+ ),
5839
+ /* @__PURE__ */ jsxs4("span", { style: { fontSize: 10, color: C.muted, lineHeight: 1.45 }, children: [
5840
+ "Raw HTML. Switch to ",
5841
+ /* @__PURE__ */ jsx6("strong", { children: "Rich text" }),
5842
+ " to apply and preview in the visual editor."
5843
+ ] }),
5844
+ /* @__PURE__ */ jsx6(
5845
+ "button",
5846
+ {
5847
+ type: "button",
5848
+ onClick: () => applyHtml(htmlDraft),
5849
+ style: {
5850
+ alignSelf: "flex-start",
5851
+ background: C.surface,
5852
+ border: `1px solid ${C.border}`,
5853
+ color: C.accent,
5854
+ borderRadius: 5,
5855
+ cursor: "pointer",
5856
+ fontSize: 11,
5857
+ padding: "6px 12px",
5858
+ fontWeight: 600
5859
+ },
5860
+ children: "Apply (normalize)"
5861
+ }
5862
+ )
5863
+ ] })
5864
+ ] }) });
5865
+ }
4881
5866
  function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4882
5867
  const { IS, CI } = useIS(C);
4883
5868
  const imageFileRef = useRef2(null);
4884
5869
  const p = block.props;
4885
- const set = (k, v) => onChange({ ...block, props: { ...p, [k]: v } });
5870
+ const set = (k, v) => {
5871
+ if (block.type === "html" && k === "content") {
5872
+ onChange({ ...block, content: v, props: { ...p, content: v } });
5873
+ return;
5874
+ }
5875
+ if (block.type === "html") {
5876
+ const body = typeof block.content === "string" ? block.content : typeof p.content === "string" ? p.content : "";
5877
+ onChange({ ...block, props: { ...p, [k]: v, content: body } });
5878
+ return;
5879
+ }
5880
+ onChange({ ...block, props: { ...p, [k]: v } });
5881
+ };
4886
5882
  const Num = (k, lbl, mn = 0, mx = 300) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: /* @__PURE__ */ jsx6("input", { type: "number", style: IS, value: p[k] ?? 0, min: mn, max: mx, onChange: (e) => set(k, +e.target.value) }) });
4887
5883
  const Txt = (k, lbl, multi = false) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: multi ? /* @__PURE__ */ jsx6("textarea", { style: { ...IS, minHeight: 68, resize: "vertical" }, value: p[k] || "", onChange: (e) => set(k, e.target.value) }) : /* @__PURE__ */ jsx6("input", { type: "text", style: IS, value: p[k] || "", onChange: (e) => set(k, e.target.value) }) });
4888
5884
  const Col = (k, lbl) => /* @__PURE__ */ jsx6(PR, { label: lbl, C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
@@ -4954,10 +5950,10 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4954
5950
  "textarea",
4955
5951
  {
4956
5952
  style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
4957
- value: normalizeRichHtmlForStorage(p.content),
4958
- onChange: (e) => set("content", normalizeRichHtmlForStorage(e.target.value)),
5953
+ value: String(p.content ?? ""),
5954
+ onChange: (e) => set("content", e.target.value),
4959
5955
  spellCheck: false,
4960
- placeholder: "e.g. <p>Your message</p>"
5956
+ placeholder: "Write your text\u2026"
4961
5957
  },
4962
5958
  block.id
4963
5959
  ) }),
@@ -4976,25 +5972,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4976
5972
  ] });
4977
5973
  case "html":
4978
5974
  return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
4979
- /* @__PURE__ */ jsx6(Fragment3, { children: /* @__PURE__ */ jsx6(
4980
- TextRichEditor,
4981
- {
4982
- value: normalizeRichHtmlForStorage(p.content),
4983
- onChange: (html) => set("content", html),
4984
- typography: {
4985
- fontSize: p.fontSize,
4986
- color: p.color,
4987
- align: p.align,
4988
- fontFamily: p.fontFamily,
4989
- lineHeight: p.lineHeight,
4990
- letterSpacing: p.letterSpacing,
4991
- padding: p.padding
4992
- },
4993
- placeholder: "Write your rich content\u2026",
4994
- headerTitle: "Rich editor",
4995
- C
4996
- }
4997
- ) }, block.id),
5975
+ /* @__PURE__ */ jsx6(HtmlBlockRichPanel, { block, onChange, C }),
4998
5976
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4999
5977
  /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5000
5978
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
@@ -5014,8 +5992,25 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5014
5992
  "Object position"
5015
5993
  ] }), C, children: /* @__PURE__ */ jsx6("select", { style: IS, value: p.objectPosition || "center", onChange: (e) => set("objectPosition", e.target.value), children: ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"].map((v) => /* @__PURE__ */ jsx6("option", { value: v, children: v }, v)) }) }),
5016
5994
  Txt("alt", "Alt Text"),
5017
- Txt("link", "Link URL"),
5018
- Sel("linkTarget", "Open In", ["_blank", "_self"]),
5995
+ /* @__PURE__ */ jsx6(PR, { label: "Link image", C, children: /* @__PURE__ */ jsxs4("label", { style: { display: "flex", alignItems: "center", gap: 7, cursor: "pointer" }, children: [
5996
+ /* @__PURE__ */ jsx6(
5997
+ "input",
5998
+ {
5999
+ type: "checkbox",
6000
+ checked: !!(p.linkEnabled ?? (p.link && String(p.link).trim())),
6001
+ onChange: (e) => {
6002
+ if (e.target.checked) set("linkEnabled", true);
6003
+ else onChange({ ...block, props: { ...p, linkEnabled: false, link: "" } });
6004
+ },
6005
+ style: { width: 15, height: 15, accentColor: C.accent }
6006
+ }
6007
+ ),
6008
+ /* @__PURE__ */ jsx6("span", { style: { color: C.muted, fontSize: 12 }, children: p.linkEnabled ?? (p.link && String(p.link).trim()) ? "On" : "Off" })
6009
+ ] }) }),
6010
+ p.linkEnabled ?? (p.link && String(p.link).trim()) ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
6011
+ Txt("link", "Link URL"),
6012
+ Sel("linkTarget", "Open In", ["_blank", "_self"])
6013
+ ] }) : null,
5019
6014
  /* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5020
6015
  /* @__PURE__ */ jsx6(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
5021
6016
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
@@ -5365,7 +6360,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5365
6360
  bgPosition: p.bgPosition ?? "center",
5366
6361
  bgGradient: p.bgGradient ?? null,
5367
6362
  cells: p.cells || [],
5368
- columnStyles: p.columnStyles
6363
+ columns: getColumns(p)
5369
6364
  },
5370
6365
  onChange: (row) => onChange({
5371
6366
  ...block,
@@ -5383,7 +6378,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5383
6378
  bgGradient: row.bgGradient,
5384
6379
  ratios: [...row.ratios || [1]],
5385
6380
  cells: row.cells || [],
5386
- columnStyles: row.columnStyles
6381
+ columns: row.columns ?? getColumns(row)
5387
6382
  }
5388
6383
  }),
5389
6384
  onClose,
@@ -5402,8 +6397,8 @@ function inferLayoutRowBgStep(r) {
5402
6397
  if (String(r.bgColor || "").trim()) return "solid";
5403
6398
  return null;
5404
6399
  }
5405
- function inferColumnBgStep(columnStyles, selCol) {
5406
- const cs = (columnStyles || {})[selCol] || {};
6400
+ function inferColumnBgStep(container, selCol) {
6401
+ const cs = getColumnPropsAt(container, selCol);
5407
6402
  if (cs.bgGradient) return "gradient";
5408
6403
  if (String(cs.bgImage || "").trim()) return "image";
5409
6404
  if (String(cs.bgColor || "").trim()) return "solid";
@@ -5415,6 +6410,10 @@ function LayoutRowEditor({
5415
6410
  onClose,
5416
6411
  onUpload,
5417
6412
  initialCol = null,
6413
+ rowIndex = -1,
6414
+ rowCount = 1,
6415
+ onMoveUp,
6416
+ onMoveDown,
5418
6417
  C,
5419
6418
  /** Shown under the inspector header when editing a Layout block */
5420
6419
  customizationHint
@@ -5427,8 +6426,8 @@ function LayoutRowEditor({
5427
6426
  const [colBgPickerMode, setColBgPickerMode] = useState3(null);
5428
6427
  const inferredRowBg = useMemo(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
5429
6428
  const inferredColBg = useMemo(
5430
- () => inferColumnBgStep(row.columnStyles, selCol),
5431
- [row.columnStyles, selCol]
6429
+ () => inferColumnBgStep(row, selCol),
6430
+ [row.columns, row.columnStyles, selCol]
5432
6431
  );
5433
6432
  const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
5434
6433
  const colBgUiStep = inferredColBg ?? colBgPickerMode;
@@ -5467,13 +6466,12 @@ function LayoutRowEditor({
5467
6466
  onChange({ ...row, cols: n, preset: "custom", cells, ratios });
5468
6467
  };
5469
6468
  const updCol = (i, upd) => {
5470
- const styles = { ...row.columnStyles || {} };
5471
- styles[i] = { ...styles[i] || {}, ...upd };
5472
- set("columnStyles", styles);
6469
+ const columns = patchColumnPropsAt(row, i, upd);
6470
+ onChange(withColumnsOnly(row, columns));
5473
6471
  };
5474
6472
  const setColBgMode = (mode) => {
5475
6473
  setColBgPickerMode(mode);
5476
- const cs = (row.columnStyles || {})[selCol] || {};
6474
+ const cs = getColumnPropsAt(row, selCol);
5477
6475
  if (mode === "solid") {
5478
6476
  updCol(selCol, { bgGradient: null, bgImage: "" });
5479
6477
  return;
@@ -5505,7 +6503,51 @@ function LayoutRowEditor({
5505
6503
  set("bgGradient", { angle: 135, colors: ["#0ea5e9", "#6366f1", "#ec4899"], stops: [0, 50, 100] });
5506
6504
  }
5507
6505
  };
6506
+ const canMoveUp = rowIndex > 0;
6507
+ const canMoveDown = rowIndex >= 0 && rowIndex < rowCount - 1;
5508
6508
  return /* @__PURE__ */ jsxs4(Fragment4, { children: [
6509
+ onMoveUp && onMoveDown ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 6, marginBottom: 12 }, children: [
6510
+ /* @__PURE__ */ jsx6(
6511
+ "button",
6512
+ {
6513
+ type: "button",
6514
+ disabled: !canMoveUp,
6515
+ onClick: onMoveUp,
6516
+ style: {
6517
+ flex: 1,
6518
+ padding: "8px 10px",
6519
+ fontSize: 12,
6520
+ fontWeight: 600,
6521
+ borderRadius: 8,
6522
+ border: `1px solid ${C.border}`,
6523
+ background: canMoveUp ? C.surface : C.canvas,
6524
+ color: canMoveUp ? C.text : C.muted,
6525
+ cursor: canMoveUp ? "pointer" : "not-allowed"
6526
+ },
6527
+ children: "\u2191 Move section up"
6528
+ }
6529
+ ),
6530
+ /* @__PURE__ */ jsx6(
6531
+ "button",
6532
+ {
6533
+ type: "button",
6534
+ disabled: !canMoveDown,
6535
+ onClick: onMoveDown,
6536
+ style: {
6537
+ flex: 1,
6538
+ padding: "8px 10px",
6539
+ fontSize: 12,
6540
+ fontWeight: 600,
6541
+ borderRadius: 8,
6542
+ border: `1px solid ${C.border}`,
6543
+ background: canMoveDown ? C.surface : C.canvas,
6544
+ color: canMoveDown ? C.text : C.muted,
6545
+ cursor: canMoveDown ? "pointer" : "not-allowed"
6546
+ },
6547
+ children: "\u2193 Move section down"
6548
+ }
6549
+ )
6550
+ ] }) : null,
5509
6551
  customizationHint ? /* @__PURE__ */ jsx6(
5510
6552
  "div",
5511
6553
  {
@@ -5655,15 +6697,15 @@ function LayoutRowEditor({
5655
6697
  /* @__PURE__ */ jsx6(
5656
6698
  ColorSwatchGrid,
5657
6699
  {
5658
- value: (row.columnStyles || {})[selCol]?.bgColor || "",
6700
+ value: getColumnPropsAt(row, selCol)?.bgColor || "",
5659
6701
  C,
5660
6702
  compact: true,
5661
6703
  onPick: (hex) => updCol(selCol, { bgColor: hex === "#ffffff" ? "" : hex })
5662
6704
  }
5663
6705
  ),
5664
6706
  /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 5, alignItems: "center" }, children: [
5665
- /* @__PURE__ */ jsx6("input", { type: "color", style: CI, value: (row.columnStyles || {})[selCol]?.bgColor || "#ffffff", onChange: (e) => updCol(selCol, { bgColor: e.target.value === "#ffffff" ? "" : e.target.value }) }),
5666
- /* @__PURE__ */ jsx6("input", { type: "text", style: { ...IS, width: 120 }, placeholder: "transparent", value: (row.columnStyles || {})[selCol]?.bgColor || "", onChange: (e) => updCol(selCol, { bgColor: e.target.value }) })
6707
+ /* @__PURE__ */ jsx6("input", { type: "color", style: CI, value: getColumnPropsAt(row, selCol)?.bgColor || "#ffffff", onChange: (e) => updCol(selCol, { bgColor: e.target.value === "#ffffff" ? "" : e.target.value }) }),
6708
+ /* @__PURE__ */ jsx6("input", { type: "text", style: { ...IS, width: 120 }, placeholder: "transparent", value: getColumnPropsAt(row, selCol)?.bgColor || "", onChange: (e) => updCol(selCol, { bgColor: e.target.value }) })
5667
6709
  ] })
5668
6710
  ] }) }) : null,
5669
6711
  colBgUiStep === "image" ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
@@ -5671,7 +6713,7 @@ function LayoutRowEditor({
5671
6713
  /* @__PURE__ */ jsx6(ImageIcon, { size: 13, strokeWidth: 2.25, "aria-hidden": true }),
5672
6714
  "bg image"
5673
6715
  ] }), C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
5674
- /* @__PURE__ */ jsx6("input", { type: "text", style: IS, placeholder: "https://\u2026", value: (row.columnStyles || {})[selCol]?.bgImage || "", onChange: (e) => updCol(selCol, { bgImage: e.target.value }) }),
6716
+ /* @__PURE__ */ jsx6("input", { type: "text", style: IS, placeholder: "https://\u2026", value: getColumnPropsAt(row, selCol)?.bgImage || "", onChange: (e) => updCol(selCol, { bgImage: e.target.value }) }),
5675
6717
  onUpload && /* @__PURE__ */ jsxs4("div", { style: { marginTop: 0 }, children: [
5676
6718
  /* @__PURE__ */ jsx6(
5677
6719
  "input",
@@ -5709,18 +6751,18 @@ function LayoutRowEditor({
5709
6751
  ] })
5710
6752
  ] }) }),
5711
6753
  /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, marginBottom: 12 }, children: [
5712
- /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "fit", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: (row.columnStyles || {})[selCol]?.bgSize || "cover", onChange: (e) => updCol(selCol, { bgSize: e.target.value }), children: [
6754
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "fit", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgSize || "cover", onChange: (e) => updCol(selCol, { bgSize: e.target.value }), children: [
5713
6755
  /* @__PURE__ */ jsx6("option", { value: "auto", children: "Auto" }),
5714
6756
  /* @__PURE__ */ jsx6("option", { value: "cover", children: "Cover" }),
5715
6757
  /* @__PURE__ */ jsx6("option", { value: "contain", children: "Contain" })
5716
6758
  ] }) }) }),
5717
- /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "repeat", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: (row.columnStyles || {})[selCol]?.bgRepeat || "no-repeat", onChange: (e) => updCol(selCol, { bgRepeat: e.target.value }), children: [
6759
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "repeat", C, children: /* @__PURE__ */ jsxs4("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgRepeat || "no-repeat", onChange: (e) => updCol(selCol, { bgRepeat: e.target.value }), children: [
5718
6760
  /* @__PURE__ */ jsx6("option", { value: "no-repeat", children: "No repeat" }),
5719
6761
  /* @__PURE__ */ jsx6("option", { value: "repeat", children: "Repeat" }),
5720
6762
  /* @__PURE__ */ jsx6("option", { value: "repeat-x", children: "Repeat X" }),
5721
6763
  /* @__PURE__ */ jsx6("option", { value: "repeat-y", children: "Repeat Y" })
5722
6764
  ] }) }) }),
5723
- /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "align", C, children: /* @__PURE__ */ jsx6("select", { style: IS, value: (row.columnStyles || {})[selCol]?.bgPosition || "center", onChange: (e) => updCol(selCol, { bgPosition: e.target.value }), children: POS_OPTS.map((p) => /* @__PURE__ */ jsx6("option", { value: p, children: p }, p)) }) }) })
6765
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(PR, { label: "align", C, children: /* @__PURE__ */ jsx6("select", { style: IS, value: getColumnPropsAt(row, selCol)?.bgPosition || "center", onChange: (e) => updCol(selCol, { bgPosition: e.target.value }), children: POS_OPTS.map((p) => /* @__PURE__ */ jsx6("option", { value: p, children: p }, p)) }) }) })
5724
6766
  ] })
5725
6767
  ] }) : null,
5726
6768
  colBgUiStep === "gradient" ? /* @__PURE__ */ jsxs4("details", { open: true, style: { marginBottom: 12 }, children: [
@@ -5729,15 +6771,15 @@ function LayoutRowEditor({
5729
6771
  DynamicGradientField,
5730
6772
  {
5731
6773
  label: "gradient",
5732
- value: (row.columnStyles || {})[selCol]?.bgGradient,
6774
+ value: getColumnPropsAt(row, selCol)?.bgGradient,
5733
6775
  onChange: (g) => updCol(selCol, { bgGradient: g }),
5734
6776
  defaults: { colors: ["#0ea5e9", "#6366f1", "#ec4899"] },
5735
6777
  C
5736
6778
  }
5737
6779
  ) })
5738
6780
  ] }) : null,
5739
- /* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
5740
- /* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: (row.columnStyles || {})[selCol]?.borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
6781
+ /* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: getColumnPropsAt(row, selCol).padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
6782
+ /* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: getColumnPropsAt(row, selCol).borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
5741
6783
  ] })
5742
6784
  ] })
5743
6785
  ] });
@@ -6534,6 +7576,11 @@ var ReactEmailEditorComponent = forwardRef(
6534
7576
  const [selContentMeta, setSelMeta] = useState6(null);
6535
7577
  const [dragOver, setDragOver] = useState6(null);
6536
7578
  const [draggingRowId, setDraggingRowId] = useState6(null);
7579
+ const draggingRowIdRef = useRef6(null);
7580
+ const setRowDrag = useCallback2((id) => {
7581
+ draggingRowIdRef.current = id;
7582
+ setDraggingRowId(id);
7583
+ }, []);
6537
7584
  const [history, setHistory] = useState6([]);
6538
7585
  const [future, setFuture] = useState6([]);
6539
7586
  const [showPreviewModal, setShowPreview] = useState6(false);
@@ -6568,10 +7615,17 @@ var ReactEmailEditorComponent = forwardRef(
6568
7615
  });
6569
7616
  const [pageBgUiStep, setPageBgUiStep] = useState6(null);
6570
7617
  const [contentBgUiStep, setContentBgUiStep] = useState6(null);
7618
+ const pageBgLastSolidRef = useRef6("#f1f5f9");
7619
+ const pageBgIsOff = !String(settings.bgColor ?? "").trim() && !String(settings.bgImage ?? "").trim() && !settings.bgGradient;
6571
7620
  const applyPageBgMode = (mode) => {
6572
7621
  setPageBgUiStep(mode);
6573
7622
  if (mode === "solid") {
6574
- setSettings((s) => ({ ...s, bgGradient: null, bgImage: "" }));
7623
+ setSettings((s) => {
7624
+ const next = { ...s, bgGradient: null, bgImage: "" };
7625
+ const raw = typeof next.bgColor === "string" ? next.bgColor.trim() : "";
7626
+ if (!raw) next.bgColor = pageBgLastSolidRef.current || "#f1f5f9";
7627
+ return next;
7628
+ });
6575
7629
  return;
6576
7630
  }
6577
7631
  if (mode === "image") {
@@ -6681,7 +7735,7 @@ var ReactEmailEditorComponent = forwardRef(
6681
7735
  return () => window.removeEventListener("keydown", handler);
6682
7736
  }, [rows, history, future, selContentMeta]);
6683
7737
  const buildApi = useCallback2(() => ({
6684
- loadJson(input) {
7738
+ loadJson(input, options2) {
6685
7739
  setJsonLoading(true);
6686
7740
  requestAnimationFrame(() => {
6687
7741
  requestAnimationFrame(() => {
@@ -6694,7 +7748,14 @@ var ReactEmailEditorComponent = forwardRef(
6694
7748
  return;
6695
7749
  }
6696
7750
  }
6697
- const norm = normalizeEmailDesignInput(parsed);
7751
+ const coerced = coerceEmailDocumentInput(parsed);
7752
+ if (options2?.mode === "append") {
7753
+ const extra = emailDocumentToEditorRows(coerced ?? parsed);
7754
+ if (!extra?.length) return;
7755
+ setRows((prev) => [...prev, ...extra]);
7756
+ return;
7757
+ }
7758
+ const norm = normalizeEmailDesignInput(coerced ?? parsed);
6698
7759
  if (!norm) return;
6699
7760
  setRows(norm.rows);
6700
7761
  const ns = norm.settings;
@@ -6731,6 +7792,8 @@ var ReactEmailEditorComponent = forwardRef(
6731
7792
  }, []);
6732
7793
  const handleRowDrop = (targetIdx, e) => {
6733
7794
  if (e) {
7795
+ e.preventDefault();
7796
+ e.stopPropagation();
6734
7797
  try {
6735
7798
  const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
6736
7799
  if (data.layoutPresetKey) {
@@ -6749,18 +7812,28 @@ var ReactEmailEditorComponent = forwardRef(
6749
7812
  } catch {
6750
7813
  }
6751
7814
  }
6752
- if (draggingRowId) {
7815
+ const moveId = draggingRowIdRef.current ?? draggingRowId ?? (() => {
7816
+ if (!e) return null;
7817
+ try {
7818
+ const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
7819
+ return typeof data.moveRowId === "string" ? data.moveRowId : null;
7820
+ } catch {
7821
+ return null;
7822
+ }
7823
+ })();
7824
+ if (moveId) {
6753
7825
  mutate((prev) => {
6754
- const fi = prev.findIndex((r) => r.id === draggingRowId);
7826
+ const fi = prev.findIndex((r) => r.id === moveId);
6755
7827
  if (fi === -1) return prev;
6756
7828
  const next = [...prev];
6757
7829
  const [m] = next.splice(fi, 1);
6758
- next.splice(targetIdx > fi ? targetIdx - 1 : targetIdx, 0, m);
7830
+ const insertAt = targetIdx > fi ? targetIdx - 1 : targetIdx;
7831
+ next.splice(Math.max(0, Math.min(insertAt, next.length)), 0, m);
6759
7832
  return next;
6760
7833
  });
6761
7834
  }
6762
7835
  setDragOver(null);
6763
- setDraggingRowId(null);
7836
+ setRowDrag(null);
6764
7837
  };
6765
7838
  const addRow = (preset) => mutate((prev) => [...prev, makeLayoutRow(preset)]);
6766
7839
  const deleteRow = (id) => {
@@ -6890,8 +7963,27 @@ var ReactEmailEditorComponent = forwardRef(
6890
7963
  }
6891
7964
  }
6892
7965
  };
7966
+ const rootContentPreset = LAYOUT_PRESETS.find((p) => p.cols === 1) ?? LAYOUT_PRESETS[0];
7967
+ const insertRootContentBlock = (contentType) => {
7968
+ const { row: newRow, block: nb } = makeRootContentRow(contentType, rootContentPreset);
7969
+ const anchorRowId = selContentMeta?.rowId ?? selectedRowId ?? null;
7970
+ mutate((prev) => {
7971
+ if (anchorRowId) {
7972
+ const i = prev.findIndex((r) => r.id === anchorRowId);
7973
+ if (i >= 0) {
7974
+ const next = [...prev];
7975
+ next.splice(i + 1, 0, newRow);
7976
+ return next;
7977
+ }
7978
+ }
7979
+ return [...prev, newRow];
7980
+ });
7981
+ setSelectedRowId(null);
7982
+ setSelContentId(nb.id);
7983
+ setSelMeta({ rowId: newRow.id, cellIdx: 0, contentIdx: 0 });
7984
+ };
6893
7985
  const insertBlockFromLibrary = (contentType) => {
6894
- if (!rows.length) return;
7986
+ const isLayoutType = contentType === "layout" || contentType === "nestedRow";
6895
7987
  if (selContentMeta?.inner) {
6896
7988
  const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
6897
7989
  dropContent(rowId, cellIdx, {
@@ -6902,30 +7994,26 @@ var ReactEmailEditorComponent = forwardRef(
6902
7994
  });
6903
7995
  return;
6904
7996
  }
6905
- if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
6906
- const r = rows.find((x) => x.id === selContentMeta.rowId);
6907
- if (r && selContentMeta.cellIdx < r.cells.length) {
6908
- dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
6909
- kind: "new",
6910
- contentType,
6911
- insertAt: null
6912
- });
6913
- return;
7997
+ if (isLayoutType) {
7998
+ if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0 && typeof selContentMeta.contentIdx === "number" && selContentMeta.contentIdx >= 0) {
7999
+ const r = rows.find((x) => x.id === selContentMeta.rowId);
8000
+ if (r && selContentMeta.cellIdx < r.cells.length) {
8001
+ dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
8002
+ kind: "new",
8003
+ contentType,
8004
+ insertAt: null
8005
+ });
8006
+ return;
8007
+ }
6914
8008
  }
8009
+ addRow(rootContentPreset);
8010
+ return;
6915
8011
  }
6916
- if (selectedRowId) {
6917
- const targetRow = rows.find((r) => r.id === selectedRowId);
6918
- if (targetRow) {
6919
- dropContent(targetRow.id, 0, {
6920
- kind: "new",
6921
- contentType,
6922
- insertAt: null
6923
- });
6924
- return;
6925
- }
8012
+ if (!rows.length) {
8013
+ insertRootContentBlock(contentType);
8014
+ return;
6926
8015
  }
6927
- const main = rows[0];
6928
- dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
8016
+ insertRootContentBlock(contentType);
6929
8017
  };
6930
8018
  const deleteContent = (rowId, cellIdx, ci, inner = null) => {
6931
8019
  if (!inner) {
@@ -7520,6 +8608,7 @@ var ReactEmailEditorComponent = forwardRef(
7520
8608
  e.preventDefault();
7521
8609
  try {
7522
8610
  const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
8611
+ if (data.moveRowId) return;
7523
8612
  if (data.layoutPresetKey) {
7524
8613
  const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
7525
8614
  if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
@@ -7574,6 +8663,7 @@ var ReactEmailEditorComponent = forwardRef(
7574
8663
  e.stopPropagation();
7575
8664
  try {
7576
8665
  const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
8666
+ if (data.moveRowId) return;
7577
8667
  if (data.contentType && typeof data.contentType === "string") {
7578
8668
  insertBlockFromLibrary(data.contentType);
7579
8669
  }
@@ -7595,10 +8685,11 @@ var ReactEmailEditorComponent = forwardRef(
7595
8685
  selectedRowId,
7596
8686
  selContentMeta,
7597
8687
  dragOver,
8688
+ draggingRowId,
7598
8689
  C,
7599
8690
  setDragOver,
7600
8691
  handleRowDrop,
7601
- setDraggingRowId,
8692
+ setRowDrag,
7602
8693
  setSelectedRowId,
7603
8694
  setSelContentId,
7604
8695
  setSelMeta,
@@ -7616,14 +8707,20 @@ var ReactEmailEditorComponent = forwardRef(
7616
8707
  id: eid("canvas-row-drop-end"),
7617
8708
  onDragOver: (e) => {
7618
8709
  e.preventDefault();
8710
+ e.stopPropagation();
7619
8711
  setDragOver(rows.length);
7620
8712
  },
7621
8713
  onDragLeave: () => setDragOver(null),
7622
- onDrop: (e) => {
7623
- e.preventDefault();
7624
- handleRowDrop(rows.length, e);
7625
- },
7626
- style: { height: dragOver === rows.length ? 24 : 3, background: dragOver === rows.length ? `${C.accent}25` : "transparent", border: dragOver === rows.length ? `2px dashed ${C.accent}` : "2px solid transparent", borderRadius: 4, transition: "all .12s", margin: "2px 0" }
8714
+ onDrop: (e) => handleRowDrop(rows.length, e),
8715
+ style: {
8716
+ height: dragOver === rows.length ? 28 : 10,
8717
+ background: dragOver === rows.length ? `${C.accent}25` : "transparent",
8718
+ border: dragOver === rows.length ? `2px dashed ${C.accent}` : "2px solid transparent",
8719
+ borderRadius: 4,
8720
+ transition: "all .12s",
8721
+ margin: "2px 0",
8722
+ boxSizing: "border-box"
8723
+ }
7627
8724
  }
7628
8725
  )
7629
8726
  ]
@@ -7735,6 +8832,10 @@ var ReactEmailEditorComponent = forwardRef(
7735
8832
  onClose: () => setSelectedRowId(null),
7736
8833
  onUpload,
7737
8834
  initialCol: selContentMeta?.rowId === selectedRowId ? selContentMeta.cellIdx : null,
8835
+ rowIndex: rows.findIndex((r) => r.id === selectedRowId),
8836
+ rowCount: rows.length,
8837
+ onMoveUp: () => selectedRow && moveRow(selectedRow.id, -1),
8838
+ onMoveDown: () => selectedRow && moveRow(selectedRow.id, 1),
7738
8839
  C
7739
8840
  }
7740
8841
  ) })
@@ -7911,7 +9012,40 @@ var ReactEmailEditorComponent = forwardRef(
7911
9012
  /* @__PURE__ */ jsx11("summary", { style: railSectionSummary, children: "Background" }),
7912
9013
  /* @__PURE__ */ jsxs9("div", { style: railSectionBody, children: [
7913
9014
  /* @__PURE__ */ jsx11("div", { style: { fontSize: 10, color: C.muted, fontWeight: 800, textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 6 }, children: "Page background" }),
7914
- /* @__PURE__ */ jsx11(BgModeButtons, { value: pageBgUiStep, onChange: applyPageBgMode, C }),
9015
+ /* @__PURE__ */ jsx11("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }, children: /* @__PURE__ */ jsxs9("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", userSelect: "none" }, children: [
9016
+ /* @__PURE__ */ jsx11(
9017
+ "input",
9018
+ {
9019
+ type: "checkbox",
9020
+ checked: pageBgIsOff,
9021
+ onChange: (e) => {
9022
+ const off = e.target.checked;
9023
+ if (off) {
9024
+ const cur = String(settings.bgColor ?? "").trim();
9025
+ if (cur) pageBgLastSolidRef.current = cur;
9026
+ setPageBgUiStep(null);
9027
+ setSettings((s) => ({ ...s, bgColor: "", bgImage: "", bgGradient: null }));
9028
+ } else {
9029
+ setPageBgUiStep("solid");
9030
+ setSettings((s) => ({
9031
+ ...s,
9032
+ bgColor: pageBgLastSolidRef.current || "#f1f5f9"
9033
+ }));
9034
+ }
9035
+ },
9036
+ style: { width: 15, height: 15, accentColor: C.accent, cursor: "pointer" }
9037
+ }
9038
+ ),
9039
+ /* @__PURE__ */ jsx11("span", { style: { color: C.muted, fontSize: 12 }, children: "No page background" })
9040
+ ] }) }),
9041
+ /* @__PURE__ */ jsx11(
9042
+ BgModeButtons,
9043
+ {
9044
+ value: pageBgIsOff ? null : pageBgUiStep,
9045
+ onChange: applyPageBgMode,
9046
+ C
9047
+ }
9048
+ ),
7915
9049
  pageBgUiStep === null ? /* @__PURE__ */ jsx11("div", { style: { fontSize: 11, color: C.muted, marginBottom: 12, lineHeight: 1.45 }, children: "Choose Color, Gradient, or Image \u2014 then the matching settings appear below." }) : null,
7916
9050
  pageBgUiStep === "solid" ? /* @__PURE__ */ jsx11(
7917
9051
  ColorField,
@@ -7921,7 +9055,10 @@ var ReactEmailEditorComponent = forwardRef(
7921
9055
  "bg color"
7922
9056
  ] }),
7923
9057
  value: settings.bgColor,
7924
- onChange: (v) => setSettings((s) => ({ ...s, bgColor: v })),
9058
+ onChange: (v) => {
9059
+ pageBgLastSolidRef.current = v;
9060
+ setSettings((s) => ({ ...s, bgColor: v }));
9061
+ },
7925
9062
  placeholder: "#f1f5f9",
7926
9063
  C
7927
9064
  }
@@ -8162,80 +9299,17 @@ var ReactEmailEditorComponent = forwardRef(
8162
9299
  );
8163
9300
  }
8164
9301
  );
8165
- var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml });
8166
-
8167
- // src/lib/htmlToEmailDesign.ts
8168
- var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
8169
- function extractHtmlForDesign(html) {
8170
- const t = String(html ?? "").trim();
8171
- if (!t) return "";
8172
- const head = t.slice(0, 800).toLowerCase();
8173
- if (!head.includes("<html") && !head.includes("<!doctype")) {
8174
- return normalizeRichHtmlForStorage(t);
8175
- }
8176
- try {
8177
- const doc = new DOMParser().parseFromString(t, "text/html");
8178
- const body = doc.body;
8179
- if (!body) return normalizeRichHtmlForStorage(t);
8180
- return normalizeRichHtmlForStorage(body.innerHTML);
8181
- } catch {
8182
- return normalizeRichHtmlForStorage(t);
8183
- }
8184
- }
8185
- function htmlToEmailDesignTemplate(html) {
8186
- const inner = extractHtmlForDesign(html);
8187
- if (!inner) return null;
8188
- const doc = {
8189
- type: "email_document",
8190
- settings: {
8191
- width: 600,
8192
- backgroundColor: "#f1f5f9",
8193
- contentBackgroundColor: "#ffffff",
8194
- fontFamily: "Arial, Helvetica, sans-serif",
8195
- lineHeightBase: 1.6,
8196
- color: "#111827",
8197
- responsive: true,
8198
- rtl: false
8199
- },
8200
- rows: [
8201
- {
8202
- id: "row_html_import",
8203
- type: "row",
8204
- layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
8205
- styles: {
8206
- backgroundColor: "#ffffff",
8207
- backgroundRepeat: "no-repeat",
8208
- backgroundSize: "cover",
8209
- padding: pad(24),
8210
- textAlign: "left"
8211
- },
8212
- columns: [
8213
- {
8214
- id: "col_html_import",
8215
- layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
8216
- styles: { padding: pad(0), backgroundColor: "" },
8217
- blocks: [
8218
- {
8219
- id: "block_html_import",
8220
- type: "html",
8221
- content: { html: inner },
8222
- styles: {}
8223
- }
8224
- ]
8225
- }
8226
- ]
8227
- }
8228
- ]
8229
- };
8230
- return doc;
8231
- }
9302
+ var ReactEmailEditor2 = Object.assign(ReactEmailEditorComponent, { jsonToHtml, htmlToJson });
8232
9303
  export {
8233
9304
  PreviewModal as EmailPreviewModal,
8234
9305
  ReactEmailEditor2 as ReactEmailEditor,
8235
9306
  base64ToUtf8,
9307
+ canonicalizeEmailDocument,
9308
+ coerceEmailDocumentInput,
8236
9309
  DEVICES as emailPreviewDevices,
8237
9310
  extractHtmlForDesign,
8238
9311
  htmlToEmailDesignTemplate,
9312
+ htmlToJson,
8239
9313
  jsonToHtml,
8240
9314
  utf8ToBase64
8241
9315
  };