react-email-studio 3.1.1 → 3.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [3.1.1] - 2026-04-30
9
+
10
+ ### Added
11
+
12
+ - **Loading state for `loadJson`**: `ReactEmailEditor` shows a full-workspace overlay (spinner + localized “Loading design…”) while a design JSON is applied, including invalid-parse early exit.
13
+ - **Lossless studio JSON**: document `settings._reactEmailStudio.editorSettings` stores a snapshot of editor settings; each row may include `_reactEmailStudio.row` (row fields without `cells`) for presets, ratios, `columnStyles`, etc.
14
+ - **i18n**: `loadingDesign` string in `en`, `fr`, `de`, `es`.
15
+
16
+ ### Fixed
17
+
18
+ - **Settings round-trip**: exported document `rtl` / `responsive` now follow `pageRtl` / `pageResponsive` so normalization no longer overwrites them from stale canonical defaults.
19
+ - **Social blocks**: canonical `content.networks` includes URLs from `socialUrls` even when not listed in `networks`; import supports `networks` as an array plus `content.socialUrls`; `styles.padding` is exported/imported; legacy flat-merge skips `socialUrls` where appropriate.
20
+ - **Nested layout `columnStyles`**: importing a layout whose `props.cells` is hydrated no longer drops `columnStyles` that exist only on `content` (merge after hydrate).
21
+ - **Nested layout inspector**: `LayoutRowEditor` background-step UI (row + per-column) re-syncs when `bgColor` / `bgImage` / `bgGradient` / `columnStyles` change, not only when `row.id` changes.
22
+ - **Block palette (click)**: new blocks go to the focused column, a focused nested column, column 0 of a row selected in the rails, or **first row / first column** by default (instead of always column 0 of the last row).
23
+
24
+ ### Notes for integrators
25
+
26
+ - Prefer **`loadJson` / `exportJson`** (or `designToEmailDocument` + `normalizeEmailDesignInput`) for full fidelity; blocks may include a `props` object alongside `content` / `styles` for editor-only fields.
27
+ - **`jsonToHtml`** nested layouts already receive `columnStyles` when present on hydrated blocks.
package/README.md CHANGED
@@ -5,6 +5,10 @@
5
5
  [![npm](https://img.shields.io/npm/v/react-email-studio.svg)](https://www.npmjs.com/package/react-email-studio)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
+ ## Release notes
9
+
10
+ Version history and migration hints: **[CHANGELOG.md](./CHANGELOG.md)** (also included in the published npm tarball under `node_modules/react-email-studio/CHANGELOG.md`).
11
+
8
12
  ---
9
13
 
10
14
  ## Features
@@ -13,7 +17,7 @@
13
17
  - **Page & content** backgrounds (solid, gradient, image), responsive preview
14
18
  - **Undo / redo**, zoom, device preview modal
15
19
  - **Internationalized** UI strings (`en`, `fr`, `de`, `es`)
16
- - **Merge tags** support for templates
20
+ - **Template placeholders** (e.g. `{{first_name}}`) can be typed directly in HTML / text blocks
17
21
  - Tree-shakeable ESM + CJS, TypeScript types
18
22
 
19
23
  ---
package/TUTORIAL.md CHANGED
@@ -134,7 +134,7 @@ Adjust the import path to match your structure.
134
134
  | Prop | Type | Description |
135
135
  |------|------|-------------|
136
136
  | `ref` | `ReactEmailEditorRef` | Imperative API: `loadJson`, `exportJson` (see below). |
137
- | `options` | `ReactEmailEditorOptions` | Editor chrome, locale, merge tags, feature flags, etc. (see §6). |
137
+ | `options` | `ReactEmailEditorOptions` | Editor chrome, locale, feature flags, etc. (see §6). |
138
138
  | `onUpload` | `(file: File) => Promise<string>` | Required for good image/video UX; return public URL string. |
139
139
  | `onLoad` / `onReady` | `(api: ReactEmailEditorRef) => void` | Fired once with the same API object; use to call `loadJson` with server data. |
140
140
  | `hideTemplates` | `boolean` | Hide built-in templates UI; use when you only load via `loadJson`. |
@@ -163,7 +163,6 @@ Adjust the import path to match your structure.
163
163
  | **`customCSS`** | Extra `<style>` content for generated HTML (preview / export parity). |
164
164
  | **`appearance`** | `theme`, `colors`, `customThemes` — chrome theming. |
165
165
  | **`locale`** | `"en"` \| `"fr"` \| `"de"` \| `"es"` for UI strings. |
166
- | **`mergeTags`** | `{ name: string; value: string }[]` for rich-text insertion. |
167
166
  | **`features.autoSave`** | `{ enabled?: boolean; interval?: number }` — UI autosave hint. |
168
167
  | **`tools`** | `{ [blockType: string]: { enabled?: boolean } }` — toggle blocks. |
169
168
  | **`blockLibrary`** | `groupHeadings`, `paletteGroupLabels` — library copy overrides. |
package/dist/index.cjs CHANGED
@@ -152,7 +152,6 @@ var I18N = {
152
152
  delete: "Delete",
153
153
  moveUp: "Move Up",
154
154
  moveDown: "Move Down",
155
- mergeTags: "Merge Tags",
156
155
  autoSaved: "Auto-saved",
157
156
  search: "Search blocks\u2026",
158
157
  zoomIn: "Zoom In",
@@ -211,7 +210,6 @@ var I18N = {
211
210
  delete: "Supprimer",
212
211
  moveUp: "Monter",
213
212
  moveDown: "Descendre",
214
- mergeTags: "Balises fusion",
215
213
  autoSaved: "Auto-sauvegard\xE9",
216
214
  search: "Rechercher\u2026",
217
215
  colorScheme: "Sch\xE9ma de couleurs",
@@ -271,7 +269,6 @@ var I18N = {
271
269
  delete: "L\xF6schen",
272
270
  moveUp: "Nach oben",
273
271
  moveDown: "Nach unten",
274
- mergeTags: "Merge-Tags",
275
272
  autoSaved: "Auto-gespeichert",
276
273
  search: "Bl\xF6cke suchen\u2026",
277
274
  colorScheme: "Farbschema",
@@ -329,7 +326,6 @@ var I18N = {
329
326
  delete: "Eliminar",
330
327
  moveUp: "Subir",
331
328
  moveDown: "Bajar",
332
- mergeTags: "Etiquetas de fusi\xF3n",
333
329
  autoSaved: "Auto-guardado",
334
330
  search: "Buscar bloques\u2026",
335
331
  zoomIn: "Zoom +",
@@ -2238,9 +2234,8 @@ function useLiveCountdown(endDate) {
2238
2234
  const s = Math.floor(diff % 6e4 / 1e3);
2239
2235
  return [d, h, m, s];
2240
2236
  }
2241
- function editChromeBorder(C, preview) {
2242
- if (preview) return {};
2243
- return { border: `1px dotted ${C.border}`, boxSizing: "border-box" };
2237
+ function editChromeBorder(_C, _preview) {
2238
+ return {};
2244
2239
  }
2245
2240
  function blockLinkCaptureHandler(preview, e) {
2246
2241
  if (preview) return;
@@ -2542,6 +2537,8 @@ function NestedRowBlock({
2542
2537
  rowId,
2543
2538
  cellIdx,
2544
2539
  parentBlockIdx,
2540
+ /** When this layout sits inside another layout column, meta uses `contentIdx` + `inner` like other nested blocks. */
2541
+ selectionInner,
2545
2542
  editorId,
2546
2543
  selectedKey,
2547
2544
  selContentMeta,
@@ -2553,7 +2550,7 @@ function NestedRowBlock({
2553
2550
  C
2554
2551
  }) {
2555
2552
  const p = block.props;
2556
- const rowSelected = !preview && selContentMeta && selContentMeta.rowId === rowId && selContentMeta.cellIdx === cellIdx && selContentMeta.contentIdx === parentBlockIdx && !selContentMeta.inner && selectedKey === block.id;
2553
+ 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
2554
  const rowStyle = {
2558
2555
  padding: p.padding ?? 8,
2559
2556
  position: "relative",
@@ -2577,7 +2574,13 @@ function NestedRowBlock({
2577
2574
  style: rowStyle,
2578
2575
  onClick: preview ? void 0 : (e) => {
2579
2576
  e.stopPropagation();
2580
- onSelectContent(block.id, rowId, cellIdx, parentBlockIdx, null);
2577
+ onSelectContent(
2578
+ block.id,
2579
+ rowId,
2580
+ cellIdx,
2581
+ parentBlockIdx,
2582
+ selectionInner ?? null
2583
+ );
2581
2584
  },
2582
2585
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: p.gap ?? 12 }, children: (p.cells || []).map((innerBlocks, ici) => {
2583
2586
  const cS = p.columnStyles && p.columnStyles[ici] || {};
@@ -2643,7 +2646,13 @@ function BlockItem({
2643
2646
  if (!onSelectContent) return;
2644
2647
  e.stopPropagation();
2645
2648
  if (isSplitLayoutBlock(cb)) {
2646
- onSelectContent(cb.id, rowId, cellIdx, nest ? nest.parentBlockIdx : ci, null);
2649
+ onSelectContent(
2650
+ cb.id,
2651
+ rowId,
2652
+ cellIdx,
2653
+ nest ? nest.parentBlockIdx : ci,
2654
+ nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null
2655
+ );
2647
2656
  } else {
2648
2657
  onSelectContent(
2649
2658
  cb.id,
@@ -2714,6 +2723,7 @@ function BlockItem({
2714
2723
  rowId,
2715
2724
  cellIdx,
2716
2725
  parentBlockIdx: nest ? nest.parentBlockIdx : ci,
2726
+ selectionInner: nest ? { cellIdx: innerCellIdx, contentIdx: ci } : null,
2717
2727
  editorId,
2718
2728
  selectedKey,
2719
2729
  selContentMeta,
@@ -3301,11 +3311,8 @@ function ToolbarTypographyControls({ editor, C }) {
3301
3311
  }
3302
3312
  function FormattingToolbar({
3303
3313
  editor,
3304
- C,
3305
- mergeTags,
3306
- onMergeTagInsert
3314
+ C
3307
3315
  }) {
3308
- const [mergeSel, setMergeSel] = (0, import_react2.useState)("");
3309
3316
  const [linkPanelOpen, setLinkPanelOpen] = (0, import_react2.useState)(false);
3310
3317
  const [linkUrlValue, setLinkUrlValue] = (0, import_react2.useState)("");
3311
3318
  const ibtn = (active) => toolbarIconBtn(C, active);
@@ -3625,8 +3632,6 @@ function TextRichEditor({
3625
3632
  value,
3626
3633
  onChange,
3627
3634
  typography,
3628
- mergeTags,
3629
- onMergeTagInsert,
3630
3635
  placeholder = "Write your message\u2026",
3631
3636
  headerTitle = "Rich editor",
3632
3637
  C
@@ -3770,15 +3775,7 @@ function TextRichEditor({
3770
3775
  ]
3771
3776
  }
3772
3777
  ),
3773
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3774
- FormattingToolbar,
3775
- {
3776
- editor,
3777
- C,
3778
- mergeTags,
3779
- onMergeTagInsert
3780
- }
3781
- ),
3778
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
3782
3779
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
3783
3780
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3784
3781
  "div",
@@ -3829,15 +3826,7 @@ function TextRichEditor({
3829
3826
  background: C.inputBg
3830
3827
  },
3831
3828
  children: [
3832
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3833
- FormattingToolbar,
3834
- {
3835
- editor,
3836
- C,
3837
- mergeTags,
3838
- onMergeTagInsert
3839
- }
3840
- ),
3829
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormattingToolbar, { editor, C }),
3841
3830
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: proseStyle }),
3842
3831
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3843
3832
  "div",
@@ -4837,7 +4826,7 @@ function BlockSurfaceBgInspector({
4837
4826
  ] }) : null
4838
4827
  ] });
4839
4828
  }
4840
- function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }) {
4829
+ function ContentBlockEditor({ block, onChange, onClose, onUpload, C }) {
4841
4830
  const { IS, CI } = useIS(C);
4842
4831
  const imageFileRef = (0, import_react4.useRef)(null);
4843
4832
  const p = block.props;
@@ -4856,7 +4845,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4856
4845
  /* @__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
4846
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: C.muted, fontSize: 12 }, children: p[k] ? "On" : "Off" })
4858
4847
  ] }) });
4859
- const TagPicker = (_field) => null;
4860
4848
  const FONTS2 = ["Georgia,serif", "Arial,sans-serif", "Verdana,sans-serif", "'Courier New',monospace", "Trebuchet MS,sans-serif", "Impact,sans-serif"];
4861
4849
  const ImgUpload = (key) => {
4862
4850
  if (!onUpload) return null;
@@ -4910,7 +4898,7 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4910
4898
  ] });
4911
4899
  case "text":
4912
4900
  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)(
4901
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PR, { label: "Content", C, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4914
4902
  "textarea",
4915
4903
  {
4916
4904
  style: { ...IS, minHeight: 200, resize: "vertical", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", fontSize: 12, lineHeight: 1.45 },
@@ -4921,24 +4909,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4921
4909
  },
4922
4910
  block.id
4923
4911
  ) }),
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
4912
  /* @__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
4913
  Col("color", "Color"),
4944
4914
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignButtons, { value: p.align, onChange: (v) => set("align", v), options: ["left", "center", "right", "justify"], C }),
@@ -4968,8 +4938,6 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
4968
4938
  letterSpacing: p.letterSpacing,
4969
4939
  padding: p.padding
4970
4940
  },
4971
- mergeTags,
4972
- onMergeTagInsert: (tag) => set("content", (p.content || "") + " " + tag),
4973
4941
  placeholder: "Write your rich content\u2026",
4974
4942
  headerTitle: "Rich editor",
4975
4943
  C
@@ -5376,6 +5344,19 @@ function ContentBlockEditor({ block, onChange, mergeTags, onClose, onUpload, C }
5376
5344
  return null;
5377
5345
  }
5378
5346
  }
5347
+ function inferLayoutRowBgStep(r) {
5348
+ if (r.bgGradient) return "gradient";
5349
+ if (String(r.bgImage || "").trim()) return "image";
5350
+ if (String(r.bgColor || "").trim()) return "solid";
5351
+ return null;
5352
+ }
5353
+ function inferColumnBgStep(columnStyles, selCol) {
5354
+ const cs = (columnStyles || {})[selCol] || {};
5355
+ if (cs.bgGradient) return "gradient";
5356
+ if (String(cs.bgImage || "").trim()) return "image";
5357
+ if (String(cs.bgColor || "").trim()) return "solid";
5358
+ return null;
5359
+ }
5379
5360
  function LayoutRowEditor({
5380
5361
  row,
5381
5362
  onChange,
@@ -5387,49 +5368,37 @@ function LayoutRowEditor({
5387
5368
  customizationHint
5388
5369
  }) {
5389
5370
  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);
5371
+ const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
5372
+ const ratioList = row.ratios?.length ? row.ratios : [1];
5373
+ const [selCol, setSelCol] = (0, import_react4.useState)(0);
5374
+ const [rowBgPickerMode, setRowBgPickerMode] = (0, import_react4.useState)(null);
5375
+ const [colBgPickerMode, setColBgPickerMode] = (0, import_react4.useState)(null);
5376
+ const inferredRowBg = (0, import_react4.useMemo)(() => inferLayoutRowBgStep(row), [row.bgGradient, row.bgImage, row.bgColor]);
5377
+ const inferredColBg = (0, import_react4.useMemo)(
5378
+ () => inferColumnBgStep(row.columnStyles, selCol),
5379
+ [row.columnStyles, selCol]
5380
+ );
5381
+ const rowBgUiStep = inferredRowBg ?? rowBgPickerMode;
5382
+ const colBgUiStep = inferredColBg ?? colBgPickerMode;
5393
5383
  (0, import_react4.useEffect)(() => {
5394
- if (initialCol !== null && initialCol !== void 0) {
5395
- setSelCol(initialCol);
5384
+ if (initialCol !== null && initialCol !== void 0 && initialCol >= 0) {
5385
+ setSelCol(Math.min(initialCol, colCount - 1));
5386
+ } else {
5387
+ setSelCol((c) => Math.min(Math.max(0, c), colCount - 1));
5396
5388
  }
5397
- }, [initialCol]);
5389
+ }, [initialCol, row.id, colCount]);
5398
5390
  (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);
5391
+ setRowBgPickerMode(null);
5412
5392
  }, [row.id]);
5413
5393
  (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);
5394
+ setColBgPickerMode(null);
5432
5395
  }, [selCol, row.id]);
5396
+ (0, import_react4.useEffect)(() => {
5397
+ if (inferredRowBg) setRowBgPickerMode(null);
5398
+ }, [inferredRowBg]);
5399
+ (0, import_react4.useEffect)(() => {
5400
+ if (inferredColBg) setColBgPickerMode(null);
5401
+ }, [inferredColBg]);
5433
5402
  const set = (k, v) => onChange({ ...row, [k]: v });
5434
5403
  const applyColumnCount = (n) => {
5435
5404
  const prevCells = [...row.cells || []];
@@ -5445,16 +5414,13 @@ function LayoutRowEditor({
5445
5414
  }
5446
5415
  onChange({ ...row, cols: n, preset: "custom", cells, ratios });
5447
5416
  };
5448
- const colCount = Math.max(1, row.ratios?.length || row.cells?.length || 1);
5449
- const ratioList = row.ratios?.length ? row.ratios : [1];
5450
5417
  const updCol = (i, upd) => {
5451
5418
  const styles = { ...row.columnStyles || {} };
5452
5419
  styles[i] = { ...styles[i] || {}, ...upd };
5453
5420
  set("columnStyles", styles);
5454
5421
  };
5455
5422
  const setColBgMode = (mode) => {
5456
- if (selCol === null) return;
5457
- setColBgUiStep(mode);
5423
+ setColBgPickerMode(mode);
5458
5424
  const cs = (row.columnStyles || {})[selCol] || {};
5459
5425
  if (mode === "solid") {
5460
5426
  updCol(selCol, { bgGradient: null, bgImage: "" });
@@ -5472,7 +5438,7 @@ function LayoutRowEditor({
5472
5438
  const total = ratioList.reduce((a, b) => a + b, 0) || 1;
5473
5439
  const POS_OPTS = ["center", "top", "bottom", "left", "right", "top left", "top right", "bottom left", "bottom right"];
5474
5440
  const setRowBgMode = (mode) => {
5475
- setRowBgUiStep(mode);
5441
+ setRowBgPickerMode(mode);
5476
5442
  if (mode === "solid") {
5477
5443
  set("bgGradient", null);
5478
5444
  set("bgImage", "");
@@ -5630,7 +5596,7 @@ function LayoutRowEditor({
5630
5596
  },
5631
5597
  i
5632
5598
  )) }),
5633
- selCol !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
5599
+ colCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { background: C.surface, padding: 10, borderRadius: 8, border: `1px solid ${C.border}` }, children: [
5634
5600
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BgModeButtons, { value: colBgUiStep, onChange: setColBgMode, C }),
5635
5601
  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
5602
  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: [
@@ -6521,7 +6487,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6521
6487
  const [locale, setLocale] = (0, import_react8.useState)(
6522
6488
  typeof opt.locale === "string" ? opt.locale : "en"
6523
6489
  );
6524
- const [mergeTags, setMergeTagsState] = (0, import_react8.useState)(() => opt.mergeTags ?? []);
6525
6490
  const [zoom, setZoom] = (0, import_react8.useState)(100);
6526
6491
  const [rightRailWidth, setRightRailWidth] = (0, import_react8.useState)(RIGHT_RAIL_DEFAULT_W);
6527
6492
  const [ctxMenu, setCtxMenu] = (0, import_react8.useState)(null);
@@ -6916,7 +6881,7 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
6916
6881
  }));
6917
6882
  return;
6918
6883
  }
6919
- const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: inner.parentBlockIdx, innerCellIdx: inner.cellIdx };
6884
+ const loc = { type: "nested", rowId, outerCellIdx: cellIdx, blockIdx: ci, innerCellIdx: inner.cellIdx };
6920
6885
  mutate((prev) => updateColumnAt(prev, loc, (c) => c.map((b, j) => j === inner.contentIdx ? upd : b)));
6921
6886
  };
6922
6887
  const selectContent = (contentId, rowId, cellIdx, parentBlockIdx, inner = null) => {
@@ -7647,7 +7612,6 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7647
7612
  setSelMeta(null);
7648
7613
  setSelectedRowId(null);
7649
7614
  },
7650
- mergeTags,
7651
7615
  onUpload,
7652
7616
  C
7653
7617
  }
@@ -7716,11 +7680,43 @@ var ReactEmailEditor = (0, import_react8.forwardRef)(
7716
7680
  e.dataTransfer.setData("application/json", JSON.stringify({ contentType: bt.type }));
7717
7681
  },
7718
7682
  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 });
7683
+ if (!rows.length) return;
7684
+ if (selContentMeta?.inner) {
7685
+ const { rowId, cellIdx, contentIdx, inner } = selContentMeta;
7686
+ dropContent(rowId, cellIdx, {
7687
+ kind: "new",
7688
+ contentType: bt.type,
7689
+ insertAt: null,
7690
+ nested: { parentBlockIdx: contentIdx, innerCellIdx: inner.cellIdx }
7691
+ });
7692
+ return;
7693
+ }
7694
+ if (selContentMeta?.rowId && typeof selContentMeta.cellIdx === "number" && selContentMeta.cellIdx >= 0) {
7695
+ const r = rows.find((x) => x.id === selContentMeta.rowId);
7696
+ if (r && selContentMeta.cellIdx < r.cells.length) {
7697
+ dropContent(selContentMeta.rowId, selContentMeta.cellIdx, {
7698
+ kind: "new",
7699
+ contentType: bt.type,
7700
+ insertAt: null
7701
+ });
7702
+ return;
7703
+ }
7704
+ }
7705
+ if (selectedRowId) {
7706
+ const targetRow = rows.find((r) => r.id === selectedRowId);
7707
+ if (targetRow) {
7708
+ dropContent(targetRow.id, 0, {
7709
+ kind: "new",
7710
+ contentType: bt.type,
7711
+ insertAt: null
7712
+ });
7713
+ return;
7714
+ }
7715
+ }
7716
+ const main = rows[0];
7717
+ dropContent(main.id, 0, { kind: "new", contentType: bt.type, insertAt: null });
7722
7718
  },
7723
- title: `Drag into a cell or click to add to ${selectedRowId ? "selected row" : "last row"}`,
7719
+ 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
7720
  style: {
7725
7721
  display: "flex",
7726
7722
  flexDirection: "row",