react-email-studio 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -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 DROP_HERE_HEIGHT_PX = 45;
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 : DROP_HERE_HEIGHT_PX,
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,44 @@ function Cell({
2816
2834
  onDragLeave: handleDragLeave,
2817
2835
  onDrop: handleDrop,
2818
2836
  style: {
2819
- flex: 1,
2820
- minHeight: preview ? void 0 : 44,
2837
+ display: "flex",
2838
+ flexDirection: "column",
2839
+ minHeight: preview ? void 0 : cellEmpty ? DROP_HERE_HEIGHT_PX : 44,
2821
2840
  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",
2841
+ ...preview ? {} : insertAt !== null && !cellEmpty ? { border: `1px dotted ${C.accent}`, background: `${C.accent}0a` } : {},
2842
+ background: insertAt !== null && !cellEmpty ? `${C.accent}0a` : "transparent",
2824
2843
  transition: "border-color .15s,background .15s",
2825
2844
  padding: preview ? 0 : 0,
2826
2845
  position: "relative"
2827
2846
  },
2828
2847
  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" }),
2848
+ cellEmpty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2849
+ "div",
2850
+ {
2851
+ style: {
2852
+ height: DROP_HERE_HEIGHT_PX,
2853
+ minHeight: DROP_HERE_HEIGHT_PX,
2854
+ maxHeight: DROP_HERE_HEIGHT_PX,
2855
+ display: "flex",
2856
+ alignItems: "center",
2857
+ justifyContent: "center",
2858
+ margin: 0,
2859
+ borderRadius: 6,
2860
+ boxSizing: "border-box",
2861
+ textAlign: "center",
2862
+ userSelect: "none",
2863
+ transition: "color .15s, border-color .15s, background .15s, box-shadow .15s",
2864
+ border: insertAt !== null ? `2px dashed ${C.accent}` : `2px dashed ${C.border}`,
2865
+ background: insertAt !== null ? `${C.accent}14` : `${C.canvas}`,
2866
+ boxShadow: insertAt !== null ? `inset 0 0 0 1px ${C.accent}22` : `inset 0 0 0 1px ${C.border}99`,
2867
+ color: insertAt !== null ? C.accent2 : C.muted,
2868
+ fontSize: 10,
2869
+ fontWeight: insertAt !== null ? 700 : 500,
2870
+ letterSpacing: "0.02em"
2871
+ },
2872
+ children: insertAt !== null ? "Release to drop" : "Drop here"
2873
+ }
2874
+ ),
2830
2875
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { idx: 0 }),
2831
2876
  blocks.map((cb, ci) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2832
2877
  BlockItem,
@@ -2877,7 +2922,6 @@ function LayoutRow({
2877
2922
  transition: "outline .12s",
2878
2923
  padding: row.padding,
2879
2924
  position: "relative",
2880
- ...editChromeBorder(C, preview),
2881
2925
  ...backgroundLayerStyle({
2882
2926
  bgColor: row.bgColor,
2883
2927
  bgImage: row.bgImage,
@@ -2916,8 +2960,9 @@ function LayoutRow({
2916
2960
  id: editorId ? `${editorId}-col-${row.id}-${ci}` : void 0,
2917
2961
  style: {
2918
2962
  flex: row.ratios[ci] ?? 1,
2919
- minHeight: 50,
2920
- ...editChromeBorder(C, preview),
2963
+ minHeight: preview ? 50 : DROP_HERE_HEIGHT_PX,
2964
+ display: "flex",
2965
+ flexDirection: "column",
2921
2966
  ...backgroundLayerStyle(cS),
2922
2967
  padding: paddingCssLayout(cS.padding),
2923
2968
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -3091,13 +3136,13 @@ var FONTS = [
3091
3136
  "Trebuchet MS,sans-serif",
3092
3137
  "Impact,sans-serif"
3093
3138
  ];
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;
3139
+ function paddingToCss(pad3, fallback) {
3140
+ if (typeof pad3 === "number" && Number.isFinite(pad3)) return `${Math.max(0, pad3)}px`;
3141
+ if (pad3 && typeof pad3 === "object" && !Array.isArray(pad3)) {
3142
+ const t = typeof pad3.top === "number" && Number.isFinite(pad3.top) ? pad3.top : fallback;
3143
+ const r = typeof pad3.right === "number" && Number.isFinite(pad3.right) ? pad3.right : t;
3144
+ const b = typeof pad3.bottom === "number" && Number.isFinite(pad3.bottom) ? pad3.bottom : t;
3145
+ const l = typeof pad3.left === "number" && Number.isFinite(pad3.left) ? pad3.left : r;
3101
3146
  return `${t}px ${r}px ${b}px ${l}px`;
3102
3147
  }
3103
3148
  return `${fallback}px`;
@@ -4893,7 +4938,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4893
4938
  /* @__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
4939
  /* @__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
4940
  /* @__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 }),
4941
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4897
4942
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4898
4943
  ] });
4899
4944
  case "text":
@@ -4919,7 +4964,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4919
4964
  /* @__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
4965
  /* @__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
4966
  /* @__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 }),
4967
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4923
4968
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4924
4969
  ] });
4925
4970
  case "html":
@@ -4944,7 +4989,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4944
4989
  }
4945
4990
  ) }, block.id),
4946
4991
  /* @__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 }),
4992
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4948
4993
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4949
4994
  ] });
4950
4995
  case "image":
@@ -4967,7 +5012,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4967
5012
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
4968
5013
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
4969
5014
  /* @__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 }),
5015
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4971
5016
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4972
5017
  ] });
4973
5018
  case "button":
@@ -4987,7 +5032,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4987
5032
  Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
4988
5033
  Tog("fullWidth", "Full Width"),
4989
5034
  /* @__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 }),
5035
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4991
5036
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4992
5037
  ] });
4993
5038
  case "link":
@@ -5002,7 +5047,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5002
5047
  Sel("fontFamily", "Font", FONTS2),
5003
5048
  Tog("underline", "Underline"),
5004
5049
  /* @__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 }),
5050
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5006
5051
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5007
5052
  ] });
5008
5053
  case "divider":
@@ -5011,14 +5056,14 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5011
5056
  /* @__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
5057
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DividerStyleButtons, { value: p.style, onChange: (s) => set("style", s), C }),
5013
5058
  /* @__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 }),
5059
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5015
5060
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5016
5061
  ] });
5017
5062
  case "spacer":
5018
5063
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5019
5064
  /* @__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
5065
  /* @__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 }),
5066
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5022
5067
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5023
5068
  ] });
5024
5069
  case "social":
@@ -5067,7 +5112,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5067
5112
  Sel("shape", "Shape", ["circle", "square"]),
5068
5113
  /* @__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
5114
  /* @__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 }),
5115
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5071
5116
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5072
5117
  ] });
5073
5118
  case "video":
@@ -5127,7 +5172,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5127
5172
  ] }),
5128
5173
  /* @__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
5174
  /* @__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 }),
5175
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5131
5176
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5132
5177
  ] });
5133
5178
  case "menu":
@@ -5153,7 +5198,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5153
5198
  /* @__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
5199
  /* @__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
5200
  /* @__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 }),
5201
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5157
5202
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5158
5203
  ] });
5159
5204
  case "timer": {
@@ -5193,7 +5238,7 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5193
5238
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5194
5239
  Tog("showLabels", "Show Labels"),
5195
5240
  /* @__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 }),
5241
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5197
5242
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5198
5243
  ] });
5199
5244
  }
@@ -5290,8 +5335,8 @@ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
5290
5335
  Tog("striped", "Striped rows"),
5291
5336
  /* @__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
5337
  /* @__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 }),
5338
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5339
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Cell padding", value: p.cellPadding, onChange: (pad3) => set("cellPadding", pad3), C }),
5295
5340
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5296
5341
  ] });
5297
5342
  case "layout":
@@ -5684,7 +5729,7 @@ function LayoutRowEditor({
5684
5729
  }
5685
5730
  ) })
5686
5731
  ] }) : null,
5687
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad) => updCol(selCol, { padding: pad }), C }),
5732
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
5688
5733
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { label: "Column radius", value: (row.columnStyles || {})[selCol]?.borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
5689
5734
  ] })
5690
5735
  ] })
@@ -5961,8 +6006,8 @@ function MobilePhoneScaleSlot({ variantKey, children }) {
5961
6006
  const pw = phone.offsetWidth;
5962
6007
  const ph = phone.offsetHeight;
5963
6008
  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);
6009
+ const pad3 = 20;
6010
+ const raw = Math.min(1, (cw - pad3) / pw, (ch - pad3) / ph);
5966
6011
  const s = Number.isFinite(raw) ? Math.max(0.2, raw) : 1;
5967
6012
  setFit({ s, w: pw * s, h: ph * s });
5968
6013
  };
@@ -6832,6 +6877,43 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6832
6877
  }
6833
6878
  }
6834
6879
  };
6880
+ const insertBlockFromLibrary = (contentType) => {
6881
+ if (!rows.length) return;
6882
+ if (selContentMeta?.inner) {
6883
+ const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
6884
+ dropContent(rowId, cellIdx, {
6885
+ kind: "new",
6886
+ contentType,
6887
+ insertAt: null,
6888
+ nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
6889
+ });
6890
+ return;
6891
+ }
6892
+ if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
6893
+ const r = rows.find((x) => x.id === selContentMeta.rowId);
6894
+ if (r && selContentMeta.cellIdx < r.cells.length) {
6895
+ dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
6896
+ kind: "new",
6897
+ contentType,
6898
+ insertAt: null
6899
+ });
6900
+ return;
6901
+ }
6902
+ }
6903
+ if (selectedRowId) {
6904
+ const targetRow = rows.find((r) => r.id === selectedRowId);
6905
+ if (targetRow) {
6906
+ dropContent(targetRow.id, 0, {
6907
+ kind: "new",
6908
+ contentType,
6909
+ insertAt: null
6910
+ });
6911
+ return;
6912
+ }
6913
+ }
6914
+ const main = rows[0];
6915
+ dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
6916
+ };
6835
6917
  const deleteContent = (rowId, cellIdx, ci, inner = null) => {
6836
6918
  if (!inner) {
6837
6919
  mutate((prev) => prev.map((r) => {
@@ -7428,6 +7510,8 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7428
7510
  if (data.layoutPresetKey) {
7429
7511
  const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
7430
7512
  if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
7513
+ } else if (data.contentType && typeof data.contentType === "string") {
7514
+ insertBlockFromLibrary(data.contentType);
7431
7515
  }
7432
7516
  } catch {
7433
7517
  }
@@ -7468,6 +7552,21 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7468
7552
  }
7469
7553
  return base;
7470
7554
  })(),
7555
+ onDragOver: (e) => {
7556
+ e.preventDefault();
7557
+ e.stopPropagation();
7558
+ },
7559
+ onDrop: (e) => {
7560
+ e.preventDefault();
7561
+ e.stopPropagation();
7562
+ try {
7563
+ const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
7564
+ if (data.contentType && typeof data.contentType === "string") {
7565
+ insertBlockFromLibrary(data.contentType);
7566
+ }
7567
+ } catch {
7568
+ }
7569
+ },
7471
7570
  children: [
7472
7571
  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
7572
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 30, marginBottom: 10 }, children: "\u2709" }),
@@ -7679,43 +7778,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7679
7778
  onDragStart: (e) => {
7680
7779
  e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
7681
7780
  },
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
- },
7781
+ onClick: () => insertBlockFromLibrary(bt.type),
7719
7782
  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
7783
  style: {
7721
7784
  display: "flex",
@@ -8086,12 +8149,80 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
8086
8149
  );
8087
8150
  }
8088
8151
  );
8152
+
8153
+ // src/lib/htmlToEmailDesign.ts
8154
+ var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
8155
+ function extractHtmlForDesign(html) {
8156
+ const t = String(html ?? "").trim();
8157
+ if (!t) return "";
8158
+ const head = t.slice(0, 800).toLowerCase();
8159
+ if (!head.includes("<html") && !head.includes("<!doctype")) {
8160
+ return normalizeRichHtmlForStorage(t);
8161
+ }
8162
+ try {
8163
+ const doc = new DOMParser().parseFromString(t, "text/html");
8164
+ const body = doc.body;
8165
+ if (!body) return normalizeRichHtmlForStorage(t);
8166
+ return normalizeRichHtmlForStorage(body.innerHTML);
8167
+ } catch {
8168
+ return normalizeRichHtmlForStorage(t);
8169
+ }
8170
+ }
8171
+ function htmlToEmailDesignTemplate(html) {
8172
+ const inner = extractHtmlForDesign(html);
8173
+ if (!inner) return null;
8174
+ const doc = {
8175
+ type: "email_document",
8176
+ settings: {
8177
+ width: 600,
8178
+ backgroundColor: "#f1f5f9",
8179
+ contentBackgroundColor: "#ffffff",
8180
+ fontFamily: "Arial, Helvetica, sans-serif",
8181
+ lineHeightBase: 1.6,
8182
+ color: "#111827",
8183
+ responsive: true,
8184
+ rtl: false
8185
+ },
8186
+ rows: [
8187
+ {
8188
+ id: "row_html_import",
8189
+ type: "row",
8190
+ layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
8191
+ styles: {
8192
+ backgroundColor: "#ffffff",
8193
+ backgroundRepeat: "no-repeat",
8194
+ backgroundSize: "cover",
8195
+ padding: pad(24),
8196
+ textAlign: "left"
8197
+ },
8198
+ columns: [
8199
+ {
8200
+ id: "col_html_import",
8201
+ layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
8202
+ styles: { padding: pad(0), backgroundColor: "" },
8203
+ blocks: [
8204
+ {
8205
+ id: "block_html_import",
8206
+ type: "html",
8207
+ content: { html: inner },
8208
+ styles: {}
8209
+ }
8210
+ ]
8211
+ }
8212
+ ]
8213
+ }
8214
+ ]
8215
+ };
8216
+ return doc;
8217
+ }
8089
8218
  // Annotate the CommonJS export names for ESM import in node:
8090
8219
  0 && (module.exports = {
8091
8220
  EmailPreviewModal,
8092
8221
  ReactEmailEditor,
8093
8222
  base64ToUtf8,
8094
8223
  emailPreviewDevices,
8224
+ extractHtmlForDesign,
8225
+ htmlToEmailDesignTemplate,
8095
8226
  jsonToHtml,
8096
8227
  utf8ToBase64
8097
8228
  });