react-email-studio 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -34,6 +34,8 @@ __export(index_exports, {
34
34
  ReactEmailEditor: () => ReactEmailEditor,
35
35
  base64ToUtf8: () => base64ToUtf8,
36
36
  emailPreviewDevices: () => DEVICES,
37
+ extractHtmlForDesign: () => extractHtmlForDesign,
38
+ htmlToEmailDesignTemplate: () => htmlToEmailDesignTemplate,
37
39
  jsonToHtml: () => jsonToHtml,
38
40
  utf8ToBase64: () => utf8ToBase64
39
41
  });
@@ -168,7 +170,7 @@ var I18N = {
168
170
  blockPaletteGroupActions: "Buttons & links",
169
171
  blockPaletteGroupWidgets: "Widgets",
170
172
  closePanel: "Close",
171
- canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
173
+ canvasEmptyHint: "Drag row layouts or blocks from the library. Drop blocks into a column or onto the mail preview area.",
172
174
  emailContentSettings: "Email content",
173
175
  loadingDesign: "Loading design\u2026"
174
176
  },
@@ -1123,7 +1125,7 @@ function rowToHtml(row) {
1123
1125
  const r = row.ratios[i] ?? 1;
1124
1126
  const cs = row.columnStyles && row.columnStyles[i] ? row.columnStyles[i] : {};
1125
1127
  const colShell = emailSurfaceBgCss(cs);
1126
- const pad = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
1128
+ const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
1127
1129
  const o = cs.padding;
1128
1130
  const t = numOr2(o.top, 0);
1129
1131
  const rgt = numOr2(o.right, t);
@@ -1139,7 +1141,7 @@ function rowToHtml(row) {
1139
1141
  const bl = numOr2(x.bl, tr2);
1140
1142
  return `border-radius:${tl}px ${tr2}px ${br}px ${bl}px;`;
1141
1143
  })() : typeof cs.borderRadius === "number" && Number.isFinite(cs.borderRadius) ? `border-radius:${cs.borderRadius}px;` : "";
1142
- return `<div style="flex:${r} 1 0;min-width:0;box-sizing:border-box;${pad}${rad}${colShell}">${cell.map(blockToHtml).join("")}</div>`;
1144
+ return `<div style="flex:${r} 1 0;min-width:0;box-sizing:border-box;${pad3}${rad}${colShell}">${cell.map(blockToHtml).join("")}</div>`;
1143
1145
  }).join("")}</div>`;
1144
1146
  const innerTrim = inner.replace(/>\s+</g, "><").trim();
1145
1147
  const body = innerTrim || "&nbsp;";
@@ -1359,8 +1361,8 @@ function layoutColumnBorderRadiusForDoc(r) {
1359
1361
  }
1360
1362
  return 0;
1361
1363
  }
1362
- function columnPaddingNonZero(pad) {
1363
- return pad.top !== 0 || pad.right !== 0 || pad.bottom !== 0 || pad.left !== 0;
1364
+ function columnPaddingNonZero(pad3) {
1365
+ return pad3.top !== 0 || pad3.right !== 0 || pad3.bottom !== 0 || pad3.left !== 0;
1364
1366
  }
1365
1367
  function columnRadiusNonZero(r) {
1366
1368
  if (typeof r === "number") return r !== 0;
@@ -1717,9 +1719,9 @@ function mapBlockToInternal(b, layoutDepth = 0) {
1717
1719
  if (asNum(s.fontWeight) != null) block.props.fontWeight = s.fontWeight;
1718
1720
  if (asNum(s.borderRadius) != null) block.props.borderRadius = s.borderRadius;
1719
1721
  if (s.padding && typeof s.padding === "object") {
1720
- const pad = s.padding;
1721
- block.props.paddingV = asNum(pad.top) ?? block.props.paddingV;
1722
- block.props.paddingH = asNum(pad.right) ?? block.props.paddingH;
1722
+ const pad3 = s.padding;
1723
+ block.props.paddingV = asNum(pad3.top) ?? block.props.paddingV;
1724
+ block.props.paddingH = asNum(pad3.right) ?? block.props.paddingH;
1723
1725
  }
1724
1726
  if (asStr(s.textAlign)) block.props.align = s.textAlign;
1725
1727
  break;
@@ -2234,9 +2236,15 @@ function useLiveCountdown(endDate) {
2234
2236
  const s = Math.floor(diff % 6e4 / 1e3);
2235
2237
  return [d, h, m, s];
2236
2238
  }
2237
- function editChromeBorder(_C, _preview) {
2238
- return {};
2239
+ function layoutSplitChromeBorder(_C, preview) {
2240
+ if (preview) return {};
2241
+ return {
2242
+ border: "1px dotted rgba(148, 163, 184, 0.5)",
2243
+ boxSizing: "border-box"
2244
+ };
2239
2245
  }
2246
+ var CONTENT_BLOCK_HOVER_BORDER = "1px dotted rgba(148, 163, 184, 0.5)";
2247
+ var EMPTY_CELL_COLUMN_MIN_H = "min(320px, 42vh)";
2240
2248
  function blockLinkCaptureHandler(preview, e) {
2241
2249
  if (preview) return;
2242
2250
  const t = e.target;
@@ -2268,6 +2276,7 @@ function radiusCssLayout(v) {
2268
2276
  return "0px";
2269
2277
  }
2270
2278
  function ContentBlock({ block, selected, onClick, preview, C }) {
2279
+ const [hover, setHover] = (0, import_react.useState)(false);
2271
2280
  const { type, props: p } = block;
2272
2281
  const numOr2 = (v, fb) => typeof v === "number" && Number.isFinite(v) ? v : fb;
2273
2282
  const boxPx = (v) => {
@@ -2311,14 +2320,19 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
2311
2320
  onClick: preview ? void 0 : onClick || void 0,
2312
2321
  onClickCapture: (e) => blockLinkCaptureHandler(preview, e),
2313
2322
  onAuxClickCapture: (e) => blockLinkCaptureHandler(preview, e),
2323
+ onMouseEnter: preview ? void 0 : () => setHover(true),
2324
+ onMouseLeave: preview ? void 0 : () => setHover(false),
2314
2325
  style: {
2315
2326
  cursor: preview ? "default" : onClick ? "pointer" : "default",
2316
2327
  outline: selected && !preview ? `2px solid ${C.accent}` : "2px solid transparent",
2317
2328
  outlineOffset: 1,
2318
2329
  borderRadius: 3,
2319
- transition: "outline .12s",
2330
+ transition: "outline .12s, border-color .12s",
2320
2331
  position: "relative",
2321
- ...editChromeBorder(C, preview),
2332
+ ...preview ? {} : {
2333
+ border: hover ? CONTENT_BLOCK_HOVER_BORDER : "1px solid transparent",
2334
+ boxSizing: "border-box"
2335
+ },
2322
2336
  ...shellBg,
2323
2337
  padding: paddingCss(p.padding),
2324
2338
  margin: marginCss(p.margin)
@@ -2558,7 +2572,7 @@ function NestedRowBlock({
2558
2572
  outline: rowSelected ? `2px solid ${C.accent}` : "2px solid transparent",
2559
2573
  outlineOffset: 1,
2560
2574
  transition: "outline .12s",
2561
- ...editChromeBorder(C, preview),
2575
+ ...layoutSplitChromeBorder(C, preview),
2562
2576
  ...backgroundLayerStyle({
2563
2577
  bgColor: p.bgColor,
2564
2578
  bgImage: p.bgImage,
@@ -2590,7 +2604,10 @@ function NestedRowBlock({
2590
2604
  style: {
2591
2605
  flex: p.ratios?.[ici] ?? 1,
2592
2606
  minWidth: 0,
2593
- ...editChromeBorder(C, preview),
2607
+ minHeight: preview ? void 0 : EMPTY_CELL_COLUMN_MIN_H,
2608
+ display: "flex",
2609
+ flexDirection: "column",
2610
+ ...layoutSplitChromeBorder(C, preview),
2594
2611
  ...backgroundLayerStyle(cS),
2595
2612
  padding: paddingCssLayout(cS.padding),
2596
2613
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -2807,6 +2824,7 @@ function Cell({
2807
2824
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { position: "absolute", left: -4, top: "50%", transform: "translateY(-50%)", width: 8, height: 8, borderRadius: "50%", background: C.accent } }),
2808
2825
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { position: "absolute", right: -4, top: "50%", transform: "translateY(-50%)", width: 8, height: 8, borderRadius: "50%", background: C.accent } })
2809
2826
  ] }) : null;
2827
+ const cellEmpty = !preview && blocks.length === 0;
2810
2828
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2811
2829
  "div",
2812
2830
  {
@@ -2816,17 +2834,43 @@ function Cell({
2816
2834
  onDragLeave: handleDragLeave,
2817
2835
  onDrop: handleDrop,
2818
2836
  style: {
2819
- flex: 1,
2820
- minHeight: preview ? void 0 : 44,
2837
+ flex: cellEmpty ? 1 : void 0,
2838
+ display: "flex",
2839
+ flexDirection: "column",
2840
+ minHeight: preview ? void 0 : cellEmpty ? 0 : 44,
2821
2841
  borderRadius: 4,
2822
- ...preview ? {} : insertAt !== null ? { border: `1px dotted ${C.accent}`, background: `${C.accent}0a` } : editChromeBorder(C, false),
2823
- background: insertAt !== null ? `${C.accent}0a` : "transparent",
2842
+ ...preview ? {} : insertAt !== null && !cellEmpty ? { border: `1px dotted ${C.accent}`, background: `${C.accent}0a` } : {},
2843
+ background: insertAt !== null && !cellEmpty ? `${C.accent}0a` : "transparent",
2824
2844
  transition: "border-color .15s,background .15s",
2825
2845
  padding: preview ? 0 : 0,
2826
2846
  position: "relative"
2827
2847
  },
2828
2848
  children: [
2829
- !preview && blocks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: "10px 4px", textAlign: "center", color: insertAt !== null ? C.accent2 : C.muted, fontSize: 10, userSelect: "none", fontWeight: insertAt !== null ? 700 : 400, transition: "color .15s" }, children: insertAt !== null ? "Release to drop" : "Drop here" }),
2849
+ cellEmpty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2850
+ "div",
2851
+ {
2852
+ style: {
2853
+ flex: 1,
2854
+ display: "flex",
2855
+ alignItems: "center",
2856
+ justifyContent: "center",
2857
+ margin: 4,
2858
+ borderRadius: 8,
2859
+ boxSizing: "border-box",
2860
+ textAlign: "center",
2861
+ userSelect: "none",
2862
+ transition: "color .15s, border-color .15s, background .15s, box-shadow .15s",
2863
+ border: insertAt !== null ? `2px dashed ${C.accent}` : `2px dashed ${C.border}`,
2864
+ background: insertAt !== null ? `${C.accent}14` : `${C.canvas}`,
2865
+ boxShadow: insertAt !== null ? `inset 0 0 0 1px ${C.accent}22` : `inset 0 0 0 1px ${C.border}99`,
2866
+ color: insertAt !== null ? C.accent2 : C.muted,
2867
+ fontSize: 11,
2868
+ fontWeight: insertAt !== null ? 700 : 500,
2869
+ letterSpacing: "0.02em"
2870
+ },
2871
+ children: insertAt !== null ? "Release to drop" : "Drop here"
2872
+ }
2873
+ ),
2830
2874
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { idx: 0 }),
2831
2875
  blocks.map((cb, ci) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2832
2876
  BlockItem,
@@ -2877,7 +2921,6 @@ function LayoutRow({
2877
2921
  transition: "outline .12s",
2878
2922
  padding: row.padding,
2879
2923
  position: "relative",
2880
- ...editChromeBorder(C, preview),
2881
2924
  ...backgroundLayerStyle({
2882
2925
  bgColor: row.bgColor,
2883
2926
  bgImage: row.bgImage,
@@ -2916,8 +2959,9 @@ function LayoutRow({
2916
2959
  id: editorId ? `${editorId}-col-${row.id}-${ci}` : void 0,
2917
2960
  style: {
2918
2961
  flex: row.ratios[ci] ?? 1,
2919
- minHeight: 50,
2920
- ...editChromeBorder(C, preview),
2962
+ minHeight: preview ? 50 : EMPTY_CELL_COLUMN_MIN_H,
2963
+ display: "flex",
2964
+ flexDirection: "column",
2921
2965
  ...backgroundLayerStyle(cS),
2922
2966
  padding: paddingCssLayout(cS.padding),
2923
2967
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -3091,13 +3135,13 @@ var FONTS = [
3091
3135
  "Trebuchet MS,sans-serif",
3092
3136
  "Impact,sans-serif"
3093
3137
  ];
3094
- function paddingToCss(pad, fallback) {
3095
- if (typeof pad === "number" && Number.isFinite(pad)) return `${Math.max(0, pad)}px`;
3096
- if (pad && typeof pad === "object" && !Array.isArray(pad)) {
3097
- const t = typeof pad.top === "number" && Number.isFinite(pad.top) ? pad.top : fallback;
3098
- const r = typeof pad.right === "number" && Number.isFinite(pad.right) ? pad.right : t;
3099
- const b = typeof pad.bottom === "number" && Number.isFinite(pad.bottom) ? pad.bottom : t;
3100
- const l = typeof pad.left === "number" && Number.isFinite(pad.left) ? pad.left : r;
3138
+ function paddingToCss(pad3, fallback) {
3139
+ if (typeof pad3 === "number" && Number.isFinite(pad3)) return `${Math.max(0, pad3)}px`;
3140
+ if (pad3 && typeof pad3 === "object" && !Array.isArray(pad3)) {
3141
+ const t = typeof pad3.top === "number" && Number.isFinite(pad3.top) ? pad3.top : fallback;
3142
+ const r = typeof pad3.right === "number" && Number.isFinite(pad3.right) ? pad3.right : t;
3143
+ const b = typeof pad3.bottom === "number" && Number.isFinite(pad3.bottom) ? pad3.bottom : t;
3144
+ const l = typeof pad3.left === "number" && Number.isFinite(pad3.left) ? pad3.left : r;
3101
3145
  return `${t}px ${r}px ${b}px ${l}px`;
3102
3146
  }
3103
3147
  return `${fallback}px`;
@@ -4893,7 +4937,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4893
4937
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
4894
4938
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Line height", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", style: useIS(C).IS, min: 0.8, max: 4, step: 0.05, value: p.lineHeight ?? 1.2, onChange: (e) => set("lineHeight", +e.target.value) }) }),
4895
4939
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4896
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
4940
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4897
4941
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4898
4942
  ] });
4899
4943
  case "text":
@@ -4919,7 +4963,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4919
4963
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Line height", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", style: useIS(C).IS, min: 1, max: 4, step: 0.05, value: p.lineHeight ?? 1.65, onChange: (e) => set("lineHeight", +e.target.value) }) }),
4920
4964
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
4921
4965
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4922
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
4966
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4923
4967
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4924
4968
  ] });
4925
4969
  case "html":
@@ -4944,7 +4988,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4944
4988
  }
4945
4989
  ) }, block.id),
4946
4990
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4947
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
4991
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4948
4992
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4949
4993
  ] });
4950
4994
  case "image":
@@ -4967,7 +5011,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4967
5011
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
4968
5012
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
4969
5013
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4970
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5014
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4971
5015
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4972
5016
  ] });
4973
5017
  case "button":
@@ -4987,7 +5031,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4987
5031
  Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
4988
5032
  Tog("fullWidth", "Full Width"),
4989
5033
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "backdrop", p, set, C, onUpload, syncKey: block.id }),
4990
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5034
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4991
5035
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4992
5036
  ] });
4993
5037
  case "link":
@@ -5002,7 +5046,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5002
5046
  Sel("fontFamily", "Font", FONTS2),
5003
5047
  Tog("underline", "Underline"),
5004
5048
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5005
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5049
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5006
5050
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5007
5051
  ] });
5008
5052
  case "divider":
@@ -5011,14 +5055,14 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5011
5055
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Thickness", value: p.thickness ?? 1, onChange: (n) => set("thickness", n), min: 1, max: 20, step: 1, C }),
5012
5056
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DividerStyleButtons, { value: p.style, onChange: (s) => set("style", s), C }),
5013
5057
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5014
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5058
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5015
5059
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5016
5060
  ] });
5017
5061
  case "spacer":
5018
5062
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5019
5063
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Height", value: p.height ?? 24, onChange: (n) => set("height", n), min: 4, max: 300, step: 1, C }),
5020
5064
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5021
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5065
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5022
5066
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5023
5067
  ] });
5024
5068
  case "social":
@@ -5067,7 +5111,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5067
5111
  Sel("shape", "Shape", ["circle", "square"]),
5068
5112
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Gap", value: p.gap ?? 6, onChange: (n) => set("gap", n), min: 0, max: 40, step: 1, C }),
5069
5113
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5070
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5114
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5071
5115
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5072
5116
  ] });
5073
5117
  case "video":
@@ -5127,7 +5171,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5127
5171
  ] }),
5128
5172
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Height", value: p.height ?? 280, onChange: (n) => set("height", n), min: 80, max: 720, step: 1, C }),
5129
5173
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5130
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5174
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5131
5175
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5132
5176
  ] });
5133
5177
  case "menu":
@@ -5153,7 +5197,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5153
5197
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Font size", value: p.fontSize ?? 14, onChange: (n) => set("fontSize", n), min: 8, max: 48, step: 1, C }),
5154
5198
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Item gap", value: p.gap ?? 20, onChange: (n) => set("gap", n), min: 0, max: 80, step: 1, C }),
5155
5199
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5156
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5200
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5157
5201
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5158
5202
  ] });
5159
5203
  case "timer": {
@@ -5193,7 +5237,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5193
5237
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5194
5238
  Tog("showLabels", "Show Labels"),
5195
5239
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "backdrop", p, set, C, onUpload, syncKey: block.id }),
5196
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5240
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5197
5241
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5198
5242
  ] });
5199
5243
  }
@@ -5290,8 +5334,8 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5290
5334
  Tog("striped", "Striped rows"),
5291
5335
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Font size", value: p.fontSize ?? 13, onChange: (n) => set("fontSize", n), min: 10, max: 24, step: 1, C }),
5292
5336
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5293
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5294
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Cell padding", value: p.cellPadding, onChange: (pad) => set("cellPadding", pad), C }),
5337
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5338
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Cell padding", value: p.cellPadding, onChange: (pad3) => set("cellPadding", pad3), C }),
5295
5339
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5296
5340
  ] });
5297
5341
  case "layout":
@@ -5684,7 +5728,7 @@ function LayoutRowEditor({
5684
5728
  }
5685
5729
  ) })
5686
5730
  ] }) : null,
5687
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad) => updCol(selCol, { padding: pad }), C }),
5731
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
5688
5732
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { label: "Column radius", value: (row.columnStyles || {})[selCol]?.borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
5689
5733
  ] })
5690
5734
  ] })
@@ -5961,8 +6005,8 @@ function MobilePhoneScaleSlot({ variantKey, children }) {
5961
6005
  const pw = phone.offsetWidth;
5962
6006
  const ph = phone.offsetHeight;
5963
6007
  if (cw < 12 || ch < 12 || pw < 12 || ph < 12) return;
5964
- const pad = 20;
5965
- const raw = Math.min(1, (cw - pad) / pw, (ch - pad) / ph);
6008
+ const pad3 = 20;
6009
+ const raw = Math.min(1, (cw - pad3) / pw, (ch - pad3) / ph);
5966
6010
  const s = Number.isFinite(raw) ? Math.max(0.2, raw) : 1;
5967
6011
  setFit({ s, w: pw * s, h: ph * s });
5968
6012
  };
@@ -6832,6 +6876,43 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6832
6876
  }
6833
6877
  }
6834
6878
  };
6879
+ const insertBlockFromLibrary = (contentType) => {
6880
+ if (!rows.length) return;
6881
+ if (selContentMeta?.inner) {
6882
+ const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
6883
+ dropContent(rowId, cellIdx, {
6884
+ kind: "new",
6885
+ contentType,
6886
+ insertAt: null,
6887
+ nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
6888
+ });
6889
+ return;
6890
+ }
6891
+ if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
6892
+ const r = rows.find((x) => x.id === selContentMeta.rowId);
6893
+ if (r && selContentMeta.cellIdx < r.cells.length) {
6894
+ dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
6895
+ kind: "new",
6896
+ contentType,
6897
+ insertAt: null
6898
+ });
6899
+ return;
6900
+ }
6901
+ }
6902
+ if (selectedRowId) {
6903
+ const targetRow = rows.find((r) => r.id === selectedRowId);
6904
+ if (targetRow) {
6905
+ dropContent(targetRow.id, 0, {
6906
+ kind: "new",
6907
+ contentType,
6908
+ insertAt: null
6909
+ });
6910
+ return;
6911
+ }
6912
+ }
6913
+ const main = rows[0];
6914
+ dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
6915
+ };
6835
6916
  const deleteContent = (rowId, cellIdx, ci, inner = null) => {
6836
6917
  if (!inner) {
6837
6918
  mutate((prev) => prev.map((r) => {
@@ -7428,6 +7509,8 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7428
7509
  if (data.layoutPresetKey) {
7429
7510
  const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
7430
7511
  if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
7512
+ } else if (data.contentType && typeof data.contentType === "string") {
7513
+ insertBlockFromLibrary(data.contentType);
7431
7514
  }
7432
7515
  } catch {
7433
7516
  }
@@ -7468,6 +7551,21 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7468
7551
  }
7469
7552
  return base;
7470
7553
  })(),
7554
+ onDragOver: (e) => {
7555
+ e.preventDefault();
7556
+ e.stopPropagation();
7557
+ },
7558
+ onDrop: (e) => {
7559
+ e.preventDefault();
7560
+ e.stopPropagation();
7561
+ try {
7562
+ const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
7563
+ if (data.contentType && typeof data.contentType === "string") {
7564
+ insertBlockFromLibrary(data.contentType);
7565
+ }
7566
+ } catch {
7567
+ }
7568
+ },
7471
7569
  children: [
7472
7570
  rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { id: eid("canvas-empty"), style: { padding: "56px 20px", textAlign: "center", color: C.muted, border: `2px dashed ${C.border}`, borderRadius: 7 }, children: [
7473
7571
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 30, marginBottom: 10 }, children: "\u2709" }),
@@ -7679,43 +7777,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7679
7777
  onDragStart: (e) => {
7680
7778
  e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
7681
7779
  },
7682
- onClick: () => {
7683
- if (!rows.length) return;
7684
- if (selContentMeta?.inner) {
7685
- const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
7686
- dropContent(rowId, cellIdx, {
7687
- kind: "new",
7688
- contentType: bt.type,
7689
- insertAt: null,
7690
- nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
7691
- });
7692
- return;
7693
- }
7694
- if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
7695
- const r = rows.find((x) => x.id === selContentMeta.rowId);
7696
- if (r && selContentMeta.cellIdx < r.cells.length) {
7697
- dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
7698
- kind: "new",
7699
- contentType: bt.type,
7700
- insertAt: null
7701
- });
7702
- return;
7703
- }
7704
- }
7705
- if (selectedRowId) {
7706
- const targetRow = rows.find((r) => r.id === selectedRowId);
7707
- if (targetRow) {
7708
- dropContent(targetRow.id, 0, {
7709
- kind: "new",
7710
- contentType: bt.type,
7711
- insertAt: null
7712
- });
7713
- return;
7714
- }
7715
- }
7716
- const main = rows[0];
7717
- dropContent(main.id, 0, { kind: "new", contentType: bt.type, insertAt: null });
7718
- },
7780
+ onClick: () => insertBlockFromLibrary(bt.type),
7719
7781
  title: selContentMeta?.inner ? "Click to add inside the selected nested column" : selContentMeta?.rowId ? "Click to add in the selected column" : selectedRowId ? "Click to add to column 1 of the selected row" : "Click to add to the first row, first column",
7720
7782
  style: {
7721
7783
  display: "flex",
@@ -8086,12 +8148,80 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
8086
8148
  );
8087
8149
  }
8088
8150
  );
8151
+
8152
+ // src/lib/htmlToEmailDesign.ts
8153
+ var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
8154
+ function extractHtmlForDesign(html) {
8155
+ const t = String(html ?? "").trim();
8156
+ if (!t) return "";
8157
+ const head = t.slice(0, 800).toLowerCase();
8158
+ if (!head.includes("<html") && !head.includes("<!doctype")) {
8159
+ return normalizeRichHtmlForStorage(t);
8160
+ }
8161
+ try {
8162
+ const doc = new DOMParser().parseFromString(t, "text/html");
8163
+ const body = doc.body;
8164
+ if (!body) return normalizeRichHtmlForStorage(t);
8165
+ return normalizeRichHtmlForStorage(body.innerHTML);
8166
+ } catch {
8167
+ return normalizeRichHtmlForStorage(t);
8168
+ }
8169
+ }
8170
+ function htmlToEmailDesignTemplate(html) {
8171
+ const inner = extractHtmlForDesign(html);
8172
+ if (!inner) return null;
8173
+ const doc = {
8174
+ type: "email_document",
8175
+ settings: {
8176
+ width: 600,
8177
+ backgroundColor: "#f1f5f9",
8178
+ contentBackgroundColor: "#ffffff",
8179
+ fontFamily: "Arial, Helvetica, sans-serif",
8180
+ lineHeightBase: 1.6,
8181
+ color: "#111827",
8182
+ responsive: true,
8183
+ rtl: false
8184
+ },
8185
+ rows: [
8186
+ {
8187
+ id: "row_html_import",
8188
+ type: "row",
8189
+ layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
8190
+ styles: {
8191
+ backgroundColor: "#ffffff",
8192
+ backgroundRepeat: "no-repeat",
8193
+ backgroundSize: "cover",
8194
+ padding: pad(24),
8195
+ textAlign: "left"
8196
+ },
8197
+ columns: [
8198
+ {
8199
+ id: "col_html_import",
8200
+ layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
8201
+ styles: { padding: pad(0), backgroundColor: "" },
8202
+ blocks: [
8203
+ {
8204
+ id: "block_html_import",
8205
+ type: "html",
8206
+ content: { html: inner },
8207
+ styles: {}
8208
+ }
8209
+ ]
8210
+ }
8211
+ ]
8212
+ }
8213
+ ]
8214
+ };
8215
+ return doc;
8216
+ }
8089
8217
  // Annotate the CommonJS export names for ESM import in node:
8090
8218
  0 && (module.exports = {
8091
8219
  EmailPreviewModal,
8092
8220
  ReactEmailEditor,
8093
8221
  base64ToUtf8,
8094
8222
  emailPreviewDevices,
8223
+ extractHtmlForDesign,
8224
+ htmlToEmailDesignTemplate,
8095
8225
  jsonToHtml,
8096
8226
  utf8ToBase64
8097
8227
  });