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.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  useRef as useRef6,
5
5
  useEffect as useEffect6,
6
6
  useCallback as useCallback2,
7
- useMemo as useMemo2,
7
+ useMemo as useMemo3,
8
8
  forwardRef,
9
9
  useImperativeHandle,
10
10
  Fragment as Fragment6
@@ -120,7 +120,6 @@ var I18N = {
120
120
  delete: "Delete",
121
121
  moveUp: "Move Up",
122
122
  moveDown: "Move Down",
123
- mergeTags: "Merge Tags",
124
123
  autoSaved: "Auto-saved",
125
124
  search: "Search blocks\u2026",
126
125
  zoomIn: "Zoom In",
@@ -137,7 +136,7 @@ var I18N = {
137
136
  blockPaletteGroupActions: "Buttons & links",
138
137
  blockPaletteGroupWidgets: "Widgets",
139
138
  closePanel: "Close",
140
- canvasEmptyHint: "Drag blocks from the library into the column, or add a Layout block for multi-column sections.",
139
+ canvasEmptyHint: "Drag row layouts or blocks from the library. Drop blocks into a column or onto the mail preview area.",
141
140
  emailContentSettings: "Email content",
142
141
  loadingDesign: "Loading design\u2026"
143
142
  },
@@ -179,7 +178,6 @@ var I18N = {
179
178
  delete: "Supprimer",
180
179
  moveUp: "Monter",
181
180
  moveDown: "Descendre",
182
- mergeTags: "Balises fusion",
183
181
  autoSaved: "Auto-sauvegard\xE9",
184
182
  search: "Rechercher\u2026",
185
183
  colorScheme: "Sch\xE9ma de couleurs",
@@ -239,7 +237,6 @@ var I18N = {
239
237
  delete: "L\xF6schen",
240
238
  moveUp: "Nach oben",
241
239
  moveDown: "Nach unten",
242
- mergeTags: "Merge-Tags",
243
240
  autoSaved: "Auto-gespeichert",
244
241
  search: "Bl\xF6cke suchen\u2026",
245
242
  colorScheme: "Farbschema",
@@ -297,7 +294,6 @@ var I18N = {
297
294
  delete: "Eliminar",
298
295
  moveUp: "Subir",
299
296
  moveDown: "Bajar",
300
- mergeTags: "Etiquetas de fusi\xF3n",
301
297
  autoSaved: "Auto-guardado",
302
298
  search: "Buscar bloques\u2026",
303
299
  zoomIn: "Zoom +",
@@ -1095,7 +1091,7 @@ function rowToHtml(row) {
1095
1091
  const r = row.ratios[i] ?? 1;
1096
1092
  const cs = row.columnStyles && row.columnStyles[i] ? row.columnStyles[i] : {};
1097
1093
  const colShell = emailSurfaceBgCss(cs);
1098
- const pad = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
1094
+ const pad3 = cs.padding && typeof cs.padding === "object" && !Array.isArray(cs.padding) ? `padding:${(() => {
1099
1095
  const o = cs.padding;
1100
1096
  const t = numOr2(o.top, 0);
1101
1097
  const rgt = numOr2(o.right, t);
@@ -1111,7 +1107,7 @@ function rowToHtml(row) {
1111
1107
  const bl = numOr2(x.bl, tr2);
1112
1108
  return `border-radius:${tl}px ${tr2}px ${br}px ${bl}px;`;
1113
1109
  })() : typeof cs.borderRadius === "number" && Number.isFinite(cs.borderRadius) ? `border-radius:${cs.borderRadius}px;` : "";
1114
- return `<div style="flex:${r} 1 0;min-width:0;box-sizing:border-box;${pad}${rad}${colShell}">${cell.map(blockToHtml).join("")}</div>`;
1110
+ return `<div style="flex:${r} 1 0;min-width:0;box-sizing:border-box;${pad3}${rad}${colShell}">${cell.map(blockToHtml).join("")}</div>`;
1115
1111
  }).join("")}</div>`;
1116
1112
  const innerTrim = inner.replace(/>\s+</g, "><").trim();
1117
1113
  const body = innerTrim || "&nbsp;";
@@ -1331,8 +1327,8 @@ function layoutColumnBorderRadiusForDoc(r) {
1331
1327
  }
1332
1328
  return 0;
1333
1329
  }
1334
- function columnPaddingNonZero(pad) {
1335
- return pad.top !== 0 || pad.right !== 0 || pad.bottom !== 0 || pad.left !== 0;
1330
+ function columnPaddingNonZero(pad3) {
1331
+ return pad3.top !== 0 || pad3.right !== 0 || pad3.bottom !== 0 || pad3.left !== 0;
1336
1332
  }
1337
1333
  function columnRadiusNonZero(r) {
1338
1334
  if (typeof r === "number") return r !== 0;
@@ -1689,9 +1685,9 @@ function mapBlockToInternal(b, layoutDepth = 0) {
1689
1685
  if (asNum(s.fontWeight) != null) block.props.fontWeight = s.fontWeight;
1690
1686
  if (asNum(s.borderRadius) != null) block.props.borderRadius = s.borderRadius;
1691
1687
  if (s.padding && typeof s.padding === "object") {
1692
- const pad = s.padding;
1693
- block.props.paddingV = asNum(pad.top) ?? block.props.paddingV;
1694
- block.props.paddingH = asNum(pad.right) ?? block.props.paddingH;
1688
+ const pad3 = s.padding;
1689
+ block.props.paddingV = asNum(pad3.top) ?? block.props.paddingV;
1690
+ block.props.paddingH = asNum(pad3.right) ?? block.props.paddingH;
1695
1691
  }
1696
1692
  if (asStr(s.textAlign)) block.props.align = s.textAlign;
1697
1693
  break;
@@ -2206,10 +2202,15 @@ function useLiveCountdown(endDate) {
2206
2202
  const s = Math.floor(diff % 6e4 / 1e3);
2207
2203
  return [d, h, m, s];
2208
2204
  }
2209
- function editChromeBorder(C, preview) {
2205
+ function layoutSplitChromeBorder(_C, preview) {
2210
2206
  if (preview) return {};
2211
- return { border: `1px dotted ${C.border}`, boxSizing: "border-box" };
2207
+ return {
2208
+ border: "1px dotted rgba(148, 163, 184, 0.5)",
2209
+ boxSizing: "border-box"
2210
+ };
2212
2211
  }
2212
+ var CONTENT_BLOCK_HOVER_BORDER = "1px dotted rgba(148, 163, 184, 0.5)";
2213
+ var EMPTY_CELL_COLUMN_MIN_H = "min(320px, 42vh)";
2213
2214
  function blockLinkCaptureHandler(preview, e) {
2214
2215
  if (preview) return;
2215
2216
  const t = e.target;
@@ -2241,6 +2242,7 @@ function radiusCssLayout(v) {
2241
2242
  return "0px";
2242
2243
  }
2243
2244
  function ContentBlock({ block, selected, onClick, preview, C }) {
2245
+ const [hover, setHover] = useState(false);
2244
2246
  const { type, props: p } = block;
2245
2247
  const numOr2 = (v, fb) => typeof v === "number" && Number.isFinite(v) ? v : fb;
2246
2248
  const boxPx = (v) => {
@@ -2284,14 +2286,19 @@ function ContentBlock({ block, selected, onClick, preview, C }) {
2284
2286
  onClick: preview ? void 0 : onClick || void 0,
2285
2287
  onClickCapture: (e) => blockLinkCaptureHandler(preview, e),
2286
2288
  onAuxClickCapture: (e) => blockLinkCaptureHandler(preview, e),
2289
+ onMouseEnter: preview ? void 0 : () => setHover(true),
2290
+ onMouseLeave: preview ? void 0 : () => setHover(false),
2287
2291
  style: {
2288
2292
  cursor: preview ? "default" : onClick ? "pointer" : "default",
2289
2293
  outline: selected && !preview ? `2px solid ${C.accent}` : "2px solid transparent",
2290
2294
  outlineOffset: 1,
2291
2295
  borderRadius: 3,
2292
- transition: "outline .12s",
2296
+ transition: "outline .12s, border-color .12s",
2293
2297
  position: "relative",
2294
- ...editChromeBorder(C, preview),
2298
+ ...preview ? {} : {
2299
+ border: hover ? CONTENT_BLOCK_HOVER_BORDER : "1px solid transparent",
2300
+ boxSizing: "border-box"
2301
+ },
2295
2302
  ...shellBg,
2296
2303
  padding: paddingCss(p.padding),
2297
2304
  margin: marginCss(p.margin)
@@ -2510,6 +2517,8 @@ function NestedRowBlock({
2510
2517
  rowId,
2511
2518
  cellIdx,
2512
2519
  parentBlockIdx,
2520
+ /** When this layout sits inside another layout column, meta uses `contentIdx` + `inner` like other nested blocks. */
2521
+ selectionInner,
2513
2522
  editorId,
2514
2523
  selectedKey,
2515
2524
  selContentMeta,
@@ -2521,7 +2530,7 @@ function NestedRowBlock({
2521
2530
  C
2522
2531
  }) {
2523
2532
  const p = block.props;
2524
- const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner && selectedKey === block.id;
2533
+ 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);
2525
2534
  const rowStyle = {
2526
2535
  padding: p.padding ?? 8,
2527
2536
  position: "relative",
@@ -2529,7 +2538,7 @@ function NestedRowBlock({
2529
2538
  outline: rowSelected ? `2px solid ${C.accent}` : "2px solid transparent",
2530
2539
  outlineOffset: 1,
2531
2540
  transition: "outline .12s",
2532
- ...editChromeBorder(C, preview),
2541
+ ...layoutSplitChromeBorder(C, preview),
2533
2542
  ...backgroundLayerStyle({
2534
2543
  bgColor: p.bgColor,
2535
2544
  bgImage: p.bgImage,
@@ -2545,7 +2554,13 @@ function NestedRowBlock({
2545
2554
  style: rowStyle,
2546
2555
  onClick: preview ? void 0 : (e) => {
2547
2556
  e.stopPropagation();
2548
- onSelectContent(block.id, rowId, cellIdx, parentBlockIdx, null);
2557
+ onSelectContent(
2558
+ block.id,
2559
+ rowId,
2560
+ cellIdx,
2561
+ parentBlockIdx,
2562
+ selectionInner ?? null
2563
+ );
2549
2564
  },
2550
2565
  children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
2551
2566
  const cS = p.columnStyles && p.columnStyles[ici] || {};
@@ -2555,7 +2570,10 @@ function NestedRowBlock({
2555
2570
  style: {
2556
2571
  flex: p.ratios?.[ici] ?? 1,
2557
2572
  minWidth: 0,
2558
- ...editChromeBorder(C, preview),
2573
+ minHeight: preview ? void 0 : EMPTY_CELL_COLUMN_MIN_H,
2574
+ display: "flex",
2575
+ flexDirection: "column",
2576
+ ...layoutSplitChromeBorder(C, preview),
2559
2577
  ...backgroundLayerStyle(cS),
2560
2578
  padding: paddingCssLayout(cS.padding),
2561
2579
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -2611,7 +2629,13 @@ function BlockItem({
2611
2629
  if (!onSelectContent) return;
2612
2630
  e.stopPropagation();
2613
2631
  if (isSplitLayoutBlock(cb)) {
2614
- onSelectContent(cb.id, rowId, cellIdx, nest ? nest.parentBlockIdx : ci, null);
2632
+ onSelectContent(
2633
+ cb.id,
2634
+ rowId,
2635
+ cellIdx,
2636
+ nest ? nest.parentBlockIdx : ci,
2637
+ nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null
2638
+ );
2615
2639
  } else {
2616
2640
  onSelectContent(
2617
2641
  cb.id,
@@ -2682,6 +2706,7 @@ function BlockItem({
2682
2706
  rowId,
2683
2707
  cellIdx,
2684
2708
  parentBlockIdx: nest ? nest.parentBlockIdx : ci,
2709
+ selectionInner: nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null,
2685
2710
  editorId,
2686
2711
  selectedKey,
2687
2712
  selContentMeta,
@@ -2765,6 +2790,7 @@ function Cell({
2765
2790
  /* @__PURE__ */ jsx3("div", { style: { position: "absolute", left: -4, top: "50%", transform: "translateY(-50%)", width: 8, height: 8, borderRadius: "50%", background: C.accent } }),
2766
2791
  /* @__PURE__ */ jsx3("div", { style: { position: "absolute", right: -4, top: "50%", transform: "translateY(-50%)", width: 8, height: 8, borderRadius: "50%", background: C.accent } })
2767
2792
  ] }) : null;
2793
+ const cellEmpty = !preview && blocks.length === 0;
2768
2794
  return /* @__PURE__ */ jsxs2(
2769
2795
  "div",
2770
2796
  {
@@ -2774,17 +2800,43 @@ function Cell({
2774
2800
  onDragLeave: handleDragLeave,
2775
2801
  onDrop: handleDrop,
2776
2802
  style: {
2777
- flex: 1,
2778
- minHeight: preview ? void 0 : 44,
2803
+ flex: cellEmpty ? 1 : void 0,
2804
+ display: "flex",
2805
+ flexDirection: "column",
2806
+ minHeight: preview ? void 0 : cellEmpty ? 0 : 44,
2779
2807
  borderRadius: 4,
2780
- ...preview ? {} : insertAt !== null ? { border: `1px dotted ${C.accent}`, background: `${C.accent}0a` } : editChromeBorder(C, false),
2781
- background: insertAt !== null ? `${C.accent}0a` : "transparent",
2808
+ ...preview ? {} : insertAt !== null && !cellEmpty ? { border: `1px dotted ${C.accent}`, background: `${C.accent}0a` } : {},
2809
+ background: insertAt !== null && !cellEmpty ? `${C.accent}0a` : "transparent",
2782
2810
  transition: "border-color .15s,background .15s",
2783
2811
  padding: preview ? 0 : 0,
2784
2812
  position: "relative"
2785
2813
  },
2786
2814
  children: [
2787
- !preview && blocks.length === 0 && /* @__PURE__ */ jsx3("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" }),
2815
+ cellEmpty && /* @__PURE__ */ jsx3(
2816
+ "div",
2817
+ {
2818
+ style: {
2819
+ flex: 1,
2820
+ display: "flex",
2821
+ alignItems: "center",
2822
+ justifyContent: "center",
2823
+ margin: 4,
2824
+ borderRadius: 8,
2825
+ boxSizing: "border-box",
2826
+ textAlign: "center",
2827
+ userSelect: "none",
2828
+ transition: "color .15s, border-color .15s, background .15s, box-shadow .15s",
2829
+ border: insertAt !== null ? `2px dashed ${C.accent}` : `2px dashed ${C.border}`,
2830
+ background: insertAt !== null ? `${C.accent}14` : `${C.canvas}`,
2831
+ boxShadow: insertAt !== null ? `inset 0 0 0 1px ${C.accent}22` : `inset 0 0 0 1px ${C.border}99`,
2832
+ color: insertAt !== null ? C.accent2 : C.muted,
2833
+ fontSize: 11,
2834
+ fontWeight: insertAt !== null ? 700 : 500,
2835
+ letterSpacing: "0.02em"
2836
+ },
2837
+ children: insertAt !== null ? "Release to drop" : "Drop here"
2838
+ }
2839
+ ),
2788
2840
  /* @__PURE__ */ jsx3(Line, { idx: 0 }),
2789
2841
  blocks.map((cb, ci) => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3(
2790
2842
  BlockItem,
@@ -2835,7 +2887,6 @@ function LayoutRow({
2835
2887
  transition: "outline .12s",
2836
2888
  padding: row.padding,
2837
2889
  position: "relative",
2838
- ...editChromeBorder(C, preview),
2839
2890
  ...backgroundLayerStyle({
2840
2891
  bgColor: row.bgColor,
2841
2892
  bgImage: row.bgImage,
@@ -2874,8 +2925,9 @@ function LayoutRow({
2874
2925
  id: editorId ? `${editorId}-col-${row.id}-${ci}` : void 0,
2875
2926
  style: {
2876
2927
  flex: row.ratios[ci] ?? 1,
2877
- minHeight: 50,
2878
- ...editChromeBorder(C, preview),
2928
+ minHeight: preview ? 50 : EMPTY_CELL_COLUMN_MIN_H,
2929
+ display: "flex",
2930
+ flexDirection: "column",
2879
2931
  ...backgroundLayerStyle(cS),
2880
2932
  padding: paddingCssLayout(cS.padding),
2881
2933
  borderRadius: radiusCssLayout(cS.borderRadius),
@@ -2989,7 +3041,7 @@ var CanvasRow = ({
2989
3041
  import { Image as ImageIcon2, Repeat as RepeatIcon, Scaling as Scaling2, Crosshair as Crosshair2, Blend as GradientIcon2, Droplets as ColorIcon } from "lucide-react";
2990
3042
 
2991
3043
  // src/editor/properties/PropertyEditors.tsx
2992
- import { useRef as useRef2, useState as useState3, useEffect as useEffect3, Fragment as Fragment3 } from "react";
3044
+ import { useRef as useRef2, useState as useState3, useEffect as useEffect3, useMemo, Fragment as Fragment3 } from "react";
2993
3045
  import {
2994
3046
  AlignCenter as AlignCenter2,
2995
3047
  AlignJustify as AlignJustify2,
@@ -3090,13 +3142,13 @@ var FONTS = [
3090
3142
  "Trebuchet MS,sans-serif",
3091
3143
  "Impact,sans-serif"
3092
3144
  ];
3093
- function paddingToCss(pad, fallback) {
3094
- if (typeof pad === "number" && Number.isFinite(pad)) return `${Math.max(0, pad)}px`;
3095
- if (pad && typeof pad === "object" && !Array.isArray(pad)) {
3096
- const t = typeof pad.top === "number" && Number.isFinite(pad.top) ? pad.top : fallback;
3097
- const r = typeof pad.right === "number" && Number.isFinite(pad.right) ? pad.right : t;
3098
- const b = typeof pad.bottom === "number" && Number.isFinite(pad.bottom) ? pad.bottom : t;
3099
- const l = typeof pad.left === "number" && Number.isFinite(pad.left) ? pad.left : r;
3145
+ function paddingToCss(pad3, fallback) {
3146
+ if (typeof pad3 === "number" && Number.isFinite(pad3)) return `${Math.max(0, pad3)}px`;
3147
+ if (pad3 && typeof pad3 === "object" && !Array.isArray(pad3)) {
3148
+ const t = typeof pad3.top === "number" && Number.isFinite(pad3.top) ? pad3.top : fallback;
3149
+ const r = typeof pad3.right === "number" && Number.isFinite(pad3.right) ? pad3.right : t;
3150
+ const b = typeof pad3.bottom === "number" && Number.isFinite(pad3.bottom) ? pad3.bottom : t;
3151
+ const l = typeof pad3.left === "number" && Number.isFinite(pad3.left) ? pad3.left : r;
3100
3152
  return `${t}px ${r}px ${b}px ${l}px`;
3101
3153
  }
3102
3154
  return `${fallback}px`;
@@ -3310,11 +3362,8 @@ function ToolbarTypographyControls({ editor, C }) {
3310
3362
  }
3311
3363
  function FormattingToolbar({
3312
3364
  editor,
3313
- C,
3314
- mergeTags,
3315
- onMergeTagInsert
3365
+ C
3316
3366
  }) {
3317
- const [mergeSel, setMergeSel] = useState2("");
3318
3367
  const [linkPanelOpen, setLinkPanelOpen] = useState2(false);
3319
3368
  const [linkUrlValue, setLinkUrlValue] = useState2("");
3320
3369
  const ibtn = (active) => toolbarIconBtn(C, active);
@@ -3634,8 +3683,6 @@ function TextRichEditor({
3634
3683
  value,
3635
3684
  onChange,
3636
3685
  typography,
3637
- mergeTags,
3638
- onMergeTagInsert,
3639
3686
  placeholder = "Write your message\u2026",
3640
3687
  headerTitle = "Rich editor",
3641
3688
  C
@@ -3779,15 +3826,7 @@ function TextRichEditor({
3779
3826
  ]
3780
3827
  }
3781
3828
  ),
3782
- /* @__PURE__ */ jsx4(
3783
- FormattingToolbar,
3784
- {
3785
- editor,
3786
- C,
3787
- mergeTags,
3788
- onMergeTagInsert
3789
- }
3790
- ),
3829
+ /* @__PURE__ */ jsx4(FormattingToolbar, { editor, C }),
3791
3830
  /* @__PURE__ */ jsx4("style", { children: proseStyle }),
3792
3831
  /* @__PURE__ */ jsx4(
3793
3832
  "div",
@@ -3838,15 +3877,7 @@ function TextRichEditor({
3838
3877
  background: C.inputBg
3839
3878
  },
3840
3879
  children: [
3841
- /* @__PURE__ */ jsx4(
3842
- FormattingToolbar,
3843
- {
3844
- editor,
3845
- C,
3846
- mergeTags,
3847
- onMergeTagInsert
3848
- }
3849
- ),
3880
+ /* @__PURE__ */ jsx4(FormattingToolbar, { editor, C }),
3850
3881
  /* @__PURE__ */ jsx4("style", { children: proseStyle }),
3851
3882
  /* @__PURE__ */ jsx4(
3852
3883
  "div",
@@ -4846,7 +4877,7 @@ function BlockSurfaceBgInspector({
4846
4877
  ] }) : null
4847
4878
  ] });
4848
4879
  }
4849
- function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }) {
4880
+ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4850
4881
  const { IS, CI } = useIS(C);
4851
4882
  const imageFileRef = useRef2(null);
4852
4883
  const p = block.props;
@@ -4865,7 +4896,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4865
4896
  /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: !!p[k], onChange: (e) => set(k, e.target.checked), style: { width: 15, height: 15, accentColor: C.accent } }),
4866
4897
  /* @__PURE__ */ jsx6("span", { style: { color: C.muted, fontSize: 12 }, children: p[k] ? "On" : "Off" })
4867
4898
  ] }) });
4868
- const TagPicker = (_field) => null;
4869
4899
  const FONTS2 = ["Georgia,serif", "Arial,sans-serif", "Verdana,sans-serif", "'Courier New',monospace", "Trebuchet MS,sans-serif", "Impact,sans-serif"];
4870
4900
  const ImgUpload = (key) => {
4871
4901
  if (!onUpload) return null;
@@ -4914,12 +4944,12 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4914
4944
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
4915
4945
  /* @__PURE__ */ jsx6(PR, { label: "Line height", C, children: /* @__PURE__ */ jsx6("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) }) }),
4916
4946
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4917
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
4947
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4918
4948
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4919
4949
  ] });
4920
4950
  case "text":
4921
4951
  return /* @__PURE__ */ jsxs4(Fragment4, { children: [
4922
- /* @__PURE__ */ jsx6(PR, { label: "Content (HTML)", C, children: /* @__PURE__ */ jsx6(
4952
+ /* @__PURE__ */ jsx6(PR, { label: "Content", C, children: /* @__PURE__ */ jsx6(
4923
4953
  "textarea",
4924
4954
  {
4925
4955
  style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
@@ -4930,24 +4960,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4930
4960
  },
4931
4961
  block.id
4932
4962
  ) }),
4933
- mergeTags && mergeTags.length > 0 ? /* @__PURE__ */ jsx6(PR, { label: "Insert merge tag", C, children: /* @__PURE__ */ jsxs4(
4934
- "select",
4935
- {
4936
- style: IS,
4937
- defaultValue: "",
4938
- onChange: (e) => {
4939
- const v = e.target.value;
4940
- if (v) {
4941
- set("content", normalizeRichHtmlForStorage((p.content || "") + " " + v));
4942
- e.target.value = "";
4943
- }
4944
- },
4945
- children: [
4946
- /* @__PURE__ */ jsx6("option", { value: "", children: "Choose\u2026" }),
4947
- mergeTags.map((t) => /* @__PURE__ */ jsx6("option", { value: t.value, children: t.name }, t.name))
4948
- ]
4949
- }
4950
- ) }) : null,
4951
4963
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Font size", value: p.fontSize ?? 15, onChange: (n) => set("fontSize", n), min: 8, max: 96, step: 1, C }),
4952
4964
  Col("color", "Color"),
4953
4965
  /* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
@@ -4958,7 +4970,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4958
4970
  /* @__PURE__ */ jsx6(PR, { label: "Line height", C, children: /* @__PURE__ */ jsx6("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) }) }),
4959
4971
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Letter spacing", value: p.letterSpacing ?? 0, onChange: (n) => set("letterSpacing", n), min: 0, max: 30, step: 0.5, C }),
4960
4972
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4961
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
4973
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4962
4974
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4963
4975
  ] });
4964
4976
  case "html":
@@ -4977,15 +4989,13 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4977
4989
  letterSpacing: p.letterSpacing,
4978
4990
  padding: p.padding
4979
4991
  },
4980
- mergeTags,
4981
- onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
4982
4992
  placeholder: "Write your rich content\u2026",
4983
4993
  headerTitle: "Rich editor",
4984
4994
  C
4985
4995
  }
4986
4996
  ) }, block.id),
4987
4997
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
4988
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
4998
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
4989
4999
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
4990
5000
  ] });
4991
5001
  case "image":
@@ -5008,7 +5018,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5008
5018
  /* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5009
5019
  /* @__PURE__ */ jsx6(BorderRadiusEditor, { value: p.borderRadius, onChange: (br) => set("borderRadius", br), C }),
5010
5020
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5011
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5021
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5012
5022
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5013
5023
  ] });
5014
5024
  case "button":
@@ -5028,7 +5038,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5028
5038
  Sel("fontWeight", "Weight", ["400", "500", "600", "700", "800"]),
5029
5039
  Tog("fullWidth", "Full Width"),
5030
5040
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "backdrop", p, set, C, onUpload, syncKey: block.id }),
5031
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5041
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5032
5042
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5033
5043
  ] });
5034
5044
  case "link":
@@ -5043,7 +5053,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5043
5053
  Sel("fontFamily", "Font", FONTS2),
5044
5054
  Tog("underline", "Underline"),
5045
5055
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5046
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5056
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5047
5057
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5048
5058
  ] });
5049
5059
  case "divider":
@@ -5052,14 +5062,14 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5052
5062
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Thickness", value: p.thickness ?? 1, onChange: (n) => set("thickness", n), min: 1, max: 20, step: 1, C }),
5053
5063
  /* @__PURE__ */ jsx6(DividerStyleButtons, { value: p.style, onChange: (s) => set("style", s), C }),
5054
5064
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5055
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5065
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5056
5066
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5057
5067
  ] });
5058
5068
  case "spacer":
5059
5069
  return /* @__PURE__ */ jsxs4(Fragment4, { children: [
5060
5070
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Height", value: p.height ?? 24, onChange: (n) => set("height", n), min: 4, max: 300, step: 1, C }),
5061
5071
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5062
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5072
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5063
5073
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5064
5074
  ] });
5065
5075
  case "social":
@@ -5108,7 +5118,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5108
5118
  Sel("shape", "Shape", ["circle", "square"]),
5109
5119
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Gap", value: p.gap ?? 6, onChange: (n) => set("gap", n), min: 0, max: 40, step: 1, C }),
5110
5120
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5111
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5121
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5112
5122
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5113
5123
  ] });
5114
5124
  case "video":
@@ -5168,7 +5178,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5168
5178
  ] }),
5169
5179
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Height", value: p.height ?? 280, onChange: (n) => set("height", n), min: 80, max: 720, step: 1, C }),
5170
5180
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5171
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5181
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5172
5182
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5173
5183
  ] });
5174
5184
  case "menu":
@@ -5194,7 +5204,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5194
5204
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Font size", value: p.fontSize ?? 14, onChange: (n) => set("fontSize", n), min: 8, max: 48, step: 1, C }),
5195
5205
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Item gap", value: p.gap ?? 20, onChange: (n) => set("gap", n), min: 0, max: 80, step: 1, C }),
5196
5206
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5197
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5207
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5198
5208
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5199
5209
  ] });
5200
5210
  case "timer": {
@@ -5234,7 +5244,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5234
5244
  /* @__PURE__ */ jsx6(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right"], C }),
5235
5245
  Tog("showLabels", "Show Labels"),
5236
5246
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "backdrop", p, set, C, onUpload, syncKey: block.id }),
5237
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5247
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5238
5248
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5239
5249
  ] });
5240
5250
  }
@@ -5331,8 +5341,8 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5331
5341
  Tog("striped", "Striped rows"),
5332
5342
  /* @__PURE__ */ jsx6(NumRangePx, { label: "Font size", value: p.fontSize ?? 13, onChange: (n) => set("fontSize", n), min: 10, max: 24, step: 1, C }),
5333
5343
  /* @__PURE__ */ jsx6(BlockSurfaceBgInspector, { variant: "surface", p, set, C, onUpload, syncKey: block.id }),
5334
- /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad) => set("padding", pad), C }),
5335
- /* @__PURE__ */ jsx6(PaddingEditor, { label: "Cell padding", value: p.cellPadding, onChange: (pad) => set("cellPadding", pad), C }),
5344
+ /* @__PURE__ */ jsx6(PaddingEditor, { value: p.padding, onChange: (pad3) => set("padding", pad3), C }),
5345
+ /* @__PURE__ */ jsx6(PaddingEditor, { label: "Cell padding", value: p.cellPadding, onChange: (pad3) => set("cellPadding", pad3), C }),
5336
5346
  /* @__PURE__ */ jsx6(MarginEditor, { value: p.margin, onChange: (m) => set("margin", m), C })
5337
5347
  ] });
5338
5348
  case "layout":
@@ -5385,6 +5395,19 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5385
5395
  return null;
5386
5396
  }
5387
5397
  }
5398
+ function inferLayoutRowBgStep(r) {
5399
+ if (r.bgGradient) return "gradient";
5400
+ if (String(r.bgImage || "").trim()) return "image";
5401
+ if (String(r.bgColor || "").trim()) return "solid";
5402
+ return null;
5403
+ }
5404
+ function inferColumnBgStep(columnStyles, selCol) {
5405
+ const cs = (columnStyles || {})[selCol] || {};
5406
+ if (cs.bgGradient) return "gradient";
5407
+ if (String(cs.bgImage || "").trim()) return "image";
5408
+ if (String(cs.bgColor || "").trim()) return "solid";
5409
+ return null;
5410
+ }
5388
5411
  function LayoutRowEditor({
5389
5412
  row,
5390
5413
  onChange,
@@ -5396,49 +5419,37 @@ function LayoutRowEditor({
5396
5419
  customizationHint
5397
5420
  }) {
5398
5421
  const { IS, CI } = useIS(C);
5399
- const [selCol, setSelCol] = useState3(initialCol);
5400
- const [rowBgUiStep, setRowBgUiStep] = useState3(null);
5401
- const [colBgUiStep, setColBgUiStep] = useState3(null);
5422
+ const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
5423
+ const ratioList = row.ratios?.length ? row.ratios : [1];
5424
+ const [selCol, setSelCol] = useState3(0);
5425
+ const [rowBgPickerMode, setRowBgPickerMode] = useState3(null);
5426
+ const [colBgPickerMode, setColBgPickerMode] = useState3(null);
5427
+ const inferredRowBg = useMemo(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
5428
+ const inferredColBg = useMemo(
5429
+ () => inferColumnBgStep(row.columnStyles, selCol),
5430
+ [row.columnStyles, selCol]
5431
+ );
5432
+ const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
5433
+ const colBgUiStep = inferredColBg ?? colBgPickerMode;
5402
5434
  useEffect3(() => {
5403
- if (initialCol !== null && initialCol !== void 0) {
5404
- setSelCol(initialCol);
5435
+ if (initialCol !== null && initialCol !== void 0 && initialCol >= 0) {
5436
+ setSelCol(Math.min(initialCol, colCount - 1));
5437
+ } else {
5438
+ setSelCol((c) => Math.min(Math.max(0, c), colCount - 1));
5405
5439
  }
5406
- }, [initialCol]);
5440
+ }, [initialCol, row.id, colCount]);
5407
5441
  useEffect3(() => {
5408
- if (row.bgGradient) {
5409
- setRowBgUiStep("gradient");
5410
- return;
5411
- }
5412
- if (String(row.bgImage || "").trim()) {
5413
- setRowBgUiStep("image");
5414
- return;
5415
- }
5416
- if (String(row.bgColor || "").trim()) {
5417
- setRowBgUiStep("solid");
5418
- return;
5419
- }
5420
- setRowBgUiStep(null);
5442
+ setRowBgPickerMode(null);
5421
5443
  }, [row.id]);
5422
5444
  useEffect3(() => {
5423
- if (selCol === null) {
5424
- setColBgUiStep(null);
5425
- return;
5426
- }
5427
- const cs = (row.columnStyles || {})[selCol] || {};
5428
- if (cs.bgGradient) {
5429
- setColBgUiStep("gradient");
5430
- return;
5431
- }
5432
- if (String(cs.bgImage || "").trim()) {
5433
- setColBgUiStep("image");
5434
- return;
5435
- }
5436
- if (String(cs.bgColor || "").trim()) {
5437
- setColBgUiStep("solid");
5438
- return;
5439
- }
5440
- setColBgUiStep(null);
5445
+ setColBgPickerMode(null);
5441
5446
  }, [selCol, row.id]);
5447
+ useEffect3(() => {
5448
+ if (inferredRowBg) setRowBgPickerMode(null);
5449
+ }, [inferredRowBg]);
5450
+ useEffect3(() => {
5451
+ if (inferredColBg) setColBgPickerMode(null);
5452
+ }, [inferredColBg]);
5442
5453
  const set = (k, v) => onChange({ ...row, [k]: v });
5443
5454
  const applyColumnCount = (n) => {
5444
5455
  const prevCells = [...row.cells || []];
@@ -5454,16 +5465,13 @@ function LayoutRowEditor({
5454
5465
  }
5455
5466
  onChange({ ...row, cols: n, preset: "custom", cells, ratios });
5456
5467
  };
5457
- const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
5458
- const ratioList = row.ratios?.length ? row.ratios : [1];
5459
5468
  const updCol = (i, upd) => {
5460
5469
  const styles = { ...row.columnStyles || {} };
5461
5470
  styles[i] = { ...styles[i] || {}, ...upd };
5462
5471
  set("columnStyles", styles);
5463
5472
  };
5464
5473
  const setColBgMode = (mode) => {
5465
- if (selCol === null) return;
5466
- setColBgUiStep(mode);
5474
+ setColBgPickerMode(mode);
5467
5475
  const cs = (row.columnStyles || {})[selCol] || {};
5468
5476
  if (mode === "solid") {
5469
5477
  updCol(selCol, { bgGradient: null, bgImage: "" });
@@ -5481,7 +5489,7 @@ function LayoutRowEditor({
5481
5489
  const total = ratioList.reduce((a, b) => a + b, 0) || 1;
5482
5490
  const POS_OPTS = ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"];
5483
5491
  const setRowBgMode = (mode) => {
5484
- setRowBgUiStep(mode);
5492
+ setRowBgPickerMode(mode);
5485
5493
  if (mode === "solid") {
5486
5494
  set("bgGradient", null);
5487
5495
  set("bgImage", "");
@@ -5639,7 +5647,7 @@ function LayoutRowEditor({
5639
5647
  },
5640
5648
  i
5641
5649
  )) }),
5642
- selCol !== null && /* @__PURE__ */ jsxs4("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
5650
+ colCount > 0 && /* @__PURE__ */ jsxs4("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
5643
5651
  /* @__PURE__ */ jsx6(BgModeButtons, { value: colBgUiStep, onChange: setColBgMode, C }),
5644
5652
  colBgUiStep === null ? /* @__PURE__ */ jsx6("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,
5645
5653
  colBgUiStep === "solid" ? /* @__PURE__ */ jsx6(PR, { label: `col ${selCol + 1} bg`, C, children: /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
@@ -5727,7 +5735,7 @@ function LayoutRowEditor({
5727
5735
  }
5728
5736
  ) })
5729
5737
  ] }) : null,
5730
- /* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad) => updCol(selCol, { padding: pad }), C }),
5738
+ /* @__PURE__ */ jsx6(PaddingEditor, { label: "Column padding", value: (row.columnStyles || {})[selCol]?.padding, onChange: (pad3) => updCol(selCol, { padding: pad3 }), C }),
5731
5739
  /* @__PURE__ */ jsx6(BorderRadiusEditor, { label: "Column radius", value: (row.columnStyles || {})[selCol]?.borderRadius, onChange: (br) => updCol(selCol, { borderRadius: br }), C })
5732
5740
  ] })
5733
5741
  ] })
@@ -5738,7 +5746,7 @@ function LayoutRowEditor({
5738
5746
  import {
5739
5747
  useState as useState4,
5740
5748
  useEffect as useEffect4,
5741
- useMemo,
5749
+ useMemo as useMemo2,
5742
5750
  useRef as useRef3,
5743
5751
  useLayoutEffect
5744
5752
  } from "react";
@@ -6010,8 +6018,8 @@ function MobilePhoneScaleSlot({ variantKey, children }) {
6010
6018
  const pw = phone.offsetWidth;
6011
6019
  const ph = phone.offsetHeight;
6012
6020
  if (cw < 12 || ch < 12 || pw < 12 || ph < 12) return;
6013
- const pad = 20;
6014
- const raw = Math.min(1, (cw - pad) / pw, (ch - pad) / ph);
6021
+ const pad3 = 20;
6022
+ const raw = Math.min(1, (cw - pad3) / pw, (ch - pad3) / ph);
6015
6023
  const s = Number.isFinite(raw) ? Math.max(0.2, raw) : 1;
6016
6024
  setFit({ s, w: pw * s, h: ph * s });
6017
6025
  };
@@ -6116,8 +6124,8 @@ function PreviewModal({
6116
6124
  const iframeHDesktop = "min(720px, calc(100vh - 200px))";
6117
6125
  const mobileIframeW = previewIframeWidthPx("mobile", mobileVariant);
6118
6126
  const tabletIframeW = previewIframeWidthPx("tablet");
6119
- const srcDocMobile = useMemo(() => previewEmailSrcDoc(html, mobileIframeW), [html, mobileIframeW]);
6120
- const srcDocTablet = useMemo(() => previewEmailSrcDoc(html, tabletIframeW), [html, tabletIframeW]);
6127
+ const srcDocMobile = useMemo2(() => previewEmailSrcDoc(html, mobileIframeW), [html, mobileIframeW]);
6128
+ const srcDocTablet = useMemo2(() => previewEmailSrcDoc(html, tabletIframeW), [html, tabletIframeW]);
6121
6129
  const IframeContent = ({
6122
6130
  srcDoc,
6123
6131
  title: iframeTitle,
@@ -6513,7 +6521,7 @@ var ReactEmailEditor = forwardRef(
6513
6521
  style = {}
6514
6522
  }, ref) {
6515
6523
  const opt = options;
6516
- const mergedThemes = useMemo2(
6524
+ const mergedThemes = useMemo3(
6517
6525
  () => ({ ...THEMES, ...opt.appearance?.customThemes || {} }),
6518
6526
  [opt.appearance?.customThemes]
6519
6527
  );
@@ -6536,7 +6544,6 @@ var ReactEmailEditor = forwardRef(
6536
6544
  const [locale, setLocale] = useState6(
6537
6545
  typeof opt.locale === "string" ? opt.locale : "en"
6538
6546
  );
6539
- const [mergeTags, setMergeTagsState] = useState6(() => opt.mergeTags ?? []);
6540
6547
  const [zoom, setZoom] = useState6(100);
6541
6548
  const [rightRailWidth, setRightRailWidth] = useState6(RIGHT_RAIL_DEFAULT_W);
6542
6549
  const [ctxMenu, setCtxMenu] = useState6(null);
@@ -6882,6 +6889,43 @@ var ReactEmailEditor = forwardRef(
6882
6889
  }
6883
6890
  }
6884
6891
  };
6892
+ const insertBlockFromLibrary = (contentType) => {
6893
+ if (!rows.length) return;
6894
+ if (selContentMeta?.inner) {
6895
+ const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
6896
+ dropContent(rowId, cellIdx, {
6897
+ kind: "new",
6898
+ contentType,
6899
+ insertAt: null,
6900
+ nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
6901
+ });
6902
+ return;
6903
+ }
6904
+ if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
6905
+ const r = rows.find((x) => x.id === selContentMeta.rowId);
6906
+ if (r && selContentMeta.cellIdx < r.cells.length) {
6907
+ dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
6908
+ kind: "new",
6909
+ contentType,
6910
+ insertAt: null
6911
+ });
6912
+ return;
6913
+ }
6914
+ }
6915
+ if (selectedRowId) {
6916
+ const targetRow = rows.find((r) => r.id === selectedRowId);
6917
+ if (targetRow) {
6918
+ dropContent(targetRow.id, 0, {
6919
+ kind: "new",
6920
+ contentType,
6921
+ insertAt: null
6922
+ });
6923
+ return;
6924
+ }
6925
+ }
6926
+ const main = rows[0];
6927
+ dropContent(main.id, 0, { kind: "new", contentType, insertAt: null });
6928
+ };
6885
6929
  const deleteContent = (rowId, cellIdx, ci, inner = null) => {
6886
6930
  if (!inner) {
6887
6931
  mutate((prev) => prev.map((r) => {
@@ -6931,7 +6975,7 @@ var ReactEmailEditor = forwardRef(
6931
6975
  }));
6932
6976
  return;
6933
6977
  }
6934
- const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: inner.parentBlockIdx, innerCellIdx: inner.cellIdx };
6978
+ const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: ci, innerCellIdx: inner.cellIdx };
6935
6979
  mutate((prev) => updateColumnAt(prev, loc, (c) => c.map((b, j) => j === inner.contentIdx ? upd : b)));
6936
6980
  };
6937
6981
  const selectContent = (contentId, rowId, cellIdx, parentBlockIdx, inner = null) => {
@@ -7478,6 +7522,8 @@ var ReactEmailEditor = forwardRef(
7478
7522
  if (data.layoutPresetKey) {
7479
7523
  const preset = LAYOUT_PRESETS.find((p) => p.key === data.layoutPresetKey);
7480
7524
  if (preset) mutate((prev) => [...prev, makeLayoutRow(preset)]);
7525
+ } else if (data.contentType && typeof data.contentType === "string") {
7526
+ insertBlockFromLibrary(data.contentType);
7481
7527
  }
7482
7528
  } catch {
7483
7529
  }
@@ -7518,6 +7564,21 @@ var ReactEmailEditor = forwardRef(
7518
7564
  }
7519
7565
  return base;
7520
7566
  })(),
7567
+ onDragOver: (e) => {
7568
+ e.preventDefault();
7569
+ e.stopPropagation();
7570
+ },
7571
+ onDrop: (e) => {
7572
+ e.preventDefault();
7573
+ e.stopPropagation();
7574
+ try {
7575
+ const data = JSON.parse(e.dataTransfer.getData("application/json") || "{}");
7576
+ if (data.contentType && typeof data.contentType === "string") {
7577
+ insertBlockFromLibrary(data.contentType);
7578
+ }
7579
+ } catch {
7580
+ }
7581
+ },
7521
7582
  children: [
7522
7583
  rows.length === 0 && /* @__PURE__ */ jsxs9("div", { id: eid("canvas-empty"), style: { padding: "56px 20px", textAlign: "center", color: C.muted, border: `2px dashed ${C.border}`, borderRadius: 7 }, children: [
7523
7584
  /* @__PURE__ */ jsx11("div", { style: { fontSize: 30, marginBottom: 10 }, children: "\u2709" }),
@@ -7662,7 +7723,6 @@ var ReactEmailEditor = forwardRef(
7662
7723
  setSelMeta(null);
7663
7724
  setSelectedRowId(null);
7664
7725
  },
7665
- mergeTags,
7666
7726
  onUpload,
7667
7727
  C
7668
7728
  }
@@ -7730,12 +7790,8 @@ var ReactEmailEditor = forwardRef(
7730
7790
  onDragStart: (e) => {
7731
7791
  e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
7732
7792
  },
7733
- onClick: () => {
7734
- const targetRow = selectedRowId ? rows.find((r) => r.id === selectedRowId) : rows[rows.length - 1];
7735
- if (!targetRow) return;
7736
- dropContent(targetRow.id, 0, { kind: "new", contentType: bt.type, insertAt: null });
7737
- },
7738
- title: `Drag into a cell or click to add to ${selectedRowId ? "selected row" : "last row"}`,
7793
+ onClick: () => insertBlockFromLibrary(bt.type),
7794
+ 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",
7739
7795
  style: {
7740
7796
  display: "flex",
7741
7797
  flexDirection: "row",
@@ -8105,11 +8161,79 @@ var ReactEmailEditor = forwardRef(
8105
8161
  );
8106
8162
  }
8107
8163
  );
8164
+
8165
+ // src/lib/htmlToEmailDesign.ts
8166
+ var pad = (n) => ({ top: n, right: n, bottom: n, left: n });
8167
+ function extractHtmlForDesign(html) {
8168
+ const t = String(html ?? "").trim();
8169
+ if (!t) return "";
8170
+ const head = t.slice(0, 800).toLowerCase();
8171
+ if (!head.includes("<html") && !head.includes("<!doctype")) {
8172
+ return normalizeRichHtmlForStorage(t);
8173
+ }
8174
+ try {
8175
+ const doc = new DOMParser().parseFromString(t, "text/html");
8176
+ const body = doc.body;
8177
+ if (!body) return normalizeRichHtmlForStorage(t);
8178
+ return normalizeRichHtmlForStorage(body.innerHTML);
8179
+ } catch {
8180
+ return normalizeRichHtmlForStorage(t);
8181
+ }
8182
+ }
8183
+ function htmlToEmailDesignTemplate(html) {
8184
+ const inner = extractHtmlForDesign(html);
8185
+ if (!inner) return null;
8186
+ const doc = {
8187
+ type: "email_document",
8188
+ settings: {
8189
+ width: 600,
8190
+ backgroundColor: "#f1f5f9",
8191
+ contentBackgroundColor: "#ffffff",
8192
+ fontFamily: "Arial, Helvetica, sans-serif",
8193
+ lineHeightBase: 1.6,
8194
+ color: "#111827",
8195
+ responsive: true,
8196
+ rtl: false
8197
+ },
8198
+ rows: [
8199
+ {
8200
+ id: "row_html_import",
8201
+ type: "row",
8202
+ layout: { columns: 1, gap: 0, stackOnMobile: true, align: "center" },
8203
+ styles: {
8204
+ backgroundColor: "#ffffff",
8205
+ backgroundRepeat: "no-repeat",
8206
+ backgroundSize: "cover",
8207
+ padding: pad(24),
8208
+ textAlign: "left"
8209
+ },
8210
+ columns: [
8211
+ {
8212
+ id: "col_html_import",
8213
+ layout: { width: "100%", verticalAlign: "top", mobileWidth: "100%" },
8214
+ styles: { padding: pad(0), backgroundColor: "" },
8215
+ blocks: [
8216
+ {
8217
+ id: "block_html_import",
8218
+ type: "html",
8219
+ content: { html: inner },
8220
+ styles: {}
8221
+ }
8222
+ ]
8223
+ }
8224
+ ]
8225
+ }
8226
+ ]
8227
+ };
8228
+ return doc;
8229
+ }
8108
8230
  export {
8109
8231
  PreviewModal as EmailPreviewModal,
8110
8232
  ReactEmailEditor,
8111
8233
  base64ToUtf8,
8112
8234
  DEVICES as emailPreviewDevices,
8235
+ extractHtmlForDesign,
8236
+ htmlToEmailDesignTemplate,
8113
8237
  jsonToHtml,
8114
8238
  utf8ToBase64
8115
8239
  };