react-email-studio 3.1.1 → 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
  });
@@ -152,7 +154,6 @@ var I18N = {
152
154
  delete: "Delete",
153
155
  moveUp: "Move Up",
154
156
  moveDown: "Move Down",
155
- mergeTags: "Merge Tags",
156
157
  autoSaved: "Auto-saved",
157
158
  search: "Search blocks\u2026",
158
159
  zoomIn: "Zoom In",
@@ -169,7 +170,7 @@ var I18N = {
169
170
  blockPaletteGroupActions: "Buttons & links",
170
171
  blockPaletteGroupWidgets: "Widgets",
171
172
  closePanel: "Close",
172
- 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.",
173
174
  emailContentSettings: "Email content",
174
175
  loadingDesign: "Loading design\u2026"
175
176
  },
@@ -211,7 +212,6 @@ var I18N = {
211
212
  delete: "Supprimer",
212
213
  moveUp: "Monter",
213
214
  moveDown: "Descendre",
214
- mergeTags: "Balises fusion",
215
215
  autoSaved: "Auto-sauvegard\xE9",
216
216
  search: "Rechercher\u2026",
217
217
  colorScheme: "Sch\xE9ma de couleurs",
@@ -271,7 +271,6 @@ var I18N = {
271
271
  delete: "L\xF6schen",
272
272
  moveUp: "Nach oben",
273
273
  moveDown: "Nach unten",
274
- mergeTags: "Merge-Tags",
275
274
  autoSaved: "Auto-gespeichert",
276
275
  search: "Bl\xF6cke suchen\u2026",
277
276
  colorScheme: "Farbschema",
@@ -329,7 +328,6 @@ var I18N = {
329
328
  delete: "Eliminar",
330
329
  moveUp: "Subir",
331
330
  moveDown: "Bajar",
332
- mergeTags: "Etiquetas de fusi\xF3n",
333
331
  autoSaved: "Auto-guardado",
334
332
  search: "Buscar bloques\u2026",
335
333
  zoomIn: "Zoom +",
@@ -1127,7 +1125,7 @@ function rowToHtml(row) {
1127
1125
  const r = row.ratios[i] ?? 1;
1128
1126
  const cs = row.columnStyles && row.columnStyles[i] ? row.columnStyles[i] : {};
1129
1127
  const colShell = emailSurfaceBgCss(cs);
1130
- 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:${(() => {
1131
1129
  const o = cs.padding;
1132
1130
  const t = numOr2(o.top, 0);
1133
1131
  const rgt = numOr2(o.right, t);
@@ -1143,7 +1141,7 @@ function rowToHtml(row) {
1143
1141
  const bl = numOr2(x.bl, tr2);
1144
1142
  return `border-radius:${tl}px ${tr2}px ${br}px ${bl}px;`;
1145
1143
  })() : typeof cs.borderRadius === "number" && Number.isFinite(cs.borderRadius) ? `border-radius:${cs.borderRadius}px;` : "";
1146
- 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>`;
1147
1145
  }).join("")}</div>`;
1148
1146
  const innerTrim = inner.replace(/>\s+</g, "><").trim();
1149
1147
  const body = innerTrim || "&nbsp;";
@@ -1363,8 +1361,8 @@ function layoutColumnBorderRadiusForDoc(r) {
1363
1361
  }
1364
1362
  return 0;
1365
1363
  }
1366
- function columnPaddingNonZero(pad) {
1367
- 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;
1368
1366
  }
1369
1367
  function columnRadiusNonZero(r) {
1370
1368
  if (typeof r === "number") return r !== 0;
@@ -1721,9 +1719,9 @@ function mapBlockToInternal(b, layoutDepth = 0) {
1721
1719
  if (asNum(s.fontWeight) != null) block.props.fontWeight = s.fontWeight;
1722
1720
  if (asNum(s.borderRadius) != null) block.props.borderRadius = s.borderRadius;
1723
1721
  if (s.padding && typeof s.padding === "object") {
1724
- const pad = s.padding;
1725
- block.props.paddingV = asNum(pad.top) ?? block.props.paddingV;
1726
- 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;
1727
1725
  }
1728
1726
  if (asStr(s.textAlign)) block.props.align = s.textAlign;
1729
1727
  break;
@@ -2238,10 +2236,15 @@ function useLiveCountdown(endDate) {
2238
2236
  const s = Math.floor(diff % 6e4 / 1e3);
2239
2237
  return [d, h, m, s];
2240
2238
  }
2241
- function editChromeBorder(C, preview) {
2239
+ function layoutSplitChromeBorder(_C, preview) {
2242
2240
  if (preview) return {};
2243
- return { border: `1px dotted ${C.border}`, boxSizing: "border-box" };
2241
+ return {
2242
+ border: "1px dotted rgba(148, 163, 184, 0.5)",
2243
+ boxSizing: "border-box"
2244
+ };
2244
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)";
2245
2248
  function blockLinkCaptureHandler(preview, e) {
2246
2249
  if (preview) return;
2247
2250
  const t = e.target;
@@ -2273,6 +2276,7 @@ function radiusCssLayout(v) {
2273
2276
  return "0px";
2274
2277
  }
2275
2278
  function ContentBlock({ block, selected, onClick, preview, C }) {
2279
+ const [hover, setHover] = (0, import_react.useState)(false);
2276
2280
  const { type, props: p } = block;
2277
2281
  const numOr2 = (v, fb) => typeof v === "number" && Number.isFinite(v) ? v : fb;
2278
2282
  const boxPx = (v) => {
@@ -2316,14 +2320,19 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
2316
2320
  onClick: preview ? void 0 : onClick || void 0,
2317
2321
  onClickCapture: (e) => blockLinkCaptureHandler(preview, e),
2318
2322
  onAuxClickCapture: (e) => blockLinkCaptureHandler(preview, e),
2323
+ onMouseEnter: preview ? void 0 : () => setHover(true),
2324
+ onMouseLeave: preview ? void 0 : () => setHover(false),
2319
2325
  style: {
2320
2326
  cursor: preview ? "default" : onClick ? "pointer" : "default",
2321
2327
  outline: selected && !preview ? `2px solid ${C.accent}` : "2px solid transparent",
2322
2328
  outlineOffset: 1,
2323
2329
  borderRadius: 3,
2324
- transition: "outline .12s",
2330
+ transition: "outline .12s, border-color .12s",
2325
2331
  position: "relative",
2326
- ...editChromeBorder(C, preview),
2332
+ ...preview ? {} : {
2333
+ border: hover ? CONTENT_BLOCK_HOVER_BORDER : "1px solid transparent",
2334
+ boxSizing: "border-box"
2335
+ },
2327
2336
  ...shellBg,
2328
2337
  padding: paddingCss(p.padding),
2329
2338
  margin: marginCss(p.margin)
@@ -2542,6 +2551,8 @@ function NestedRowBlock({
2542
2551
  rowId,
2543
2552
  cellIdx,
2544
2553
  parentBlockIdx,
2554
+ /** When this layout sits inside another layout column, meta uses `contentIdx` + `inner` like other nested blocks. */
2555
+ selectionInner,
2545
2556
  editorId,
2546
2557
  selectedKey,
2547
2558
  selContentMeta,
@@ -2553,7 +2564,7 @@ function NestedRowBlock({
2553
2564
  C
2554
2565
  }) {
2555
2566
  const p = block.props;
2556
- const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner && selectedKey === block.id;
2567
+ const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selectedKey === block.id && (selectionInner == null ? selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner : selContentMeta.contentIdx === parentBlockIdx && selContentMeta.inner && selContentMeta.inner.cellIdx === selectionInner.cellIdx && selContentMeta.inner.contentIdx === selectionInner.contentIdx);
2557
2568
  const rowStyle = {
2558
2569
  padding: p.padding ?? 8,
2559
2570
  position: "relative",
@@ -2561,7 +2572,7 @@ function NestedRowBlock({
2561
2572
  outline: rowSelected ? `2px solid ${C.accent}` : "2px solid transparent",
2562
2573
  outlineOffset: 1,
2563
2574
  transition: "outline .12s",
2564
- ...editChromeBorder(C, preview),
2575
+ ...layoutSplitChromeBorder(C, preview),
2565
2576
  ...backgroundLayerStyle({
2566
2577
  bgColor: p.bgColor,
2567
2578
  bgImage: p.bgImage,
@@ -2577,7 +2588,13 @@ function NestedRowBlock({
2577
2588
  style: rowStyle,
2578
2589
  onClick: preview ? void 0 : (e) => {
2579
2590
  e.stopPropagation();
2580
- onSelectContent(block.id, rowId, cellIdx, parentBlockIdx, null);
2591
+ onSelectContent(
2592
+ block.id,
2593
+ rowId,
2594
+ cellIdx,
2595
+ parentBlockIdx,
2596
+ selectionInner ?? null
2597
+ );
2581
2598
  },
2582
2599
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
2583
2600
  const cS = p.columnStyles && p.columnStyles[ici] || {};
@@ -2587,7 +2604,10 @@ function NestedRowBlock({
2587
2604
  style: {
2588
2605
  flex: p.ratios?.[ici] ?? 1,
2589
2606
  minWidth: 0,
2590
- ...editChromeBorder(C, preview),
2607
+ minHeight: preview ? void 0 : EMPTY_CELL_COLUMN_MIN_H,
2608
+ display: "flex",
2609
+ flexDirection: "column",
2610
+ ...layoutSplitChromeBorder(C, preview),
2591
2611
  ...backgroundLayerStyle(cS),
2592
2612
  padding: paddingCssLayout(cS.padding),
2593
2613
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -2643,7 +2663,13 @@ function BlockItem({
2643
2663
  if (!onSelectContent) return;
2644
2664
  e.stopPropagation();
2645
2665
  if (isSplitLayoutBlock(cb)) {
2646
- onSelectContent(cb.id, rowId, cellIdx, nest ? nest.parentBlockIdx : ci, null);
2666
+ onSelectContent(
2667
+ cb.id,
2668
+ rowId,
2669
+ cellIdx,
2670
+ nest ? nest.parentBlockIdx : ci,
2671
+ nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null
2672
+ );
2647
2673
  } else {
2648
2674
  onSelectContent(
2649
2675
  cb.id,
@@ -2714,6 +2740,7 @@ function BlockItem({
2714
2740
  rowId,
2715
2741
  cellIdx,
2716
2742
  parentBlockIdx: nest ? nest.parentBlockIdx : ci,
2743
+ selectionInner: nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null,
2717
2744
  editorId,
2718
2745
  selectedKey,
2719
2746
  selContentMeta,
@@ -2797,6 +2824,7 @@ function Cell({
2797
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 } }),
2798
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 } })
2799
2826
  ] }) : null;
2827
+ const cellEmpty = !preview && blocks.length === 0;
2800
2828
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2801
2829
  "div",
2802
2830
  {
@@ -2806,17 +2834,43 @@ function Cell({
2806
2834
  onDragLeave: handleDragLeave,
2807
2835
  onDrop: handleDrop,
2808
2836
  style: {
2809
- flex: 1,
2810
- 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,
2811
2841
  borderRadius: 4,
2812
- ...preview ? {} : insertAt !== null ? { border: `1px dotted ${C.accent}`, background: `${C.accent}0a` } : editChromeBorder(C, false),
2813
- 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",
2814
2844
  transition: "border-color .15s,background .15s",
2815
2845
  padding: preview ? 0 : 0,
2816
2846
  position: "relative"
2817
2847
  },
2818
2848
  children: [
2819
- !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
+ ),
2820
2874
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { idx: 0 }),
2821
2875
  blocks.map((cb, ci) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2822
2876
  BlockItem,
@@ -2867,7 +2921,6 @@ function LayoutRow({
2867
2921
  transition: "outline .12s",
2868
2922
  padding: row.padding,
2869
2923
  position: "relative",
2870
- ...editChromeBorder(C, preview),
2871
2924
  ...backgroundLayerStyle({
2872
2925
  bgColor: row.bgColor,
2873
2926
  bgImage: row.bgImage,
@@ -2906,8 +2959,9 @@ function LayoutRow({
2906
2959
  id: editorId ? `${editorId}-col-${row.id}-${ci}` : void 0,
2907
2960
  style: {
2908
2961
  flex: row.ratios[ci] ?? 1,
2909
- minHeight: 50,
2910
- ...editChromeBorder(C, preview),
2962
+ minHeight: preview ? 50 : EMPTY_CELL_COLUMN_MIN_H,
2963
+ display: "flex",
2964
+ flexDirection: "column",
2911
2965
  ...backgroundLayerStyle(cS),
2912
2966
  padding: paddingCssLayout(cS.padding),
2913
2967
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -3081,13 +3135,13 @@ var FONTS = [
3081
3135
  "Trebuchet MS,sans-serif",
3082
3136
  "Impact,sans-serif"
3083
3137
  ];
3084
- function paddingToCss(pad, fallback) {
3085
- if (typeof pad === "number" && Number.isFinite(pad)) return `${Math.max(0, pad)}px`;
3086
- if (pad && typeof pad === "object" && !Array.isArray(pad)) {
3087
- const t = typeof pad.top === "number" && Number.isFinite(pad.top) ? pad.top : fallback;
3088
- const r = typeof pad.right === "number" && Number.isFinite(pad.right) ? pad.right : t;
3089
- const b = typeof pad.bottom === "number" && Number.isFinite(pad.bottom) ? pad.bottom : t;
3090
- 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;
3091
3145
  return `${t}px ${r}px ${b}px ${l}px`;
3092
3146
  }
3093
3147
  return `${fallback}px`;
@@ -3301,11 +3355,8 @@ function ToolbarTypographyControls({ editor, C }) {
3301
3355
  }
3302
3356
  function FormattingToolbar({
3303
3357
  editor,
3304
- C,
3305
- mergeTags,
3306
- onMergeTagInsert
3358
+ C
3307
3359
  }) {
3308
- const [mergeSel, setMergeSel] = (0, import_react2.useState)("");
3309
3360
  const [linkPanelOpen, setLinkPanelOpen] = (0, import_react2.useState)(false);
3310
3361
  const [linkUrlValue, setLinkUrlValue] = (0, import_react2.useState)("");
3311
3362
  const ibtn = (active) => toolbarIconBtn(C, active);
@@ -3625,8 +3676,6 @@ function TextRichEditor({
3625
3676
  value,
3626
3677
  onChange,
3627
3678
  typography,
3628
- mergeTags,
3629
- onMergeTagInsert,
3630
3679
  placeholder = "Write your message\u2026",
3631
3680
  headerTitle = "Rich editor",
3632
3681
  C
@@ -3770,15 +3819,7 @@ function TextRichEditor({
3770
3819
  ]
3771
3820
  }
3772
3821
  ),
3773
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3774
- FormattingToolbar,
3775
- {
3776
- editor,
3777
- C,
3778
- mergeTags,
3779
- onMergeTagInsert
3780
- }
3781
- ),
3822
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
3782
3823
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
3783
3824
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3784
3825
  "div",
@@ -3829,15 +3870,7 @@ function TextRichEditor({
3829
3870
  background: C.inputBg
3830
3871
  },
3831
3872
  children: [
3832
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3833
- FormattingToolbar,
3834
- {
3835
- editor,
3836
- C,
3837
- mergeTags,
3838
- onMergeTagInsert
3839
- }
3840
- ),
3873
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
3841
3874
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
3842
3875
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3843
3876
  "div",
@@ -4837,7 +4870,7 @@ function BlockSurfaceBgInspector({
4837
4870
  ] }) : null
4838
4871
  ] });
4839
4872
  }
4840
- function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }) {
4873
+ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4841
4874
  const { IS, CI } = useIS(C);
4842
4875
  const imageFileRef = (0, import_react4.useRef)(null);
4843
4876
  const p = block.props;
@@ -4856,7 +4889,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4856
4889
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: !!p[k], onChange: (e) => set(k, e.target.checked), style: { width: 15, height: 15, accentColor: C.accent } }),
4857
4890
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: p[k] ? "On" : "Off" })
4858
4891
  ] }) });
4859
- const TagPicker = (_field) => null;
4860
4892
  const FONTS2 = ["Georgia,serif", "Arial,sans-serif", "Verdana,sans-serif", "'Courier New',monospace", "Trebuchet MS,sans-serif", "Impact,sans-serif"];
4861
4893
  const ImgUpload = (key) => {
4862
4894
  if (!onUpload) return null;
@@ -4905,12 +4937,12 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4905
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 }),
4906
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) }) }),
4907
4939
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4908
- /* @__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 }),
4909
4941
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4910
4942
  ] });
4911
4943
  case "text":
4912
4944
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4913
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content (HTML)", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4945
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4914
4946
  "textarea",
4915
4947
  {
4916
4948
  style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
@@ -4921,24 +4953,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4921
4953
  },
4922
4954
  block.id
4923
4955
  ) }),
4924
- mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4925
- "select",
4926
- {
4927
- style: IS,
4928
- defaultValue: "",
4929
- onChange: (e) => {
4930
- const v = e.target.value;
4931
- if (v) {
4932
- set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
4933
- e.target.value = "";
4934
- }
4935
- },
4936
- children: [
4937
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "Choose\u2026" }),
4938
- mergeTags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: t.value, children: t.name }, t.name))
4939
- ]
4940
- }
4941
- ) }) : null,
4942
4956
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
4943
4957
  Col("color", "Color"),
4944
4958
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
@@ -4949,7 +4963,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4949
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) }) }),
4950
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 }),
4951
4965
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4952
- /* @__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 }),
4953
4967
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4954
4968
  ] });
4955
4969
  case "html":
@@ -4968,15 +4982,13 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4968
4982
  letterSpacing: p.letterSpacing,
4969
4983
  padding: p.padding
4970
4984
  },
4971
- mergeTags,
4972
- onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
4973
4985
  placeholder: "Write your rich content\u2026",
4974
4986
  headerTitle: "Rich editor",
4975
4987
  C
4976
4988
  }
4977
4989
  ) }, block.id),
4978
4990
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4979
- /* @__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 }),
4980
4992
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4981
4993
  ] });
4982
4994
  case "image":
@@ -4999,7 +5011,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4999
5011
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5000
5012
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
5001
5013
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5002
- /* @__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 }),
5003
5015
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5004
5016
  ] });
5005
5017
  case "button":
@@ -5019,7 +5031,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5019
5031
  Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
5020
5032
  Tog("fullWidth", "Full Width"),
5021
5033
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "backdrop", p, set, C, onUpload, syncKey: block.id }),
5022
- /* @__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 }),
5023
5035
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5024
5036
  ] });
5025
5037
  case "link":
@@ -5034,7 +5046,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5034
5046
  Sel("fontFamily", "Font", FONTS2),
5035
5047
  Tog("underline", "Underline"),
5036
5048
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5037
- /* @__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 }),
5038
5050
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5039
5051
  ] });
5040
5052
  case "divider":
@@ -5043,14 +5055,14 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5043
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 }),
5044
5056
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DividerStyleButtons, { value: p.style, onChange: (s) => set("style", s), C }),
5045
5057
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5046
- /* @__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 }),
5047
5059
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5048
5060
  ] });
5049
5061
  case "spacer":
5050
5062
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5051
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 }),
5052
5064
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5053
- /* @__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 }),
5054
5066
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5055
5067
  ] });
5056
5068
  case "social":
@@ -5099,7 +5111,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5099
5111
  Sel("shape", "Shape", ["circle", "square"]),
5100
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 }),
5101
5113
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5102
- /* @__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 }),
5103
5115
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5104
5116
  ] });
5105
5117
  case "video":
@@ -5159,7 +5171,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5159
5171
  ] }),
5160
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 }),
5161
5173
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5162
- /* @__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 }),
5163
5175
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5164
5176
  ] });
5165
5177
  case "menu":
@@ -5185,7 +5197,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5185
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 }),
5186
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 }),
5187
5199
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5188
- /* @__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 }),
5189
5201
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5190
5202
  ] });
5191
5203
  case "timer": {
@@ -5225,7 +5237,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5225
5237
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5226
5238
  Tog("showLabels", "Show Labels"),
5227
5239
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "backdrop", p, set, C, onUpload, syncKey: block.id }),
5228
- /* @__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 }),
5229
5241
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5230
5242
  ] });
5231
5243
  }
@@ -5322,8 +5334,8 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5322
5334
  Tog("striped", "Striped rows"),
5323
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 }),
5324
5336
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5325
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5326
- /* @__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 }),
5327
5339
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5328
5340
  ] });
5329
5341
  case "layout":
@@ -5376,6 +5388,19 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5376
5388
  return null;
5377
5389
  }
5378
5390
  }
5391
+ function inferLayoutRowBgStep(r) {
5392
+ if (r.bgGradient) return "gradient";
5393
+ if (String(r.bgImage || "").trim()) return "image";
5394
+ if (String(r.bgColor || "").trim()) return "solid";
5395
+ return null;
5396
+ }
5397
+ function inferColumnBgStep(columnStyles, selCol) {
5398
+ const cs = (columnStyles || {})[selCol] || {};
5399
+ if (cs.bgGradient) return "gradient";
5400
+ if (String(cs.bgImage || "").trim()) return "image";
5401
+ if (String(cs.bgColor || "").trim()) return "solid";
5402
+ return null;
5403
+ }
5379
5404
  function LayoutRowEditor({
5380
5405
  row,
5381
5406
  onChange,
@@ -5387,49 +5412,37 @@ function LayoutRowEditor({
5387
5412
  customizationHint
5388
5413
  }) {
5389
5414
  const { IS, CI } = useIS(C);
5390
- const [selCol, setSelCol] = (0, import_react4.useState)(initialCol);
5391
- const [rowBgUiStep, setRowBgUiStep] = (0, import_react4.useState)(null);
5392
- const [colBgUiStep, setColBgUiStep] = (0, import_react4.useState)(null);
5415
+ const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
5416
+ const ratioList = row.ratios?.length ? row.ratios : [1];
5417
+ const [selCol, setSelCol] = (0, import_react4.useState)(0);
5418
+ const [rowBgPickerMode, setRowBgPickerMode] = (0, import_react4.useState)(null);
5419
+ const [colBgPickerMode, setColBgPickerMode] = (0, import_react4.useState)(null);
5420
+ const inferredRowBg = (0, import_react4.useMemo)(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
5421
+ const inferredColBg = (0, import_react4.useMemo)(
5422
+ () => inferColumnBgStep(row.columnStyles, selCol),
5423
+ [row.columnStyles, selCol]
5424
+ );
5425
+ const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
5426
+ const colBgUiStep = inferredColBg ?? colBgPickerMode;
5393
5427
  (0, import_react4.useEffect)(() => {
5394
- if (initialCol !== null && initialCol !== void 0) {
5395
- setSelCol(initialCol);
5428
+ if (initialCol !== null && initialCol !== void 0 && initialCol >= 0) {
5429
+ setSelCol(Math.min(initialCol, colCount - 1));
5430
+ } else {
5431
+ setSelCol((c) => Math.min(Math.max(0, c), colCount - 1));
5396
5432
  }
5397
- }, [initialCol]);
5433
+ }, [initialCol, row.id, colCount]);
5398
5434
  (0, import_react4.useEffect)(() => {
5399
- if (row.bgGradient) {
5400
- setRowBgUiStep("gradient");
5401
- return;
5402
- }
5403
- if (String(row.bgImage || "").trim()) {
5404
- setRowBgUiStep("image");
5405
- return;
5406
- }
5407
- if (String(row.bgColor || "").trim()) {
5408
- setRowBgUiStep("solid");
5409
- return;
5410
- }
5411
- setRowBgUiStep(null);
5435
+ setRowBgPickerMode(null);
5412
5436
  }, [row.id]);
5413
5437
  (0, import_react4.useEffect)(() => {
5414
- if (selCol === null) {
5415
- setColBgUiStep(null);
5416
- return;
5417
- }
5418
- const cs = (row.columnStyles || {})[selCol] || {};
5419
- if (cs.bgGradient) {
5420
- setColBgUiStep("gradient");
5421
- return;
5422
- }
5423
- if (String(cs.bgImage || "").trim()) {
5424
- setColBgUiStep("image");
5425
- return;
5426
- }
5427
- if (String(cs.bgColor || "").trim()) {
5428
- setColBgUiStep("solid");
5429
- return;
5430
- }
5431
- setColBgUiStep(null);
5438
+ setColBgPickerMode(null);
5432
5439
  }, [selCol, row.id]);
5440
+ (0, import_react4.useEffect)(() => {
5441
+ if (inferredRowBg) setRowBgPickerMode(null);
5442
+ }, [inferredRowBg]);
5443
+ (0, import_react4.useEffect)(() => {
5444
+ if (inferredColBg) setColBgPickerMode(null);
5445
+ }, [inferredColBg]);
5433
5446
  const set = (k, v) => onChange({ ...row, [k]: v });
5434
5447
  const applyColumnCount = (n) => {
5435
5448
  const prevCells = [...row.cells || []];
@@ -5445,16 +5458,13 @@ function LayoutRowEditor({
5445
5458
  }
5446
5459
  onChange({ ...row, cols: n, preset: "custom", cells, ratios });
5447
5460
  };
5448
- const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
5449
- const ratioList = row.ratios?.length ? row.ratios : [1];
5450
5461
  const updCol = (i, upd) => {
5451
5462
  const styles = { ...row.columnStyles || {} };
5452
5463
  styles[i] = { ...styles[i] || {}, ...upd };
5453
5464
  set("columnStyles", styles);
5454
5465
  };
5455
5466
  const setColBgMode = (mode) => {
5456
- if (selCol === null) return;
5457
- setColBgUiStep(mode);
5467
+ setColBgPickerMode(mode);
5458
5468
  const cs = (row.columnStyles || {})[selCol] || {};
5459
5469
  if (mode === "solid") {
5460
5470
  updCol(selCol, { bgGradient: null, bgImage: "" });
@@ -5472,7 +5482,7 @@ function LayoutRowEditor({
5472
5482
  const total = ratioList.reduce((a, b) => a + b, 0) || 1;
5473
5483
  const POS_OPTS = ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"];
5474
5484
  const setRowBgMode = (mode) => {
5475
- setRowBgUiStep(mode);
5485
+ setRowBgPickerMode(mode);
5476
5486
  if (mode === "solid") {
5477
5487
  set("bgGradient", null);
5478
5488
  set("bgImage", "");
@@ -5630,7 +5640,7 @@ function LayoutRowEditor({
5630
5640
  },
5631
5641
  i
5632
5642
  )) }),
5633
- selCol !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
5643
+ colCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
5634
5644
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BgModeButtons, { value: colBgUiStep, onChange: setColBgMode, C }),
5635
5645
  colBgUiStep === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11, color: C.muted, marginBottom: 10, lineHeight: 1.45 }, children: "Choose Color, Gradient, or Image \u2014 then edit column background details." }) : null,
5636
5646
  colBgUiStep === "solid" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: `col ${selCol + 1} bg`, C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
@@ -5718,7 +5728,7 @@ function LayoutRowEditor({
5718
5728
  }
5719
5729
  ) })
5720
5730
  ] }) : null,
5721
- /* @__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 }),
5722
5732
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BorderRadiusEditor, { label: "Column radius", value: (row.columnStyles || {})[selCol]?.borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
5723
5733
  ] })
5724
5734
  ] })
@@ -5995,8 +6005,8 @@ function MobilePhoneScaleSlot({ variantKey, children }) {
5995
6005
  const pw = phone.offsetWidth;
5996
6006
  const ph = phone.offsetHeight;
5997
6007
  if (cw < 12 || ch < 12 || pw < 12 || ph < 12) return;
5998
- const pad = 20;
5999
- 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);
6000
6010
  const s = Number.isFinite(raw) ? Math.max(0.2, raw) : 1;
6001
6011
  setFit({ s, w: pw * s, h: ph * s });
6002
6012
  };
@@ -6521,7 +6531,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6521
6531
  const [locale, setLocale] = (0, import_react8.useState)(
6522
6532
  typeof opt.locale === "string" ? opt.locale : "en"
6523
6533
  );
6524
- const [mergeTags, setMergeTagsState] = (0, import_react8.useState)(() => opt.mergeTags ?? []);
6525
6534
  const [zoom, setZoom] = (0, import_react8.useState)(100);
6526
6535
  const [rightRailWidth, setRightRailWidth] = (0, import_react8.useState)(RIGHT_RAIL_DEFAULT_W);
6527
6536
  const [ctxMenu, setCtxMenu] = (0, import_react8.useState)(null);
@@ -6867,6 +6876,43 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6867
6876
  }
6868
6877
  }
6869
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
+ };
6870
6916
  const deleteContent = (rowId, cellIdx, ci, inner = null) => {
6871
6917
  if (!inner) {
6872
6918
  mutate((prev) => prev.map((r) => {
@@ -6916,7 +6962,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6916
6962
  }));
6917
6963
  return;
6918
6964
  }
6919
- const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: inner.parentBlockIdx, innerCellIdx: inner.cellIdx };
6965
+ const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: ci, innerCellIdx: inner.cellIdx };
6920
6966
  mutate((prev) => updateColumnAt(prev, loc, (c) => c.map((b, j) => j === inner.contentIdx ? upd : b)));
6921
6967
  };
6922
6968
  const selectContent = (contentId, rowId, cellIdx, parentBlockIdx, inner = null) => {
@@ -7463,6 +7509,8 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7463
7509
  if (data.layoutPresetKey) {
7464
7510
  const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
7465
7511
  if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
7512
+ } else if (data.contentType && typeof data.contentType === "string") {
7513
+ insertBlockFromLibrary(data.contentType);
7466
7514
  }
7467
7515
  } catch {
7468
7516
  }
@@ -7503,6 +7551,21 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7503
7551
  }
7504
7552
  return base;
7505
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
+ },
7506
7569
  children: [
7507
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: [
7508
7571
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 30, marginBottom: 10 }, children: "\u2709" }),
@@ -7647,7 +7710,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7647
7710
  setSelMeta(null);
7648
7711
  setSelectedRowId(null);
7649
7712
  },
7650
- mergeTags,
7651
7713
  onUpload,
7652
7714
  C
7653
7715
  }
@@ -7715,12 +7777,8 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7715
7777
  onDragStart: (e) => {
7716
7778
  e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
7717
7779
  },
7718
- onClick: () => {
7719
- const targetRow = selectedRowId ? rows.find((r) => r.id === selectedRowId) : rows[rows.length - 1];
7720
- if (!targetRow) return;
7721
- dropContent(targetRow.id, 0, { kind: "new", contentType: bt.type, insertAt: null });
7722
- },
7723
- title: `Drag into a cell or click to add to ${selectedRowId ? "selected row" : "last row"}`,
7780
+ onClick: () => insertBlockFromLibrary(bt.type),
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",
7724
7782
  style: {
7725
7783
  display: "flex",
7726
7784
  flexDirection: "row",
@@ -8090,12 +8148,80 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
8090
8148
  );
8091
8149
  }
8092
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
+ }
8093
8217
  // Annotate the CommonJS export names for ESM import in node:
8094
8218
  0 && (module.exports = {
8095
8219
  EmailPreviewModal,
8096
8220
  ReactEmailEditor,
8097
8221
  base64ToUtf8,
8098
8222
  emailPreviewDevices,
8223
+ extractHtmlForDesign,
8224
+ htmlToEmailDesignTemplate,
8099
8225
  jsonToHtml,
8100
8226
  utf8ToBase64
8101
8227
  });