@emabuild/core 0.2.0 → 0.4.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.
Files changed (64) hide show
  1. package/dist/canvas/column-renderer.d.ts.map +1 -1
  2. package/dist/canvas/content-renderer.d.ts +1 -1
  3. package/dist/canvas/content-renderer.d.ts.map +1 -1
  4. package/dist/canvas/editor-canvas.d.ts +13 -0
  5. package/dist/canvas/editor-canvas.d.ts.map +1 -1
  6. package/dist/canvas/inline-toolbar.d.ts +8 -0
  7. package/dist/canvas/inline-toolbar.d.ts.map +1 -1
  8. package/dist/canvas/row-renderer.d.ts +2 -0
  9. package/dist/canvas/row-renderer.d.ts.map +1 -1
  10. package/dist/compat/unlayer-adapter.d.ts +33 -0
  11. package/dist/compat/unlayer-adapter.d.ts.map +1 -0
  12. package/dist/dnd/drag-manager.d.ts +1 -0
  13. package/dist/dnd/drag-manager.d.ts.map +1 -1
  14. package/dist/dnd/drag-state.d.ts +10 -3
  15. package/dist/dnd/drag-state.d.ts.map +1 -1
  16. package/dist/{form-tool-DdFDrS3b.js → form-tool-DsPgMShR.js} +10 -9
  17. package/dist/{form-tool-DdFDrS3b.js.map → form-tool-DsPgMShR.js.map} +1 -1
  18. package/dist/{html-tool-DMtmrF3n.js → html-tool-BxhBGl4L.js} +2 -2
  19. package/dist/{html-tool-DMtmrF3n.js.map → html-tool-BxhBGl4L.js.map} +1 -1
  20. package/dist/index-2S5kBS5_.js +4436 -0
  21. package/dist/index-2S5kBS5_.js.map +1 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -5
  25. package/dist/mail-editor.d.ts +13 -2
  26. package/dist/mail-editor.d.ts.map +1 -1
  27. package/dist/{menu-tool-CJcQdpcP.js → menu-tool-IZqYp8Vb.js} +14 -13
  28. package/dist/{menu-tool-CJcQdpcP.js.map → menu-tool-IZqYp8Vb.js.map} +1 -1
  29. package/dist/properties/property-panel.d.ts.map +1 -1
  30. package/dist/properties/widgets/image-upload-widget.d.ts +4 -0
  31. package/dist/properties/widgets/image-upload-widget.d.ts.map +1 -0
  32. package/dist/properties/widgets/index.d.ts +1 -0
  33. package/dist/properties/widgets/index.d.ts.map +1 -1
  34. package/dist/register-elements.d.ts.map +1 -1
  35. package/dist/sidebar/a11y-checker.d.ts +27 -0
  36. package/dist/sidebar/a11y-checker.d.ts.map +1 -0
  37. package/dist/sidebar/body-settings.d.ts +2 -0
  38. package/dist/sidebar/body-settings.d.ts.map +1 -1
  39. package/dist/sidebar/editor-sidebar.d.ts +5 -2
  40. package/dist/sidebar/editor-sidebar.d.ts.map +1 -1
  41. package/dist/{social-tool-CRY3-_sU.js → social-tool-C1FeCyUm.js} +13 -12
  42. package/dist/{social-tool-CRY3-_sU.js.map → social-tool-C1FeCyUm.js.map} +1 -1
  43. package/dist/state/editor-store.d.ts +35 -28
  44. package/dist/state/editor-store.d.ts.map +1 -1
  45. package/dist/{table-tool-DeIQci5z.js → table-tool-BD72-Fuj.js} +13 -12
  46. package/dist/{table-tool-DeIQci5z.js.map → table-tool-BD72-Fuj.js.map} +1 -1
  47. package/dist/timer-tool-Dpw9p0uW.js +55 -0
  48. package/dist/timer-tool-Dpw9p0uW.js.map +1 -0
  49. package/dist/tools/built-in/button-tool.d.ts.map +1 -1
  50. package/dist/tools/built-in/divider-tool.d.ts.map +1 -1
  51. package/dist/tools/built-in/heading-tool.d.ts.map +1 -1
  52. package/dist/tools/built-in/image-tool.d.ts.map +1 -1
  53. package/dist/tools/built-in/paragraph-tool.d.ts.map +1 -1
  54. package/dist/tools/built-in/shared-options.d.ts +105 -0
  55. package/dist/tools/built-in/shared-options.d.ts.map +1 -0
  56. package/dist/tools/built-in/text-tool.d.ts.map +1 -1
  57. package/dist/tools/tool-registry.d.ts.map +1 -1
  58. package/dist/{video-tool-g1fIoCWW.js → video-tool-CHhPfHaS.js} +7 -6
  59. package/dist/{video-tool-g1fIoCWW.js.map → video-tool-CHhPfHaS.js.map} +1 -1
  60. package/package.json +4 -4
  61. package/dist/index-CpMbWdgn.js +0 -3092
  62. package/dist/index-CpMbWdgn.js.map +0 -1
  63. package/dist/timer-tool-BVE1shO1.js +0 -54
  64. package/dist/timer-tool-BVE1shO1.js.map +0 -1
@@ -0,0 +1,4436 @@
1
+ import { css as _, LitElement as T, nothing as x, html as d } from "lit";
2
+ import { property as v, customElement as S, state as E } from "lit/decorators.js";
3
+ import { unsafeHTML as ie } from "lit/directives/unsafe-html.js";
4
+ import { styleMap as He } from "lit/directives/style-map.js";
5
+ import { repeat as ye } from "lit/directives/repeat.js";
6
+ class B {
7
+ /**
8
+ * @param host - The Lit component that owns this controller
9
+ * @param channels - Which store channels to subscribe to
10
+ */
11
+ constructor(t, o) {
12
+ this.store = null, this.host = t, this.channels = o, t.addController(this);
13
+ }
14
+ /** Set or change the store reference. Re-subscribes automatically. */
15
+ setStore(t) {
16
+ this.store !== t && (this.unsub?.(), this.store = t, this.subscribe());
17
+ }
18
+ hostConnected() {
19
+ this.subscribe();
20
+ }
21
+ hostDisconnected() {
22
+ this.unsub?.(), this.unsub = void 0;
23
+ }
24
+ subscribe() {
25
+ this.store && (this.unsub = this.store.subscribeChannels(this.channels, () => {
26
+ this.host.requestUpdate();
27
+ }));
28
+ }
29
+ }
30
+ var Ve = Object.defineProperty, Fe = Object.getOwnPropertyDescriptor, xe = (e, t, o, r) => {
31
+ for (var i = r > 1 ? void 0 : r ? Fe(t, o) : t, n = e.length - 1, s; n >= 0; n--)
32
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
33
+ return r && i && Ve(t, o, i), i;
34
+ };
35
+ function ge(e) {
36
+ if (!e || e === "transparent" || e === "" || e === "none") return null;
37
+ const t = e.trim().toLowerCase(), o = {
38
+ white: "#ffffff",
39
+ black: "#000000",
40
+ red: "#ff0000",
41
+ blue: "#0000ff",
42
+ green: "#008000",
43
+ yellow: "#ffff00",
44
+ gray: "#808080",
45
+ grey: "#808080"
46
+ };
47
+ if (o[t]) return ge(o[t]);
48
+ if (t.startsWith("#")) {
49
+ let i = t.slice(1);
50
+ if (i.length === 3 && (i = i[0] + i[0] + i[1] + i[1] + i[2] + i[2]), i.length !== 6) return null;
51
+ const n = parseInt(i, 16);
52
+ return isNaN(n) ? null : { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
53
+ }
54
+ const r = t.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
55
+ return r ? { r: +r[1], g: +r[2], b: +r[3] } : null;
56
+ }
57
+ function $e(e, t, o) {
58
+ const [r, i, n] = [e, t, o].map((s) => {
59
+ const a = s / 255;
60
+ return a <= 0.03928 ? a / 12.92 : Math.pow((a + 0.055) / 1.055, 2.4);
61
+ });
62
+ return 0.2126 * r + 0.7152 * i + 0.0722 * n;
63
+ }
64
+ function V(e, t) {
65
+ const o = ge(e), r = ge(t);
66
+ if (!o || !r) return null;
67
+ const i = $e(o.r, o.g, o.b), n = $e(r.r, r.g, r.b);
68
+ return (Math.max(i, n) + 0.05) / (Math.min(i, n) + 0.05);
69
+ }
70
+ function ze(e) {
71
+ if (typeof e != "string") return !1;
72
+ const t = e.trim().toLowerCase();
73
+ return t !== "" && t !== "transparent" && t !== "none" && t !== "inherit";
74
+ }
75
+ function Ce(...e) {
76
+ for (const t of e)
77
+ if (ze(t)) return t;
78
+ return "#ffffff";
79
+ }
80
+ function te(e, t = 0) {
81
+ return typeof e == "number" ? e : typeof e == "string" && parseInt(e, 10) || t;
82
+ }
83
+ function Ue(e) {
84
+ if (!e || e === "") return null;
85
+ const t = String(e);
86
+ if (t.endsWith("%")) return parseFloat(t) / 100;
87
+ const o = parseFloat(t);
88
+ return isNaN(o) ? null : o > 5 ? o / 100 : o;
89
+ }
90
+ let U = class extends T {
91
+ constructor() {
92
+ super(...arguments), this.storeCtrl = new B(this, ["design"]);
93
+ }
94
+ set store(e) {
95
+ this.storeCtrl.setStore(e);
96
+ }
97
+ get store() {
98
+ return this.storeCtrl.store;
99
+ }
100
+ checkAccessibility() {
101
+ return ve(this.store);
102
+ }
103
+ handleIssueClick(e) {
104
+ e && this.store.select(e);
105
+ }
106
+ handleIssueHover(e) {
107
+ this.store.hover(e ?? null);
108
+ }
109
+ handleFixPreheader(e) {
110
+ e.stopPropagation(), this.store.setActiveTab("body"), requestAnimationFrame(() => {
111
+ setTimeout(() => {
112
+ document.querySelector("mail-editor")?.shadowRoot?.querySelector("me-editor-sidebar")?.shadowRoot?.querySelector("me-body-settings")?.highlightPreheader?.();
113
+ }, 100);
114
+ });
115
+ }
116
+ handleFixHeading(e) {
117
+ e.stopPropagation();
118
+ const t = this.store.createRow([1]), r = this.store.addRow(t, 0).columns[0].id, i = this.toolRegistry.getDefaultValues("heading"), n = this.store.createContent("heading", i);
119
+ this.store.addContent(r, n), this.store.select(n.id);
120
+ }
121
+ render() {
122
+ const e = this.checkAccessibility(), t = e.filter((i) => i.severity === "error"), o = e.filter((i) => i.severity === "warning"), r = e.filter((i) => i.severity === "info");
123
+ return d`
124
+ <div class="summary">
125
+ ${t.length > 0 ? d`<span class="score-pill errors"><span class="score-dot error"></span>${t.length}</span>` : x}
126
+ ${o.length > 0 ? d`<span class="score-pill warnings"><span class="score-dot warning"></span>${o.length}</span>` : x}
127
+ ${r.length > 0 ? d`<span class="score-pill infos"><span class="score-dot info"></span>${r.length}</span>` : x}
128
+ ${t.length === 0 && o.length === 0 ? d`<span class="score-pill pass"><span class="score-dot pass"></span>Pass</span>` : x}
129
+ </div>
130
+
131
+ ${e.length === 0 ? d`
132
+ <div class="all-clear">
133
+ <div class="all-clear-icon">✓</div>
134
+ <div class="all-clear-text">Accessible</div>
135
+ <div class="all-clear-sub">No issues detected</div>
136
+ </div>
137
+ ` : d`
138
+ ${t.length > 0 ? d`
139
+ <div class="section-title">Errors</div>
140
+ <div class="issues-list">${t.map((i) => this.renderIssue(i))}</div>
141
+ ` : x}
142
+ ${o.length > 0 ? d`
143
+ <div class="section-title">Warnings</div>
144
+ <div class="issues-list">${o.map((i) => this.renderIssue(i))}</div>
145
+ ` : x}
146
+ ${r.length > 0 ? d`
147
+ <div class="section-title">Info</div>
148
+ <div class="issues-list">${r.map((i) => this.renderIssue(i))}</div>
149
+ ` : x}
150
+ `}
151
+ `;
152
+ }
153
+ renderIssue(e) {
154
+ return d`
155
+ <div class="issue ${e.severity}"
156
+ @click=${() => this.handleIssueClick(e.elementId)}
157
+ @mouseenter=${() => this.handleIssueHover(e.elementId)}
158
+ @mouseleave=${() => this.handleIssueHover(void 0)}>
159
+ <div class="issue-icon ${e.severity}">
160
+ ${e.severity === "error" ? "✕" : e.severity === "warning" ? "!" : "i"}
161
+ </div>
162
+ <div class="issue-body">
163
+ <div class="issue-rule">${e.rule}</div>
164
+ <div class="issue-msg">${e.message}</div>
165
+ ${e.element ? d`<div class="issue-element">${e.element}</div>` : x}
166
+ ${e.rule === "heading-structure" ? d`<button class="issue-fix" @click=${this.handleFixHeading}>+ Add heading</button>` : x}
167
+ ${e.rule === "preheader" ? d`<button class="issue-fix" @click=${this.handleFixPreheader}>Fix</button>` : x}
168
+ </div>
169
+ </div>
170
+ `;
171
+ }
172
+ };
173
+ U.styles = _`
174
+ :host {
175
+ display: flex;
176
+ flex-direction: column;
177
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
178
+ }
179
+
180
+ .summary {
181
+ display: flex;
182
+ flex-wrap: wrap;
183
+ gap: 6px;
184
+ margin-bottom: 12px;
185
+ }
186
+ .score-pill {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 4px;
190
+ padding: 3px 8px;
191
+ border-radius: 20px;
192
+ font-size: 11px;
193
+ font-weight: 600;
194
+ }
195
+ .score-pill.errors { background: #fef2f2; color: #dc2626; border: 1px solid #fecaca; }
196
+ .score-pill.warnings { background: #fffbeb; color: #d97706; border: 1px solid #fde68a; }
197
+ .score-pill.infos { background: #EDF5FF; color: #245A7F; border: 1px solid #bfdbfe; }
198
+ .score-pill.pass { background: #f0fdf4; color: #16a34a; border: 1px solid #bbf7d0; }
199
+ .score-dot { width: 6px; height: 6px; border-radius: 50%; }
200
+ .score-dot.error { background: #dc2626; }
201
+ .score-dot.warning { background: #d97706; }
202
+ .score-dot.info { background: #245A7F; }
203
+ .score-dot.pass { background: #16a34a; }
204
+
205
+ .section-title {
206
+ font-size: 10px; font-weight: 700; text-transform: uppercase;
207
+ letter-spacing: 0.06em; color: #245A7F;
208
+ padding: 6px 0 4px; margin-top: 8px;
209
+ border-bottom: 1px solid #EDF5FF;
210
+ }
211
+ .section-title:first-of-type { margin-top: 0; }
212
+
213
+ .issues-list {
214
+ display: flex;
215
+ flex-direction: column;
216
+ gap: 3px;
217
+ margin-top: 6px;
218
+ }
219
+ .issue {
220
+ display: flex;
221
+ gap: 8px;
222
+ padding: 6px 8px;
223
+ border-radius: 6px;
224
+ border: 1px solid #f3f4f6;
225
+ transition: all 150ms ease;
226
+ cursor: pointer;
227
+ }
228
+ .issue:hover { background: #f9fafb; border-color: #e5e7eb; }
229
+ .issue.error { border-left: 3px solid #dc2626; }
230
+ .issue.warning { border-left: 3px solid #d97706; }
231
+ .issue.info { border-left: 3px solid #5d768b; }
232
+ .issue-icon {
233
+ flex-shrink: 0;
234
+ width: 16px; height: 16px;
235
+ display: flex; align-items: center; justify-content: center;
236
+ font-size: 10px; font-weight: 700;
237
+ border-radius: 50%;
238
+ margin-top: 1px;
239
+ }
240
+ .issue-icon.error { background: #fef2f2; color: #dc2626; }
241
+ .issue-icon.warning { background: #fffbeb; color: #d97706; }
242
+ .issue-icon.info { background: #EDF5FF; color: #245A7F; }
243
+ .issue-body { flex: 1; min-width: 0; }
244
+ .issue-rule {
245
+ font-size: 9px; font-weight: 600; text-transform: uppercase;
246
+ letter-spacing: 0.04em; color: #9ca3af;
247
+ }
248
+ .issue-msg { font-size: 11px; color: #374151; line-height: 1.4; margin-top: 1px; }
249
+ .issue-element {
250
+ font-size: 10px; color: #5d768b; margin-top: 2px;
251
+ cursor: pointer;
252
+ }
253
+ .issue-element:hover { text-decoration: underline; }
254
+ .issue-fix {
255
+ display: inline-flex; align-items: center; gap: 3px;
256
+ margin-top: 4px; padding: 2px 8px;
257
+ background: #f0fdf4; color: #16a34a; border: 1px solid #bbf7d0;
258
+ border-radius: 4px; font-size: 10px; font-weight: 600;
259
+ cursor: pointer; transition: all 150ms ease;
260
+ }
261
+ .issue-fix:hover { background: #dcfce7; border-color: #86efac; }
262
+
263
+ .all-clear {
264
+ text-align: center;
265
+ padding: 24px 16px;
266
+ }
267
+ .all-clear-icon {
268
+ width: 36px; height: 36px; border-radius: 50%;
269
+ background: #f0fdf4; margin: 0 auto 8px;
270
+ display: flex; align-items: center; justify-content: center;
271
+ font-size: 18px; color: #16a34a;
272
+ }
273
+ .all-clear-text { font-size: 12px; font-weight: 600; color: #16a34a; }
274
+ .all-clear-sub { font-size: 11px; color: #9ca3af; margin-top: 4px; }
275
+ `;
276
+ xe([
277
+ v({ attribute: !1 })
278
+ ], U.prototype, "store", 1);
279
+ xe([
280
+ v({ attribute: !1 })
281
+ ], U.prototype, "toolRegistry", 2);
282
+ U = xe([
283
+ S("me-a11y-checker")
284
+ ], U);
285
+ function ve(e) {
286
+ const t = [], o = e.getDesign(), r = e.getBodyValues(), i = r.backgroundColor || "#ffffff", n = r.textColor || "#000000";
287
+ let s = !1;
288
+ const a = [];
289
+ for (const h of o.body.rows) {
290
+ const g = h.values.backgroundColor || "", b = h.values.columnsBackgroundColor || "";
291
+ for (const k of h.columns) {
292
+ const $ = Ce(k.values.backgroundColor, b, g, i);
293
+ for (const m of k.contents) {
294
+ const f = m.values, y = m.type, w = `${y} #${m.id.split("_").pop()}`;
295
+ if (y === "image") {
296
+ const C = f.alt || "";
297
+ (f.src || "") && !C.trim() ? t.push({ severity: "error", rule: "img-alt", message: "Missing alt text.", element: w, elementId: m.id }) : C && C.length > 125 && t.push({ severity: "warning", rule: "img-alt-length", message: `Alt text ${C.length} chars (max 125).`, element: w, elementId: m.id });
298
+ }
299
+ if (y === "text" || y === "heading" || y === "paragraph") {
300
+ const C = ze(f.color) ? f.color : n, A = Ce(f.backgroundColor, $), I = V(C, A);
301
+ if (I !== null) {
302
+ const Q = te(f.fontSize, 14), Oe = ["700", "800", "900"].includes(f.fontWeight || ""), ke = Q >= 18 || Q >= 14 && Oe ? 3 : 4.5;
303
+ I < ke && t.push({ severity: I < 3 ? "error" : "warning", rule: "color-contrast", message: `Ratio ${I.toFixed(1)}:1 (min ${ke}:1). "${C}" on "${A}".`, element: w, elementId: m.id });
304
+ }
305
+ const W = te(f.fontSize, 14);
306
+ y !== "heading" && W > 0 && W < 12 && t.push({ severity: "warning", rule: "font-size", message: `Font size ${W}px below 12px minimum.`, element: w, elementId: m.id });
307
+ const ee = Ue(f.lineHeight);
308
+ ee !== null && ee < 1.2 && y !== "heading" && t.push({ severity: "warning", rule: "line-height", message: `Line height ${String(f.lineHeight)} too tight (min 1.5x).`, element: w, elementId: m.id }), f.textAlign === "justify" && t.push({ severity: "warning", rule: "text-justify", message: "Justified text reduces readability.", element: w, elementId: m.id });
309
+ }
310
+ if (y === "button") {
311
+ const C = f.backgroundColor || "#3b82f6", A = f.textColor || "#ffffff", I = V(A, C);
312
+ I !== null && I < 4.5 && t.push({ severity: I < 3 ? "error" : "warning", rule: "color-contrast", message: `Button contrast ${I.toFixed(1)}:1 (min 4.5:1).`, element: w, elementId: m.id });
313
+ const W = V(C, $);
314
+ W !== null && W < 3 && t.push({ severity: "warning", rule: "btn-visibility", message: `Button blends with background (${W.toFixed(1)}:1).`, element: w, elementId: m.id }), (f.text || "").trim() || t.push({ severity: "error", rule: "link-text", message: "Button has no visible text.", element: w, elementId: m.id });
315
+ const ee = (f.buttonPadding || "10px 20px").split(/\s+/), Q = te(f.fontSize, 14) + te(ee[0], 10) * 2;
316
+ Q < 44 && t.push({ severity: "info", rule: "touch-target", message: `Button height ~${Q}px (min 44px).`, element: w, elementId: m.id });
317
+ }
318
+ if (y === "heading" && (s = !0, a.push(f.headingType || "h1")), y === "text" || y === "paragraph") {
319
+ const C = f.text || "", A = C.match(/<a[^>]*>(\s|&nbsp;)*<\/a>/gi);
320
+ A && t.push({ severity: "error", rule: "link-text", message: `${A.length} empty link(s).`, element: w, elementId: m.id });
321
+ const I = C.match(/<a[^>]*>(click here|here|link|read more)<\/a>/gi);
322
+ I && t.push({ severity: "warning", rule: "link-purpose", message: `${I.length} generic link text(s).`, element: w, elementId: m.id });
323
+ }
324
+ y === "social" && t.push({ severity: "info", rule: "social-alt", message: "Verify social icons have alt text.", element: w, elementId: m.id });
325
+ }
326
+ }
327
+ }
328
+ const c = o.body.rows.reduce((h, g) => h + g.columns.reduce((b, k) => b + k.contents.length, 0), 0);
329
+ if (c > 0 && !s && t.push({ severity: "warning", rule: "heading-structure", message: "No heading in email." }), a.length > 1) {
330
+ const h = a.map((g) => parseInt(g.replace("h", ""), 10)).filter((g) => !isNaN(g));
331
+ for (let g = 1; g < h.length; g++)
332
+ if (h[g] > h[g - 1] + 1) {
333
+ t.push({ severity: "warning", rule: "heading-skip", message: `Heading jumps h${h[g - 1]} → h${h[g]}.` });
334
+ break;
335
+ }
336
+ }
337
+ const l = V(n, i);
338
+ l !== null && l < 4.5 && t.push({ severity: "warning", rule: "color-contrast", message: `Body text contrast ${l.toFixed(1)}:1 (min 4.5:1).` });
339
+ const u = r.linkStyle?.linkColor;
340
+ if (u) {
341
+ const h = V(u, i);
342
+ h !== null && h < 4.5 && t.push({ severity: "warning", rule: "link-contrast", message: `Link contrast ${h.toFixed(1)}:1 (min 4.5:1).` });
343
+ const g = V(u, n);
344
+ g !== null && g < 3 && !r.linkStyle?.linkUnderline && t.push({ severity: "warning", rule: "link-distinguish", message: "Links not underlined and low contrast vs text." });
345
+ }
346
+ return (r.preheaderText || "").trim() || t.push({ severity: "info", rule: "preheader", message: "No preheader text set." }), c === 0 && t.push({ severity: "info", rule: "empty-email", message: "Email has no content." }), t.sort((h, g) => ({ error: 0, warning: 1, info: 2 })[h.severity] - { error: 0, warning: 1, info: 2 }[g.severity]), t;
347
+ }
348
+ function qe() {
349
+ const e = {};
350
+ return {
351
+ getCounters() {
352
+ return { ...e };
353
+ },
354
+ setCounters(t) {
355
+ for (const o of Object.keys(e))
356
+ delete e[o];
357
+ Object.assign(e, t);
358
+ },
359
+ next(t) {
360
+ const o = e[t] ?? 0;
361
+ return e[t] = o + 1, e[t];
362
+ }
363
+ };
364
+ }
365
+ class Ne {
366
+ constructor() {
367
+ this.listeners = /* @__PURE__ */ new Map();
368
+ }
369
+ /** Register a listener for an event */
370
+ on(t, o) {
371
+ this.listeners.has(t) || this.listeners.set(t, /* @__PURE__ */ new Set()), this.listeners.get(t).add(o);
372
+ }
373
+ /** Remove a specific listener */
374
+ off(t, o) {
375
+ this.listeners.get(t)?.delete(o);
376
+ }
377
+ /** Emit an event with a payload. Errors in listeners are caught and logged. */
378
+ emit(t, o) {
379
+ this.listeners.get(t)?.forEach((r) => {
380
+ try {
381
+ r(o);
382
+ } catch (i) {
383
+ console.error(`[emabuild] Error in "${t}" listener:`, i);
384
+ }
385
+ });
386
+ }
387
+ /** Remove all listeners, optionally scoped to a single event */
388
+ removeAllListeners(t) {
389
+ t ? this.listeners.delete(t) : this.listeners.clear();
390
+ }
391
+ }
392
+ class Ge {
393
+ constructor(t = 50) {
394
+ this.undoStack = [], this.redoStack = [], this.maxHistory = t;
395
+ }
396
+ /** Whether there are states to undo to */
397
+ get canUndo() {
398
+ return this.undoStack.length > 0;
399
+ }
400
+ /** Whether there are states to redo to */
401
+ get canRedo() {
402
+ return this.redoStack.length > 0;
403
+ }
404
+ /** Save current design to the undo stack before a mutation */
405
+ push(t) {
406
+ this.undoStack.push(structuredClone(t)), this.undoStack.length > this.maxHistory && this.undoStack.shift(), this.redoStack = [];
407
+ }
408
+ /** Restore the previous state, pushing current state to redo. Returns the restored design or undefined. */
409
+ undo(t) {
410
+ const o = this.undoStack.pop();
411
+ if (o)
412
+ return this.redoStack.push(structuredClone(t)), structuredClone(o);
413
+ }
414
+ /** Restore the next state, pushing current state to undo. Returns the restored design or undefined. */
415
+ redo(t) {
416
+ const o = this.redoStack.pop();
417
+ if (o)
418
+ return this.undoStack.push(structuredClone(t)), structuredClone(o);
419
+ }
420
+ /** Clear all history (e.g. when loading a new design) */
421
+ clear() {
422
+ this.undoStack = [], this.redoStack = [];
423
+ }
424
+ }
425
+ function Ye() {
426
+ return {
427
+ counters: { u_row: 0, u_column: 0 },
428
+ body: {
429
+ id: "u_body",
430
+ rows: [],
431
+ headers: [],
432
+ footers: [],
433
+ values: {
434
+ backgroundColor: "#e7e7e7",
435
+ contentAlign: "center",
436
+ contentVerticalAlign: "center",
437
+ contentWidth: "600px",
438
+ fontFamily: { label: "Arial", value: "arial,helvetica,sans-serif" },
439
+ textColor: "#000000",
440
+ linkStyle: {
441
+ body: !0,
442
+ linkColor: "#0000ee",
443
+ linkHoverColor: "#0000ee",
444
+ linkUnderline: !0,
445
+ linkHoverUnderline: !0
446
+ },
447
+ preheaderText: "",
448
+ popupPosition: "center",
449
+ popupWidth: "600px",
450
+ popupHeight: "auto",
451
+ borderRadius: "10px",
452
+ popupBackgroundColor: "#FFFFFF",
453
+ popupBackgroundImage: { url: "", fullWidth: !0, repeat: "no-repeat", center: !0, cover: !0 },
454
+ popupOverlay_backgroundColor: "rgba(0, 0, 0, 0.1)",
455
+ popupCloseButton_position: "top-right",
456
+ popupCloseButton_backgroundColor: "#DDDDDD",
457
+ popupCloseButton_iconColor: "#000000",
458
+ popupCloseButton_borderRadius: "0px",
459
+ popupCloseButton_margin: "0px",
460
+ popupCloseButton_action: {
461
+ name: "close_popup",
462
+ attrs: { onClick: "document.querySelector('.u-popup-container').style.display = 'none';" }
463
+ },
464
+ _meta: { htmlID: "u_body", htmlClassNames: "u_body" }
465
+ }
466
+ },
467
+ schemaVersion: 16
468
+ };
469
+ }
470
+ function Qe(e, t) {
471
+ const o = e.next("u_row"), r = t.map(() => {
472
+ const i = e.next("u_column");
473
+ return {
474
+ id: `u_column_${i}`,
475
+ contents: [],
476
+ values: {
477
+ backgroundColor: "",
478
+ padding: "0px",
479
+ border: {},
480
+ borderRadius: "0px",
481
+ _meta: { htmlID: `u_column_${i}`, htmlClassNames: "u_column" }
482
+ }
483
+ };
484
+ });
485
+ return {
486
+ id: `u_row_${o}`,
487
+ cells: t,
488
+ columns: r,
489
+ values: {
490
+ displayCondition: null,
491
+ columns: !1,
492
+ backgroundColor: "",
493
+ columnsBackgroundColor: "",
494
+ backgroundImage: { url: "", fullWidth: !0, repeat: !1, center: !0, cover: !1 },
495
+ padding: "0px",
496
+ anchor: "",
497
+ hideDesktop: !1,
498
+ hideMobile: !1,
499
+ _meta: { htmlID: `u_row_${o}`, htmlClassNames: "u_row" }
500
+ }
501
+ };
502
+ }
503
+ function Ke(e, t, o = {}) {
504
+ const r = e.next(`u_content_${t}`), i = `u_content_${t}_${r}`;
505
+ return {
506
+ id: i,
507
+ type: t,
508
+ values: {
509
+ containerPadding: "10px",
510
+ anchor: "",
511
+ hideDesktop: !1,
512
+ hideMobile: !1,
513
+ displayCondition: null,
514
+ _meta: { htmlID: i, htmlClassNames: `u_content_${t}` },
515
+ ...o
516
+ }
517
+ };
518
+ }
519
+ function de(e, t) {
520
+ return e.body.rows.find((o) => o.id === t);
521
+ }
522
+ function oe(e, t) {
523
+ for (const o of e.body.rows) {
524
+ const r = o.columns.find((i) => i.id === t);
525
+ if (r) return r;
526
+ }
527
+ }
528
+ function re(e, t) {
529
+ for (const o of e.body.rows)
530
+ for (const r of o.columns) {
531
+ const i = r.contents.find((n) => n.id === t);
532
+ if (i) return i;
533
+ }
534
+ }
535
+ function Je(e, t) {
536
+ for (const o of e.body.rows)
537
+ for (const r of o.columns)
538
+ if (r.contents.some((i) => i.id === t)) return r;
539
+ }
540
+ function Xe(e, t) {
541
+ for (const o of e.body.rows)
542
+ if (o.columns.some((r) => r.id === t)) return o;
543
+ }
544
+ function ce(e, t) {
545
+ return e.body.rows.findIndex((o) => o.id === t);
546
+ }
547
+ class Ze {
548
+ constructor() {
549
+ this.history = new Ge(), this.counterManager = qe(), this.channelSubscribers = /* @__PURE__ */ new Map(), this.events = new Ne(), this._mergeTags = [], this._callbacks = /* @__PURE__ */ new Map(), this._a11yIssueIds = /* @__PURE__ */ new Set(), this._selectedId = null, this._hoveredId = null, this._viewMode = "desktop", this._activeTab = "content", this.pendingChannels = /* @__PURE__ */ new Set(), this.rafId = null, this.design = Ye();
550
+ }
551
+ setCallback(t, o) {
552
+ this._callbacks.set(t, o);
553
+ }
554
+ getCallback(t) {
555
+ return this._callbacks.get(t);
556
+ }
557
+ get a11yIssueIds() {
558
+ return this._a11yIssueIds;
559
+ }
560
+ setA11yIssueIds(t) {
561
+ this._a11yIssueIds = t;
562
+ }
563
+ // ── Subscriptions ──────────────────────────────────────────
564
+ /**
565
+ * Subscribe to specific channels only. The callback is invoked only
566
+ * when one of the listed channels fires. Returns an unsubscribe function.
567
+ *
568
+ * @example
569
+ * ```ts
570
+ * // Only re-render when the design changes, not on hover/selection
571
+ * store.subscribeChannels(['design'], () => this.requestUpdate());
572
+ * ```
573
+ */
574
+ subscribeChannels(t, o) {
575
+ for (const r of t) {
576
+ let i = this.channelSubscribers.get(r);
577
+ i || (i = /* @__PURE__ */ new Set(), this.channelSubscribers.set(r, i)), i.add(o);
578
+ }
579
+ return () => {
580
+ for (const r of t) {
581
+ const i = this.channelSubscribers.get(r);
582
+ i && (i.delete(o), i.size === 0 && this.channelSubscribers.delete(r));
583
+ }
584
+ };
585
+ }
586
+ /** @deprecated Use subscribeChannels instead */
587
+ subscribe(t) {
588
+ const o = ["design", "selection", "hover", "viewMode", "activeTab"];
589
+ return this.subscribeChannels(o, t);
590
+ }
591
+ notifyChannels(...t) {
592
+ for (const o of t) this.pendingChannels.add(o);
593
+ this.rafId === null && (this.rafId = requestAnimationFrame(() => {
594
+ this.rafId = null;
595
+ const o = /* @__PURE__ */ new Set();
596
+ for (const r of this.pendingChannels) {
597
+ const i = this.channelSubscribers.get(r);
598
+ if (i) for (const n of i) o.add(n);
599
+ }
600
+ this.pendingChannels.clear();
601
+ for (const r of o) r();
602
+ }));
603
+ }
604
+ // ── Getters ────────────────────────────────────────────────
605
+ /** Get the full design document */
606
+ getDesign() {
607
+ return this.design;
608
+ }
609
+ /** Get the design body */
610
+ getBody() {
611
+ return this.design.body;
612
+ }
613
+ /** Get all rows */
614
+ getRows() {
615
+ return this.design.body.rows;
616
+ }
617
+ /** Get body-level values (background, fonts, etc.) */
618
+ getBodyValues() {
619
+ return this.design.body.values;
620
+ }
621
+ get selectedId() {
622
+ return this._selectedId;
623
+ }
624
+ get hoveredId() {
625
+ return this._hoveredId;
626
+ }
627
+ get viewMode() {
628
+ return this._viewMode;
629
+ }
630
+ get activeTab() {
631
+ return this._activeTab;
632
+ }
633
+ get mergeTags() {
634
+ return this._mergeTags;
635
+ }
636
+ get canUndo() {
637
+ return this.history.canUndo;
638
+ }
639
+ get canRedo() {
640
+ return this.history.canRedo;
641
+ }
642
+ // ── Design Loading ─────────────────────────────────────────
643
+ /** Load a design document, resetting history and selection */
644
+ loadDesign(t) {
645
+ this.design = structuredClone(t), this.counterManager.setCounters(this.design.counters), this.history.clear(), this._selectedId = null, this.notifyChannels("design", "selection"), this.events.emit("design:loaded", { design: this.design });
646
+ }
647
+ // ── Undo / Redo ────────────────────────────────────────────
648
+ undo() {
649
+ const t = this.history.undo(this.design);
650
+ t && (this.design = t, this.counterManager.setCounters(this.design.counters), this.notifyChannels("design"), this.emitUpdate("content_updated"));
651
+ }
652
+ redo() {
653
+ const t = this.history.redo(this.design);
654
+ t && (this.design = t, this.counterManager.setCounters(this.design.counters), this.notifyChannels("design"), this.emitUpdate("content_updated"));
655
+ }
656
+ // ── Selection / UI State ───────────────────────────────────
657
+ setMergeTags(t) {
658
+ this._mergeTags = t, this.notifyChannels("design");
659
+ }
660
+ select(t) {
661
+ this._selectedId = t, this.notifyChannels("selection");
662
+ }
663
+ hover(t) {
664
+ this._hoveredId = t;
665
+ const o = this.channelSubscribers.get("hover");
666
+ if (o) for (const r of o) r();
667
+ }
668
+ setViewMode(t) {
669
+ this._viewMode = t, this.notifyChannels("viewMode");
670
+ }
671
+ setActiveTab(t) {
672
+ this._activeTab = t, this.notifyChannels("activeTab");
673
+ }
674
+ // ── Row Operations ─────────────────────────────────────────
675
+ /** Add a row at the given index (or at the end). Returns the inserted row. */
676
+ addRow(t, o) {
677
+ const r = structuredClone(t);
678
+ this.history.push(this.design);
679
+ const i = this.design.body.rows;
680
+ return o !== void 0 && o >= 0 && o <= i.length ? i.splice(o, 0, r) : i.push(r), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("row_added", r), r;
681
+ }
682
+ /** Remove a row by ID. Returns true if removed. */
683
+ removeRow(t) {
684
+ const o = ce(this.design, t);
685
+ return o === -1 ? !1 : (this.history.push(this.design), this.design.body.rows.splice(o, 1), this._selectedId === t && (this._selectedId = null), this.notifyChannels("design"), this.emitUpdate("row_removed"), !0);
686
+ }
687
+ /** Move a row from one index to another. Returns true if moved. */
688
+ moveRow(t, o) {
689
+ const r = this.design.body.rows;
690
+ if (t < 0 || t >= r.length || o < 0 || o >= r.length || t === o) return !1;
691
+ this.history.push(this.design);
692
+ const [i] = r.splice(t, 1);
693
+ return r.splice(o, 0, i), this.notifyChannels("design"), this.emitUpdate("row_reordered"), !0;
694
+ }
695
+ /** Duplicate a row, assigning fresh IDs to all nested elements */
696
+ duplicateRow(t) {
697
+ const o = de(this.design, t);
698
+ if (!o) return;
699
+ this.history.push(this.design);
700
+ const r = structuredClone(o), i = this.counterManager.next("u_row");
701
+ r.id = `u_row_${i}`, r.values._meta = { htmlID: r.id, htmlClassNames: "u_row" };
702
+ for (const s of r.columns) {
703
+ const a = this.counterManager.next("u_column");
704
+ s.id = `u_column_${a}`, s.values._meta = { htmlID: s.id, htmlClassNames: "u_column" };
705
+ for (const c of s.contents) {
706
+ const l = this.counterManager.next(`u_content_${c.type}`);
707
+ c.id = `u_content_${c.type}_${l}`, c.values._meta = { htmlID: c.id, htmlClassNames: `u_content_${c.type}` };
708
+ }
709
+ }
710
+ const n = ce(this.design, t);
711
+ return this.design.body.rows.splice(n + 1, 0, r), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("row_added", r), r;
712
+ }
713
+ /** Get the index of a row */
714
+ getRowIndex(t) {
715
+ return ce(this.design, t);
716
+ }
717
+ /** Update row-level values. Returns true if updated. */
718
+ updateRowValues(t, o) {
719
+ const r = de(this.design, t);
720
+ return r ? (this.history.push(this.design), Object.assign(r.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"), !0) : !1;
721
+ }
722
+ // ── Column Operations ──────────────────────────────────────
723
+ /** Update column-level values. Returns true if updated. */
724
+ updateColumnValues(t, o) {
725
+ const r = oe(this.design, t);
726
+ return r ? (this.history.push(this.design), Object.assign(r.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"), !0) : !1;
727
+ }
728
+ // ── Content Operations ─────────────────────────────────────
729
+ /** Add content to a column at the given index. Returns the inserted content. */
730
+ addContent(t, o, r) {
731
+ const i = oe(this.design, t);
732
+ if (!i) return;
733
+ const n = structuredClone(o);
734
+ return this.history.push(this.design), r !== void 0 && r >= 0 && r <= i.contents.length ? i.contents.splice(r, 0, n) : i.contents.push(n), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("content_added", n), n;
735
+ }
736
+ /** Remove a content block by ID. Returns true if removed. */
737
+ removeContent(t) {
738
+ for (const o of this.design.body.rows)
739
+ for (const r of o.columns) {
740
+ const i = r.contents.findIndex((n) => n.id === t);
741
+ if (i !== -1)
742
+ return this.history.push(this.design), r.contents.splice(i, 1), this._selectedId === t && (this._selectedId = null), this.notifyChannels("design"), this.emitUpdate("content_removed"), !0;
743
+ }
744
+ return !1;
745
+ }
746
+ /** Update content values by ID. Returns true if updated. */
747
+ updateContentValues(t, o) {
748
+ const r = re(this.design, t);
749
+ return r ? (this.history.push(this.design), Object.assign(r.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"), !0) : !1;
750
+ }
751
+ /** Move a content block to a different column at a given index. Returns true if moved. */
752
+ moveContent(t, o, r) {
753
+ if (!re(this.design, t)) return !1;
754
+ const n = oe(this.design, o);
755
+ if (!n) return !1;
756
+ this.history.push(this.design);
757
+ let s;
758
+ e: for (const c of this.design.body.rows)
759
+ for (const l of c.columns) {
760
+ const u = l.contents.findIndex((h) => h.id === t);
761
+ if (u !== -1) {
762
+ [s] = l.contents.splice(u, 1);
763
+ break e;
764
+ }
765
+ }
766
+ if (!s) return !1;
767
+ const a = Math.min(r, n.contents.length);
768
+ return n.contents.splice(a, 0, s), this.notifyChannels("design"), this.emitUpdate("content_reordered"), !0;
769
+ }
770
+ /** Duplicate a content block, inserting the copy right after the original */
771
+ duplicateContent(t) {
772
+ const o = re(this.design, t);
773
+ if (o)
774
+ for (const r of this.design.body.rows)
775
+ for (const i of r.columns) {
776
+ const n = i.contents.findIndex((s) => s.id === t);
777
+ if (n !== -1) {
778
+ this.history.push(this.design);
779
+ const s = structuredClone(o), a = this.counterManager.next(`u_content_${o.type}`);
780
+ return s.id = `u_content_${o.type}_${a}`, s.values._meta = { htmlID: s.id, htmlClassNames: `u_content_${o.type}` }, i.contents.splice(n + 1, 0, s), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("content_added", s), s;
781
+ }
782
+ }
783
+ }
784
+ // ── Body Values ────────────────────────────────────────────
785
+ /** Update body-level values (background, fonts, etc.) */
786
+ updateBodyValues(t) {
787
+ this.history.push(this.design), Object.assign(this.design.body.values, t), this.notifyChannels("design"), this.emitUpdate("body_updated");
788
+ }
789
+ // ── Lookups (delegate to design-lookup) ────────────────────
790
+ findRow(t) {
791
+ return de(this.design, t);
792
+ }
793
+ findColumn(t) {
794
+ return oe(this.design, t);
795
+ }
796
+ findContent(t) {
797
+ return re(this.design, t);
798
+ }
799
+ findParentColumn(t) {
800
+ return Je(this.design, t);
801
+ }
802
+ findParentRow(t) {
803
+ return Xe(this.design, t);
804
+ }
805
+ // ── Factory Methods (delegate to design-factory) ───────────
806
+ /** Create a new row with the given column layout */
807
+ createRow(t) {
808
+ const o = Qe(this.counterManager, t);
809
+ return this.syncCounters(), o;
810
+ }
811
+ /** Create a new content block for the given tool type */
812
+ createContent(t, o = {}) {
813
+ const r = Ke(this.counterManager, t, o);
814
+ return this.syncCounters(), r;
815
+ }
816
+ // ── Private Helpers ────────────────────────────────────────
817
+ syncCounters() {
818
+ this.design.counters = this.counterManager.getCounters();
819
+ }
820
+ emitUpdate(t, o) {
821
+ this.events.emit("design:updated", { type: t, item: o });
822
+ }
823
+ }
824
+ class et {
825
+ constructor() {
826
+ this.tools = /* @__PURE__ */ new Map(), this.lazyLoaders = /* @__PURE__ */ new Map(), this.lazyMeta = /* @__PURE__ */ new Map(), this.loadingPromises = /* @__PURE__ */ new Map();
827
+ }
828
+ /** Register a tool eagerly (available immediately) */
829
+ register(t) {
830
+ this.tools.set(t.name, t), this.lazyLoaders.delete(t.name), this.lazyMeta.delete(t.name);
831
+ }
832
+ /**
833
+ * Register a tool lazily. The tool's code is only loaded when first needed.
834
+ * Provide metadata (name, label, icon) so the tool can appear in the sidebar.
835
+ *
836
+ * @param meta - Display metadata for the sidebar palette
837
+ * @param loader - Async function that imports and returns the tool definition
838
+ *
839
+ * @example
840
+ * ```ts
841
+ * registry.registerLazy(
842
+ * { name: 'timer', label: 'Timer', icon: '<svg>...</svg>', position: 11 },
843
+ * () => import('./built-in/timer-tool.js').then(m => m.timerTool),
844
+ * );
845
+ * ```
846
+ */
847
+ registerLazy(t, o) {
848
+ this.tools.has(t.name) || (this.lazyMeta.set(t.name, t), this.lazyLoaders.set(t.name, o));
849
+ }
850
+ /** Get a tool by name. Returns undefined if not loaded yet (use ensureLoaded for lazy tools). */
851
+ get(t) {
852
+ return this.tools.get(t);
853
+ }
854
+ /** Check if a tool is registered (eager or lazy) */
855
+ has(t) {
856
+ return this.tools.has(t) || this.lazyLoaders.has(t);
857
+ }
858
+ /** Check if a tool is fully loaded and ready to render */
859
+ isLoaded(t) {
860
+ return this.tools.has(t);
861
+ }
862
+ /**
863
+ * Ensure a lazy tool is loaded. Returns the tool definition.
864
+ * If the tool is already loaded, returns it immediately.
865
+ * If it's being loaded, returns the in-flight promise.
866
+ */
867
+ async ensureLoaded(t) {
868
+ if (this.tools.has(t)) return this.tools.get(t);
869
+ if (this.loadingPromises.has(t)) return this.loadingPromises.get(t);
870
+ const o = this.lazyLoaders.get(t);
871
+ if (!o) return;
872
+ const r = o().then((i) => {
873
+ if (!i?.name || !i?.renderer)
874
+ throw new Error(`[ToolRegistry] Loaded tool "${t}" has invalid definition`);
875
+ return this.tools.set(t, i), this.lazyLoaders.delete(t), this.lazyMeta.delete(t), this.loadingPromises.delete(t), i;
876
+ }).catch((i) => {
877
+ this.loadingPromises.delete(t), console.error(`[ToolRegistry] Failed to load tool "${t}":`, i);
878
+ });
879
+ return this.loadingPromises.set(t, r), r;
880
+ }
881
+ /**
882
+ * Get all tools for display in the sidebar palette.
883
+ * Returns both loaded tools and lazy tool metadata, sorted by position.
884
+ */
885
+ getAll() {
886
+ return Array.from(this.tools.values()).sort((t, o) => (t.position ?? 0) - (o.position ?? 0));
887
+ }
888
+ /**
889
+ * Get all tool names and display metadata (including lazy tools not yet loaded).
890
+ * Used by the sidebar to show all available tools.
891
+ */
892
+ getAllMeta() {
893
+ const t = [];
894
+ for (const o of this.tools.values())
895
+ t.push({ name: o.name, label: o.label, icon: o.icon, position: o.position });
896
+ for (const o of this.lazyMeta.values())
897
+ t.push(o);
898
+ return t.sort((o, r) => (o.position ?? 0) - (r.position ?? 0));
899
+ }
900
+ /** Get default values for a tool. Loads lazily if needed (sync — returns empty if not loaded). */
901
+ getDefaultValues(t) {
902
+ const o = this.tools.get(t);
903
+ if (!o) return {};
904
+ const r = { ...o.defaultValues };
905
+ for (const i of Object.values(o.options))
906
+ for (const [n, s] of Object.entries(i.options))
907
+ n in r || (r[n] = s.defaultValue);
908
+ return r;
909
+ }
910
+ /** Get property groups for a tool */
911
+ getPropertyGroups(t) {
912
+ return this.tools.get(t)?.options ?? {};
913
+ }
914
+ }
915
+ const D = {
916
+ draggingContentId: null,
917
+ draggingRowId: null,
918
+ draggingElement: null,
919
+ startContentDrag(e, t) {
920
+ this.draggingContentId = e, this.draggingElement = t ?? null;
921
+ },
922
+ startRowDrag(e, t) {
923
+ this.draggingRowId = e, this.draggingElement = t ?? null;
924
+ },
925
+ reset() {
926
+ this.draggingElement && (this.draggingElement.style.opacity = "1", this.draggingElement = null), this.draggingContentId = null, this.draggingRowId = null;
927
+ }
928
+ };
929
+ function Ie(e) {
930
+ const t = document.createElement("div");
931
+ return Object.assign(t.style, {
932
+ position: "absolute",
933
+ left: "0",
934
+ right: "0",
935
+ height: "3px",
936
+ background: e,
937
+ borderRadius: "2px",
938
+ pointerEvents: "none",
939
+ zIndex: "1000",
940
+ display: "none",
941
+ boxShadow: `0 0 6px ${e}80`
942
+ }), t;
943
+ }
944
+ function _e(e, t, o, r, i = "4px") {
945
+ e.parentNode !== t && (e.remove(), t.appendChild(e));
946
+ const s = (t instanceof ShadowRoot ? t.host : t).getBoundingClientRect();
947
+ let a;
948
+ o.length === 0 || r === 0 ? a = o.length === 0 ? 0 : o[0].getBoundingClientRect().top - s.top : r >= o.length ? a = o[o.length - 1].getBoundingClientRect().bottom - s.top : a = o[r].getBoundingClientRect().top - s.top, Object.assign(e.style, {
949
+ display: "block",
950
+ top: `${a}px`,
951
+ left: i,
952
+ right: i,
953
+ width: "auto"
954
+ });
955
+ }
956
+ function F(e) {
957
+ e && (e.style.display = "none");
958
+ }
959
+ function fe(e, t) {
960
+ const o = (e instanceof ShadowRoot, e.children);
961
+ for (const r of Array.from(o)) {
962
+ const i = r;
963
+ t(i), i.shadowRoot && fe(i.shadowRoot, t), i.children?.length && fe(i, t);
964
+ }
965
+ }
966
+ function Te(e, t) {
967
+ const o = [];
968
+ return fe(e, (r) => {
969
+ r.matches?.(t) && o.push(r);
970
+ }), o;
971
+ }
972
+ class tt {
973
+ constructor(t, o, r) {
974
+ this.currentDrop = null, this.contentIndicator = null, this.rowIndicator = null, this.onDragOver = (i) => {
975
+ const n = i.dataTransfer?.types || [], s = n.includes("application/maileditor-tool"), a = n.includes("application/maileditor-layout"), c = n.includes("application/maileditor-content") || !!D.draggingContentId, l = n.includes("application/maileditor-row") || !!D.draggingRowId;
976
+ !s && !c && !a && !l || (i.preventDefault(), i.dataTransfer.dropEffect = s || a ? "copy" : "move", a || l ? (this.currentDrop = this.findRowDropTarget(i.clientY), F(this.contentIndicator), this.showRowIndicator()) : (this.currentDrop = this.findContentDropTarget(i.clientX, i.clientY), F(this.rowIndicator), this.showContentIndicator()));
977
+ }, this.onDrop = async (i) => {
978
+ i.preventDefault(), this.hideAllIndicators();
979
+ const n = this.currentDrop, s = i.dataTransfer?.getData("application/maileditor-row") || D.draggingRowId;
980
+ if (s) {
981
+ this.handleRowDrop(s, n), this.reset();
982
+ return;
983
+ }
984
+ const a = i.dataTransfer?.getData("application/maileditor-layout");
985
+ if (a) {
986
+ this.handleLayoutDrop(JSON.parse(a), n), this.reset();
987
+ return;
988
+ }
989
+ const c = i.dataTransfer?.getData("application/maileditor-tool");
990
+ if (c) {
991
+ await this.handleToolDrop(c, n), this.reset();
992
+ return;
993
+ }
994
+ const l = i.dataTransfer?.getData("application/maileditor-content") || D.draggingContentId;
995
+ l && this.handleContentDrop(l, n), this.reset();
996
+ }, this.onDragEnd = () => {
997
+ this.hideAllIndicators(), this.reset();
998
+ }, this.onDragLeave = (i) => {
999
+ const n = i.relatedTarget;
1000
+ (!n || !this.root.contains(n)) && (this.hideAllIndicators(), this.currentDrop = null);
1001
+ }, this.store = t, this.toolRegistry = o, this.root = r;
1002
+ }
1003
+ /** Attach all drag event listeners to the shadow root */
1004
+ attach() {
1005
+ this.root.addEventListener("dragover", this.onDragOver), this.root.addEventListener("drop", this.onDrop), this.root.addEventListener("dragend", this.onDragEnd), this.root.addEventListener("dragleave", this.onDragLeave), this.contentIndicator = Ie("#3b82f6"), this.rowIndicator = Ie("#8b5cf6");
1006
+ }
1007
+ /** Remove all event listeners and clean up indicators */
1008
+ detach() {
1009
+ this.root.removeEventListener("dragover", this.onDragOver), this.root.removeEventListener("drop", this.onDrop), this.root.removeEventListener("dragend", this.onDragEnd), this.root.removeEventListener("dragleave", this.onDragLeave), this.contentIndicator?.remove(), this.rowIndicator?.remove();
1010
+ }
1011
+ // ── Drop Handlers ──────────────────────────────────────────
1012
+ handleRowDrop(t, o = this.currentDrop) {
1013
+ if (o?.type === "row" && o.rowIndex !== void 0) {
1014
+ const r = this.store.getRowIndex(t);
1015
+ if (r === -1) return;
1016
+ const i = o.rowIndex > r ? o.rowIndex - 1 : o.rowIndex;
1017
+ r !== i && this.store.moveRow(r, i);
1018
+ }
1019
+ }
1020
+ handleLayoutDrop(t, o = this.currentDrop) {
1021
+ const r = this.store.createRow(t), i = o?.type === "row" ? o.rowIndex : void 0;
1022
+ this.store.addRow(r, i);
1023
+ }
1024
+ async handleToolDrop(t, o) {
1025
+ if (await this.toolRegistry.ensureLoaded(t), o?.type === "content" && o.columnId) {
1026
+ const r = this.toolRegistry.getDefaultValues(t), i = this.store.createContent(t, r);
1027
+ this.store.addContent(o.columnId, i, o.contentIndex), this.store.select(i.id);
1028
+ } else {
1029
+ const r = this.store.createRow([1]);
1030
+ this.store.addRow(r);
1031
+ const i = this.toolRegistry.getDefaultValues(t), n = this.store.createContent(t, i);
1032
+ this.store.addContent(r.columns[0].id, n), this.store.select(n.id);
1033
+ }
1034
+ }
1035
+ handleContentDrop(t, o) {
1036
+ o?.type === "content" && o.columnId && (this.store.moveContent(t, o.columnId, o.contentIndex), this.store.select(t));
1037
+ }
1038
+ // ── Drop Target Detection ─────────────────────────────────
1039
+ findRowDropTarget(t) {
1040
+ const o = this.root.querySelector("me-editor-canvas");
1041
+ if (!o?.shadowRoot) return null;
1042
+ const r = Array.from(o.shadowRoot.querySelectorAll("me-row-renderer"));
1043
+ if (r.length === 0) return { type: "row", rowIndex: 0, y: 0 };
1044
+ let i = Math.abs(t - r[0].getBoundingClientRect().top), n = { type: "row", rowIndex: 0, y: r[0].getBoundingClientRect().top };
1045
+ for (let s = 0; s < r.length; s++) {
1046
+ const a = r[s].getBoundingClientRect().bottom, c = Math.abs(t - a);
1047
+ c < i && (i = c, n = { type: "row", rowIndex: s + 1, y: a });
1048
+ }
1049
+ return n;
1050
+ }
1051
+ findContentDropTarget(t, o) {
1052
+ const r = Te(this.root, "me-column-renderer");
1053
+ let i = null, n = 1 / 0;
1054
+ for (const s of r) {
1055
+ const a = s.dataset.columnId;
1056
+ if (!a || !s.shadowRoot) continue;
1057
+ const c = s.getBoundingClientRect();
1058
+ if (t < c.left || t > c.right) continue;
1059
+ const l = Array.from(s.shadowRoot.querySelectorAll("me-content-renderer"));
1060
+ if (l.length === 0) {
1061
+ const g = Math.abs(o - (c.top + c.height / 2));
1062
+ g < n && (n = g, i = { type: "content", columnId: a, contentIndex: 0, y: c.top + c.height / 2 });
1063
+ continue;
1064
+ }
1065
+ const u = l[0].getBoundingClientRect().top;
1066
+ let h = Math.abs(o - u);
1067
+ h < n && (n = h, i = { type: "content", columnId: a, contentIndex: 0, y: u });
1068
+ for (let g = 0; g < l.length; g++) {
1069
+ const b = l[g].getBoundingClientRect(), k = l[g + 1]?.getBoundingClientRect(), $ = k ? (b.bottom + k.top) / 2 : b.bottom;
1070
+ h = Math.abs(o - $), h < n && (n = h, i = { type: "content", columnId: a, contentIndex: g + 1, y: $ });
1071
+ }
1072
+ }
1073
+ return i;
1074
+ }
1075
+ // ── Indicator Positioning ──────────────────────────────────
1076
+ showContentIndicator() {
1077
+ if (!this.contentIndicator || !this.currentDrop?.columnId) {
1078
+ F(this.contentIndicator);
1079
+ return;
1080
+ }
1081
+ const o = Te(this.root, "me-column-renderer").find((i) => i.dataset.columnId === this.currentDrop.columnId);
1082
+ if (!o?.shadowRoot) return;
1083
+ const r = Array.from(o.shadowRoot.querySelectorAll("me-content-renderer"));
1084
+ _e(this.contentIndicator, o.shadowRoot, r, this.currentDrop.contentIndex ?? 0, "4px");
1085
+ }
1086
+ showRowIndicator() {
1087
+ if (!this.rowIndicator || !this.currentDrop) {
1088
+ F(this.rowIndicator);
1089
+ return;
1090
+ }
1091
+ const t = this.root.querySelector("me-editor-canvas"), o = t?.shadowRoot?.querySelector(".canvas-body");
1092
+ if (!o) return;
1093
+ const r = Array.from(t.shadowRoot.querySelectorAll("me-row-renderer"));
1094
+ _e(this.rowIndicator, o, r, this.currentDrop.rowIndex ?? 0, "0");
1095
+ }
1096
+ hideAllIndicators() {
1097
+ F(this.contentIndicator), F(this.rowIndicator);
1098
+ }
1099
+ reset() {
1100
+ this.currentDrop = null, D.reset();
1101
+ }
1102
+ }
1103
+ function p(e, t, o = "") {
1104
+ const r = e[t];
1105
+ return typeof r == "string" && r !== "" ? r : typeof r == "number" ? String(r) : o;
1106
+ }
1107
+ function Se(e) {
1108
+ return typeof e == "string" ? e : e && typeof e == "object" && "url" in e && e.url || "";
1109
+ }
1110
+ function Re(e) {
1111
+ if (e && typeof e == "object") {
1112
+ const t = e;
1113
+ return {
1114
+ width: typeof t.width == "number" ? t.width : void 0,
1115
+ maxWidth: typeof t.maxWidth == "string" ? t.maxWidth : void 0
1116
+ };
1117
+ }
1118
+ return {};
1119
+ }
1120
+ function ot(e) {
1121
+ const t = e.action;
1122
+ if (t && typeof t == "object") {
1123
+ const o = t.values;
1124
+ return {
1125
+ href: o?.href || "",
1126
+ target: o?.target || "_blank"
1127
+ };
1128
+ }
1129
+ return {
1130
+ href: p(e, "href"),
1131
+ target: p(e, "target", "_blank")
1132
+ };
1133
+ }
1134
+ function De(e, t = "") {
1135
+ const o = e.text;
1136
+ if (typeof o == "string" && o !== "") return o;
1137
+ const r = e.textJson;
1138
+ if (typeof r == "string")
1139
+ try {
1140
+ const i = JSON.parse(r), n = [], s = (a) => {
1141
+ typeof a.text == "string" && n.push(a.text), a.type === "linebreak" && n.push("<br/>");
1142
+ const c = a.children;
1143
+ c && c.forEach(s);
1144
+ };
1145
+ if (s(i.root || i), n.length > 0) return n.join("");
1146
+ } catch {
1147
+ }
1148
+ return t;
1149
+ }
1150
+ function be(e, t = "arial,helvetica,sans-serif") {
1151
+ const o = e.fontFamily;
1152
+ if (typeof o == "string" && o !== "") return o;
1153
+ if (o && typeof o == "object") {
1154
+ const r = o, i = r.value || t, n = r.url;
1155
+ if (n && typeof document < "u") {
1156
+ const s = `emabuild-font-${i.replace(/[^a-z]/gi, "")}`;
1157
+ if (!document.getElementById(s)) {
1158
+ const a = document.createElement("link");
1159
+ a.id = s, a.rel = "stylesheet", a.href = n, document.head.appendChild(a);
1160
+ }
1161
+ }
1162
+ return i;
1163
+ }
1164
+ return t;
1165
+ }
1166
+ function Pe(e) {
1167
+ const t = e.buttonColors;
1168
+ if (t && typeof t == "object") {
1169
+ const o = t;
1170
+ return {
1171
+ bg: o.backgroundColor || p(e, "backgroundColor", "#3b82f6"),
1172
+ color: o.color || p(e, "textColor", "#ffffff")
1173
+ };
1174
+ }
1175
+ return {
1176
+ bg: p(e, "backgroundColor", "#3b82f6"),
1177
+ color: p(e, "textColor", "#ffffff")
1178
+ };
1179
+ }
1180
+ function bo(e, t) {
1181
+ if (typeof e != "string") return t;
1182
+ try {
1183
+ return JSON.parse(e);
1184
+ } catch {
1185
+ return t;
1186
+ }
1187
+ }
1188
+ function N(e, t) {
1189
+ const { padding: o, align: r = "left", extraTdStyle: i = "" } = t;
1190
+ return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
1191
+ <tbody><tr><td style="${`padding:${o};font-family:arial,helvetica,sans-serif;${i}`}" align="${r}">
1192
+ ${e}
1193
+ </td></tr></tbody>
1194
+ </table>`;
1195
+ }
1196
+ function rt(e, t, o) {
1197
+ const { bgColor: r, textColor: i, fontSize: n, fontWeight: s, borderRadius: a } = o, c = parseInt(a) || 0;
1198
+ if (c <= 0) return "";
1199
+ const l = Math.round(c / 20 * 100);
1200
+ return `<!--[if mso]>
1201
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${t}" style="height:auto;v-text-anchor:middle;width:auto;" arcsize="${l}%" stroke="f" fillcolor="${r}">
1202
+ <w:anchorlock/>
1203
+ <center style="color:${i};font-family:arial,helvetica,sans-serif;font-size:${n};font-weight:${s};">${e}</center>
1204
+ </v:roundrect>
1205
+ <![endif]-->`;
1206
+ }
1207
+ const it = [
1208
+ { label: "100%", value: "100%" },
1209
+ { label: "120%", value: "120%" },
1210
+ { label: "140%", value: "140%" },
1211
+ { label: "160%", value: "160%" },
1212
+ { label: "180%", value: "180%" },
1213
+ { label: "200%", value: "200%" }
1214
+ ], nt = [
1215
+ { label: "Normal", value: "400" },
1216
+ { label: "Medium", value: "500" },
1217
+ { label: "Semi Bold", value: "600" },
1218
+ { label: "Bold", value: "700" },
1219
+ { label: "Extra Bold", value: "800" }
1220
+ ], st = [
1221
+ { label: "Normal", value: "400" },
1222
+ { label: "Bold", value: "700" }
1223
+ ], at = [
1224
+ { label: "12px", value: "12px" },
1225
+ { label: "14px", value: "14px" },
1226
+ { label: "16px", value: "16px" },
1227
+ { label: "18px", value: "18px" },
1228
+ { label: "20px", value: "20px" },
1229
+ { label: "22px", value: "22px" },
1230
+ { label: "26px", value: "26px" },
1231
+ { label: "30px", value: "30px" },
1232
+ { label: "36px", value: "36px" },
1233
+ { label: "48px", value: "48px" },
1234
+ { label: "60px", value: "60px" }
1235
+ ], lt = [
1236
+ { label: "12px", value: "12px" },
1237
+ { label: "13px", value: "13px" },
1238
+ { label: "14px", value: "14px" },
1239
+ { label: "16px", value: "16px" },
1240
+ { label: "18px", value: "18px" },
1241
+ { label: "20px", value: "20px" }
1242
+ ], Be = (e = "140%") => ({
1243
+ label: "Line Height",
1244
+ defaultValue: e,
1245
+ widget: "dropdown",
1246
+ widgetParams: { options: [...it] }
1247
+ }), We = (e = "700", t = !1) => ({
1248
+ label: "Font Weight",
1249
+ defaultValue: e,
1250
+ widget: "dropdown",
1251
+ widgetParams: { options: [...t ? st : nt] }
1252
+ }), G = {
1253
+ title: "Spacing",
1254
+ options: {
1255
+ containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
1256
+ }
1257
+ }, ne = {
1258
+ title: "General",
1259
+ options: {
1260
+ anchor: { label: "Anchor", defaultValue: "", widget: "text" },
1261
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1262
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1263
+ }
1264
+ }, dt = {
1265
+ title: "General",
1266
+ options: {
1267
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1268
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1269
+ }
1270
+ }, ct = {
1271
+ name: "text",
1272
+ label: "Text",
1273
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/></svg>',
1274
+ supportedDisplayModes: ["email", "web"],
1275
+ position: 1,
1276
+ options: {
1277
+ text: {
1278
+ title: "Text",
1279
+ options: {
1280
+ text: {
1281
+ label: "Text Content",
1282
+ defaultValue: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
1283
+ widget: "rich_text"
1284
+ }
1285
+ }
1286
+ },
1287
+ style: {
1288
+ title: "Style",
1289
+ options: {
1290
+ color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
1291
+ backgroundColor: { label: "Background Color", defaultValue: "", widget: "color_picker" },
1292
+ textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
1293
+ lineHeight: Be()
1294
+ }
1295
+ },
1296
+ spacing: G,
1297
+ general: ne
1298
+ },
1299
+ defaultValues: {
1300
+ text: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
1301
+ color: "#000000",
1302
+ backgroundColor: "",
1303
+ lineHeight: "140%",
1304
+ containerPadding: "10px",
1305
+ textAlign: "left"
1306
+ },
1307
+ renderer: {
1308
+ renderEditor(e) {
1309
+ const t = p(e, "backgroundColor", "transparent"), o = p(e, "color", "inherit"), r = p(e, "lineHeight", "140%"), i = p(e, "textAlign", "left"), n = be(e), s = p(e, "text");
1310
+ return d`
1311
+ <div style="background-color:${t};color:${o};line-height:${r};text-align:${i};font-family:${n};">
1312
+ ${ie(s)}
1313
+ </div>
1314
+ `;
1315
+ },
1316
+ renderHtml(e) {
1317
+ const t = p(e, "containerPadding", "10px"), o = p(e, "backgroundColor"), r = p(e, "color", "#000000"), i = p(e, "lineHeight", "140%"), n = p(e, "textAlign", "left"), s = p(e, "text"), a = o ? `background-color:${o};` : "", c = `<div style="font-size:14px;color:${r};line-height:${i};text-align:${n};">${s}</div>`;
1318
+ return N(c, { padding: t, extraTdStyle: a });
1319
+ }
1320
+ }
1321
+ }, pt = {
1322
+ name: "heading",
1323
+ label: "Heading",
1324
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 12h12"/><path d="M6 4v16"/><path d="M18 4v16"/></svg>',
1325
+ supportedDisplayModes: ["email", "web"],
1326
+ position: 2,
1327
+ options: {
1328
+ text: {
1329
+ title: "Heading",
1330
+ options: {
1331
+ text: { label: "Text", defaultValue: "Heading", widget: "text" },
1332
+ headingType: {
1333
+ label: "Heading Type",
1334
+ defaultValue: "h1",
1335
+ widget: "dropdown",
1336
+ widgetParams: { options: [
1337
+ { label: "H1", value: "h1" },
1338
+ { label: "H2", value: "h2" },
1339
+ { label: "H3", value: "h3" },
1340
+ { label: "H4", value: "h4" }
1341
+ ] }
1342
+ }
1343
+ }
1344
+ },
1345
+ style: {
1346
+ title: "Style",
1347
+ options: {
1348
+ fontSize: {
1349
+ label: "Font Size",
1350
+ defaultValue: "22px",
1351
+ widget: "dropdown",
1352
+ widgetParams: { options: [...at] }
1353
+ },
1354
+ color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
1355
+ textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
1356
+ fontWeight: We(),
1357
+ lineHeight: Be(),
1358
+ letterSpacing: {
1359
+ label: "Letter Spacing",
1360
+ defaultValue: "0px",
1361
+ widget: "number_unit",
1362
+ widgetParams: { unit: "px", min: -5, max: 50, step: 0.1 }
1363
+ }
1364
+ }
1365
+ },
1366
+ spacing: G,
1367
+ general: ne
1368
+ },
1369
+ defaultValues: {
1370
+ text: "Heading",
1371
+ headingType: "h1",
1372
+ fontSize: "22px",
1373
+ color: "#000000",
1374
+ textAlign: "left",
1375
+ fontWeight: "700",
1376
+ lineHeight: "140%",
1377
+ letterSpacing: "0px",
1378
+ containerPadding: "10px"
1379
+ },
1380
+ renderer: {
1381
+ renderEditor(e) {
1382
+ const t = {
1383
+ padding: p(e, "containerPadding", "10px"),
1384
+ fontSize: p(e, "fontSize", "22px"),
1385
+ color: p(e, "color", "#000000"),
1386
+ textAlign: p(e, "textAlign", "left"),
1387
+ fontWeight: p(e, "fontWeight", "700"),
1388
+ lineHeight: p(e, "lineHeight", "140%"),
1389
+ letterSpacing: p(e, "letterSpacing", "normal"),
1390
+ fontFamily: be(e)
1391
+ }, o = De(e, "Heading");
1392
+ return d`<div style=${He(t)}>${ie(o)}</div>`;
1393
+ },
1394
+ renderHtml(e) {
1395
+ const t = p(e, "containerPadding", "10px"), o = p(e, "fontSize", "22px"), r = p(e, "color", "#000000"), i = p(e, "textAlign", "left"), n = p(e, "fontWeight", "700"), s = p(e, "lineHeight", "140%"), a = p(e, "letterSpacing", "normal"), c = be(e), l = p(e, "headingType", "h1"), u = De(e, "Heading"), h = `<${l} style="margin:0;font-size:${o};color:${r};text-align:${i};font-weight:${n};line-height:${s};letter-spacing:${a};font-family:${c};">${u}</${l}>`;
1396
+ return N(h, { padding: t });
1397
+ }
1398
+ }
1399
+ }, ut = {
1400
+ name: "paragraph",
1401
+ label: "Paragraph",
1402
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><line x1="3" y1="14" x2="17" y2="14"/></svg>',
1403
+ supportedDisplayModes: ["email", "web"],
1404
+ position: 3,
1405
+ options: {
1406
+ text: {
1407
+ title: "Paragraph",
1408
+ options: {
1409
+ text: {
1410
+ label: "Text",
1411
+ defaultValue: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>',
1412
+ widget: "rich_text"
1413
+ }
1414
+ }
1415
+ },
1416
+ style: {
1417
+ title: "Style",
1418
+ options: {
1419
+ color: { label: "Text Color", defaultValue: "#374151", widget: "color_picker" },
1420
+ textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
1421
+ lineHeight: { label: "Line Height", defaultValue: "160%", widget: "text" },
1422
+ letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
1423
+ }
1424
+ },
1425
+ spacing: G
1426
+ },
1427
+ defaultValues: {
1428
+ text: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>',
1429
+ color: "#374151",
1430
+ lineHeight: "160%",
1431
+ letterSpacing: "normal",
1432
+ textAlign: "left",
1433
+ containerPadding: "10px"
1434
+ },
1435
+ renderer: {
1436
+ renderEditor(e) {
1437
+ const t = p(e, "color", "#374151"), o = p(e, "lineHeight", "160%"), r = p(e, "textAlign", "left");
1438
+ return d`<div style="color:${t};line-height:${o};text-align:${r};">${ie(p(e, "text"))}</div>`;
1439
+ },
1440
+ renderHtml(e) {
1441
+ const t = p(e, "containerPadding", "10px"), o = p(e, "color", "#374151"), r = p(e, "lineHeight", "160%"), i = p(e, "textAlign", "left"), n = p(e, "letterSpacing", "normal"), s = `<div style="font-size:14px;color:${o};line-height:${r};text-align:${i};letter-spacing:${n};word-wrap:break-word;">${p(e, "text")}</div>`;
1442
+ return N(s, { padding: t });
1443
+ }
1444
+ }
1445
+ }, ht = {
1446
+ name: "image",
1447
+ label: "Image",
1448
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
1449
+ supportedDisplayModes: ["email", "web"],
1450
+ position: 3,
1451
+ options: {
1452
+ image: {
1453
+ title: "Image",
1454
+ options: {
1455
+ src: { label: "Image URL", defaultValue: "", widget: "image_upload" },
1456
+ alt: { label: "Alt Text", defaultValue: "", widget: "text" },
1457
+ href: { label: "Link URL", defaultValue: "", widget: "text" },
1458
+ target: { label: "Link Target", defaultValue: "_blank", widget: "text" }
1459
+ }
1460
+ },
1461
+ style: {
1462
+ title: "Style",
1463
+ options: {
1464
+ width: {
1465
+ label: "Width",
1466
+ defaultValue: "100%",
1467
+ widget: "dropdown",
1468
+ widgetParams: { options: [
1469
+ { label: "Auto", value: "auto" },
1470
+ { label: "25%", value: "25%" },
1471
+ { label: "50%", value: "50%" },
1472
+ { label: "75%", value: "75%" },
1473
+ { label: "100%", value: "100%" }
1474
+ ] }
1475
+ },
1476
+ align: { label: "Align", defaultValue: "center", widget: "alignment" },
1477
+ borderRadius: { label: "Border Radius", defaultValue: "0px", widget: "text" }
1478
+ }
1479
+ },
1480
+ spacing: G,
1481
+ general: ne
1482
+ },
1483
+ defaultValues: {
1484
+ src: "https://placehold.co/600x200/e2e8f0/64748b?text=Drop+Image+Here",
1485
+ alt: "Image",
1486
+ href: "",
1487
+ target: "_blank",
1488
+ width: "100%",
1489
+ maxWidth: "100%",
1490
+ align: "center",
1491
+ borderRadius: "0px",
1492
+ containerPadding: "10px"
1493
+ },
1494
+ renderer: {
1495
+ renderEditor(e) {
1496
+ const t = Se(e.src), o = p(e, "alt"), i = Re(e.src).maxWidth || p(e, "width", "100%"), n = p(e, "borderRadius", "0px"), s = p(e, "textAlign", p(e, "align", "center"));
1497
+ return t ? d`<div style="text-align:${s};"><img src=${t} alt=${o} style="display:inline-block;max-width:100%;width:${i};border-radius:${n};border:0;" /></div>` : d`<div style="text-align:${s};"><div style="background:#f1f5f9;border:2px dashed #cbd5e1;border-radius:8px;padding:40px 20px;text-align:center;color:#94a3b8;font-size:13px;">No image set. Enter a URL in the property panel.</div></div>`;
1498
+ },
1499
+ renderHtml(e, t) {
1500
+ const o = p(e, "containerPadding", "10px"), r = Se(e.src), i = p(e, "alt"), n = ot(e), s = Re(e.src), a = s.maxWidth || p(e, "width", "100%"), c = p(e, "borderRadius", "0px"), l = p(e, "textAlign", p(e, "align", "center"));
1501
+ let u;
1502
+ a.includes("%") ? u = Math.round(t.columnWidth * (parseFloat(a) / 100)) : a === "auto" ? u = s.width || t.columnWidth : u = parseInt(a) || t.columnWidth;
1503
+ const h = c !== "0px" ? `border-radius:${c};` : "", g = `<img align="${l}" border="0" src="${r}" alt="${i}" title="${i}" style="display:block;border:0;height:auto;width:100%;max-width:${u}px;${h}" width="${u}" />`, b = n.href ? `<a href="${n.href}" target="${n.target}" style="text-decoration:none;">${g}</a>` : g;
1504
+ return N(b, { padding: o, align: l });
1505
+ }
1506
+ }
1507
+ }, gt = {
1508
+ name: "button",
1509
+ label: "Button",
1510
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="10" rx="2"/><path d="M12 7v10"/><path d="m8 12 4-3 4 3"/></svg>',
1511
+ supportedDisplayModes: ["email", "web"],
1512
+ position: 4,
1513
+ options: {
1514
+ button: {
1515
+ title: "Button",
1516
+ options: {
1517
+ text: { label: "Button Text", defaultValue: "Click Me", widget: "text" },
1518
+ href: { label: "Link URL", defaultValue: "#", widget: "text" },
1519
+ target: { label: "Target", defaultValue: "_blank", widget: "text" }
1520
+ }
1521
+ },
1522
+ style: {
1523
+ title: "Style",
1524
+ options: {
1525
+ backgroundColor: { label: "Button Color", defaultValue: "#3b82f6", widget: "color_picker" },
1526
+ textColor: { label: "Text Color", defaultValue: "#ffffff", widget: "color_picker" },
1527
+ fontSize: {
1528
+ label: "Font Size",
1529
+ defaultValue: "14px",
1530
+ widget: "dropdown",
1531
+ widgetParams: { options: [...lt] }
1532
+ },
1533
+ fontWeight: We("700", !0),
1534
+ borderRadius: { label: "Border Radius", defaultValue: "4px", widget: "text" },
1535
+ buttonWidth: {
1536
+ label: "Width",
1537
+ defaultValue: "auto",
1538
+ widget: "dropdown",
1539
+ widgetParams: { options: [
1540
+ { label: "Auto", value: "auto" },
1541
+ { label: "100%", value: "100%" },
1542
+ { label: "50%", value: "50%" }
1543
+ ] }
1544
+ },
1545
+ textAlign: { label: "Align", defaultValue: "center", widget: "alignment" },
1546
+ buttonPadding: { label: "Button Padding", defaultValue: "10px 20px", widget: "padding" },
1547
+ borderColor: { label: "Border Color", defaultValue: "", widget: "color_picker" },
1548
+ borderWidth: { label: "Border Width", defaultValue: "0px", widget: "text" }
1549
+ }
1550
+ },
1551
+ spacing: G,
1552
+ general: ne
1553
+ },
1554
+ defaultValues: {
1555
+ text: "Click Me",
1556
+ href: "#",
1557
+ target: "_blank",
1558
+ backgroundColor: "#3b82f6",
1559
+ textColor: "#ffffff",
1560
+ fontSize: "14px",
1561
+ fontWeight: "700",
1562
+ borderRadius: "4px",
1563
+ buttonWidth: "auto",
1564
+ textAlign: "center",
1565
+ buttonPadding: "10px 20px",
1566
+ borderColor: "",
1567
+ borderWidth: "0px",
1568
+ containerPadding: "10px"
1569
+ },
1570
+ renderer: {
1571
+ renderEditor(e) {
1572
+ const { bg: t, color: o } = Pe(e), r = p(e, "fontSize", "14px"), i = p(e, "fontWeight", "700"), n = p(e, "borderRadius", "4px"), s = p(e, "buttonPadding", p(e, "padding", "10px 20px")), a = p(e, "text", "Click Me"), c = p(e, "textAlign", "center"), l = p(e, "buttonWidth", "auto"), u = p(e, "borderWidth", "0px"), h = p(e, "borderColor", t), g = u !== "0px" ? `border:${u} solid ${h};` : "border:none;", b = l === "auto" ? "display:inline-block;" : `display:block;width:${l};`;
1573
+ return d`
1574
+ <div style="text-align:${c};">
1575
+ <a style="${b}background-color:${t};color:${o};font-size:${r};font-weight:${i};border-radius:${n};padding:${s};text-decoration:none;text-align:center;${g}cursor:pointer;font-family:arial,helvetica,sans-serif;">${a}</a>
1576
+ </div>
1577
+ `;
1578
+ },
1579
+ renderHtml(e) {
1580
+ const t = p(e, "containerPadding", "10px"), { bg: o, color: r } = Pe(e), i = p(e, "fontSize", "14px"), n = p(e, "fontWeight", "700"), s = p(e, "borderRadius", "4px"), a = p(e, "buttonPadding", p(e, "padding", "10px 20px")), c = p(e, "text", "Click Me"), l = p(e, "textAlign", "center"), u = p(e, "href", "#"), h = p(e, "target", "_blank"), g = p(e, "borderWidth", "0px"), b = p(e, "borderColor", o), k = g !== "0px" ? `border:${g} solid ${b};` : "border:none;", $ = rt(c, u, { bgColor: o, textColor: r, fontSize: i, fontWeight: n, borderRadius: s }), m = $ ? `${$}
1581
+ <!--[if !mso]><!-->` : "<!--[if !mso]><!-->", y = `<div align="${l}">
1582
+ ${m}
1583
+ <a href="${u}" target="${h}" style="display:inline-block;text-decoration:none;text-align:center;color:${r};background-color:${o};border-radius:${s};font-size:${i};font-weight:${n};padding:${a};font-family:arial,helvetica,sans-serif;${k}mso-border-alt:none;"><span style="line-height:120%;">${c}</span></a>
1584
+ <!--<![endif]-->
1585
+ </div>`;
1586
+ return N(y, { padding: t, align: l });
1587
+ }
1588
+ }
1589
+ }, ft = {
1590
+ name: "divider",
1591
+ label: "Divider",
1592
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="2" y1="12" x2="22" y2="12"/></svg>',
1593
+ supportedDisplayModes: ["email", "web"],
1594
+ position: 5,
1595
+ options: {
1596
+ style: {
1597
+ title: "Style",
1598
+ options: {
1599
+ borderTopWidth: { label: "Width", defaultValue: "1px", widget: "text" },
1600
+ borderTopStyle: {
1601
+ label: "Style",
1602
+ defaultValue: "solid",
1603
+ widget: "dropdown",
1604
+ widgetParams: { options: [
1605
+ { label: "Solid", value: "solid" },
1606
+ { label: "Dashed", value: "dashed" },
1607
+ { label: "Dotted", value: "dotted" },
1608
+ { label: "Double", value: "double" }
1609
+ ] }
1610
+ },
1611
+ borderTopColor: { label: "Color", defaultValue: "#cccccc", widget: "color_picker" },
1612
+ width: { label: "Line Width", defaultValue: "100%", widget: "text" }
1613
+ }
1614
+ },
1615
+ spacing: G,
1616
+ general: dt
1617
+ },
1618
+ defaultValues: {
1619
+ borderTopWidth: "1px",
1620
+ borderTopStyle: "solid",
1621
+ borderTopColor: "#cccccc",
1622
+ width: "100%",
1623
+ containerPadding: "10px"
1624
+ },
1625
+ renderer: {
1626
+ renderEditor(e) {
1627
+ p(e, "containerPadding", "10px");
1628
+ const t = p(e, "width", "100%"), o = `${p(e, "borderTopWidth", "1px")} ${p(e, "borderTopStyle", "solid")} ${p(e, "borderTopColor", "#cccccc")}`;
1629
+ return d`<div><div style="border-top:${o};width:${t};margin:0 auto;"></div></div>`;
1630
+ },
1631
+ renderHtml(e) {
1632
+ const t = p(e, "containerPadding", "10px"), o = p(e, "width", "100%"), r = `${p(e, "borderTopWidth", "1px")} ${p(e, "borderTopStyle", "solid")} ${p(e, "borderTopColor", "#cccccc")}`, i = `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="${o}" style="border-collapse:collapse;border-top:${r};"><tbody><tr><td style="font-size:0;line-height:0;">&nbsp;</td></tr></tbody></table>`;
1633
+ return N(i, { padding: t, align: "center" });
1634
+ }
1635
+ }
1636
+ }, bt = [
1637
+ ct,
1638
+ pt,
1639
+ ut,
1640
+ ht,
1641
+ gt,
1642
+ ft
1643
+ ], Ee = [
1644
+ {
1645
+ meta: { name: "html", label: "HTML", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>', position: 6 },
1646
+ loader: () => import("./html-tool-BxhBGl4L.js").then((e) => e.htmlTool)
1647
+ },
1648
+ {
1649
+ meta: { name: "social", label: "Social", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>', position: 8 },
1650
+ loader: () => import("./social-tool-C1FeCyUm.js").then((e) => e.socialTool)
1651
+ },
1652
+ {
1653
+ meta: { name: "menu", label: "Menu", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg>', position: 9 },
1654
+ loader: () => import("./menu-tool-IZqYp8Vb.js").then((e) => e.menuTool)
1655
+ },
1656
+ {
1657
+ meta: { name: "video", label: "Video", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>', position: 10 },
1658
+ loader: () => import("./video-tool-CHhPfHaS.js").then((e) => e.videoTool)
1659
+ },
1660
+ {
1661
+ meta: { name: "timer", label: "Timer", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>', position: 11 },
1662
+ loader: () => import("./timer-tool-Dpw9p0uW.js").then((e) => e.timerTool)
1663
+ },
1664
+ {
1665
+ meta: { name: "table", label: "Table", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/><path d="M9 3v18"/><path d="M15 3v18"/></svg>', position: 12 },
1666
+ loader: () => import("./table-tool-BD72-Fuj.js").then((e) => e.tableTool)
1667
+ },
1668
+ {
1669
+ meta: { name: "form", label: "Form", icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 7h10"/><path d="M7 12h10"/><path d="M7 17h6"/></svg>', position: 13 },
1670
+ loader: () => import("./form-tool-DsPgMShR.js").then((e) => e.formTool)
1671
+ }
1672
+ ];
1673
+ function mt(e, t, o) {
1674
+ const r = o.backgroundColor || "#e7e7e7", i = o.contentWidth || "600px", n = o.fontFamily?.value || "arial,helvetica,sans-serif", s = o.textColor || "#000000", a = o.preheaderText || "" || "&zwnj;", c = `<div style="display:none;font-size:1px;color:${r};line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">${a}${"&zwnj;&nbsp;".repeat(80)}</div>`;
1675
+ return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1676
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
1677
+ <head>
1678
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1679
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1680
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
1681
+ <meta name="x-apple-disable-message-reformatting">
1682
+ <meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
1683
+ <meta name="color-scheme" content="light dark">
1684
+ <meta name="supported-color-schemes" content="light dark">
1685
+ <title></title>
1686
+ <!--[if mso]>
1687
+ <noscript><xml>
1688
+ <o:OfficeDocumentSettings>
1689
+ <o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch>
1690
+ </o:OfficeDocumentSettings>
1691
+ </xml></noscript>
1692
+ <style type="text/css">
1693
+ table, td, th { font-family: ${n} !important; }
1694
+ </style>
1695
+ <![endif]-->
1696
+ <!--[if !mso]><!-->
1697
+ <style type="text/css">
1698
+ ${t}
1699
+ </style>
1700
+ <!--<![endif]-->
1701
+ <style type="text/css">
1702
+ body { margin: 0; padding: 0; word-break: normal; }
1703
+ table, tr, td { vertical-align: top; border-collapse: collapse; }
1704
+ p { margin: 0; }
1705
+ a[x-apple-data-detectors='true'] { color: inherit !important; text-decoration: none !important; }
1706
+ </style>
1707
+ </head>
1708
+ <body class="clean-body u_body" style="margin:0;padding:0;-webkit-text-size-adjust:100%;background-color:${r};color:${s};">
1709
+ ${c}
1710
+ <table id="u_body" role="presentation" style="border-collapse:collapse;border-spacing:0;margin:0 auto;background-color:${r};width:100%;" cellpadding="0" cellspacing="0" border="0">
1711
+ <tbody>
1712
+ <tr>
1713
+ <td style="vertical-align:top;">
1714
+ <!--[if (mso)|(IE)]><table width="${parseInt(i)}" align="center" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
1715
+ ${e}
1716
+ <!--[if (mso)|(IE)]></td></tr></table><![endif]-->
1717
+ </td>
1718
+ </tr>
1719
+ </tbody>
1720
+ </table>
1721
+ </body>
1722
+ </html>`;
1723
+ }
1724
+ function yt(e, t, o) {
1725
+ const r = parseInt(t.contentWidth || "600"), i = e.values.backgroundColor || "", n = e.values.columnsBackgroundColor || "", s = e.values.padding || "0px", a = e.cells.reduce((m, f) => m + f, 0), c = i ? `background-color:${i};` : "", l = e.values.backgroundImage;
1726
+ let u = "";
1727
+ if (l?.url) {
1728
+ const m = l.repeat === !0 || l.repeat === "repeat" ? "repeat" : "no-repeat", f = l.cover === !0 ? "cover" : l.fullWidth === !0 ? "100% auto" : "auto", y = l.center !== !1 ? "center top" : "left top";
1729
+ u = `background-image:url('${l.url}');background-repeat:${m};background-position:${y};background-size:${f};`;
1730
+ }
1731
+ const h = e.columns.length > 1, g = e.columns.map((m, f) => {
1732
+ const y = Math.round(e.cells[f] / a * r);
1733
+ return { colHtml: xt(m, y, n, t, o), colWidthPx: y };
1734
+ });
1735
+ let b;
1736
+ if (h) {
1737
+ const m = g.map(
1738
+ ({ colHtml: f, colWidthPx: y }) => `<!--[if (mso)|(IE)]><td align="center" width="${y}" style="width:${y}px;padding:0px;" valign="top"><![endif]-->${f}<!--[if (mso)|(IE)]></td><![endif]-->`
1739
+ );
1740
+ b = `<!--[if (mso)|(IE)]><table role="presentation" width="${r}" cellpadding="0" cellspacing="0" border="0"><tr><![endif]-->${m.join("")}<!--[if (mso)|(IE)]></tr></table><![endif]-->`;
1741
+ } else
1742
+ b = g.map(({ colHtml: m }) => m).join("");
1743
+ const k = e.values.hideDesktop ? " u_hide_desktop" : "", $ = e.values.hideMobile ? " u_hide_mobile" : "";
1744
+ return `<div class="u_row${k}${$}" style="padding:${s};${c}${u}">
1745
+ <div style="margin:0 auto;max-width:${r}px;${h ? "font-size:0;" : ""}text-align:center;">${b}</div>
1746
+ </div>`;
1747
+ }
1748
+ function xt(e, t, o, r, i) {
1749
+ const n = e.values.backgroundColor || o || "", s = e.values.padding || "0px", a = e.values.borderRadius || "0px", c = n ? `background-color:${n};` : "", l = e.contents.map((u) => {
1750
+ const h = i.get(u.type);
1751
+ if (!h) return `<!-- unknown tool: ${u.type} -->`;
1752
+ const g = {
1753
+ columnWidth: t,
1754
+ displayMode: "email",
1755
+ contentWidth: parseInt(r.contentWidth || "600"),
1756
+ bodyValues: r
1757
+ };
1758
+ let b = h(u.values, g);
1759
+ const k = !!u.values.hideDesktop, $ = !!u.values.hideMobile;
1760
+ return (k || $) && (b = `<div class="${[k && "u_hide_desktop", $ && "u_hide_mobile"].filter(Boolean).join(" ")}">${b}</div>`), b;
1761
+ }).join(`
1762
+ `);
1763
+ return `<div class="u_column" style="display:inline-block;vertical-align:top;width:${t}px;max-width:${t}px;font-size:14px;text-align:left;">
1764
+ <div style="width:100%;${c}${a !== "0px" ? `border-radius:${a};` : ""}">
1765
+ <div style="padding:${s};">
1766
+ ${l || "&nbsp;"}
1767
+ </div>
1768
+ </div>
1769
+ </div>`;
1770
+ }
1771
+ function vt(e) {
1772
+ return `
1773
+ @media only screen and (min-width: ${e + 20}px) {
1774
+ .u_row .u_column { display: inline-block !important; }
1775
+ }
1776
+
1777
+ @media only screen and (max-width: ${e + 20}px) {
1778
+ .u_row .u_column {
1779
+ display: block !important;
1780
+ width: 100% !important;
1781
+ max-width: 100% !important;
1782
+ }
1783
+ .u_row {
1784
+ width: 100% !important;
1785
+ }
1786
+ }
1787
+
1788
+ @media only screen and (max-width: 620px) {
1789
+ .u_row-container {
1790
+ max-width: 100% !important;
1791
+ padding-left: 0 !important;
1792
+ padding-right: 0 !important;
1793
+ }
1794
+ }
1795
+
1796
+ @media (prefers-color-scheme: dark) {
1797
+ /* Dark mode overrides — add per-client rules as needed */
1798
+ }
1799
+
1800
+ /* Outlook dark mode */
1801
+ [data-ogsb] body,
1802
+ [data-ogsb] table,
1803
+ [data-ogsb] td {
1804
+ /* Preserve original colors */
1805
+ }
1806
+
1807
+ .u_hide_desktop { display: block !important; }
1808
+ .u_hide_mobile { display: block !important; }
1809
+
1810
+ @media only screen and (max-width: ${e + 20}px) {
1811
+ .u_hide_desktop { display: block !important; }
1812
+ .u_hide_mobile { display: none !important; }
1813
+ }
1814
+
1815
+ @media only screen and (min-width: ${e + 21}px) {
1816
+ .u_hide_desktop { display: none !important; }
1817
+ .u_hide_mobile { display: block !important; }
1818
+ }`;
1819
+ }
1820
+ const wt = [
1821
+ "box-sizing",
1822
+ "overflow-wrap",
1823
+ "word-break",
1824
+ "word-wrap",
1825
+ "outline",
1826
+ "cursor",
1827
+ "transition",
1828
+ "animation",
1829
+ "transform",
1830
+ "z-index",
1831
+ "display:\\s*flex",
1832
+ "display:\\s*grid",
1833
+ "gap"
1834
+ ], kt = new RegExp(
1835
+ `(?:;\\s*|^\\s*)(${wt.join("|")})\\s*:[^;]*;?`,
1836
+ "gi"
1837
+ ), $t = /var\(--[^)]*\)/gi;
1838
+ function Ct(e) {
1839
+ return e.replace(/style="([^"]*)"/gi, (t, o) => {
1840
+ let r = o;
1841
+ return r = r.replace(kt, ""), r = r.replace($t, "inherit"), r = r.replace(/;\s*;/g, ";").replace(/^\s*;\s*/, "").replace(/;\s*$/, "").trim(), r ? `style="${r}"` : "";
1842
+ });
1843
+ }
1844
+ function It(e, t, o) {
1845
+ const r = e.body.values, i = parseInt(r.contentWidth || "600"), n = e.body.rows.map((g) => yt(g, r, t)).join(`
1846
+ `), s = vt(i), a = Ct(n);
1847
+ let c = mt(a, s, r);
1848
+ if (o?.mergeTags)
1849
+ for (const [g, b] of Object.entries(o.mergeTags))
1850
+ c = c.replaceAll(`{{${g}}}`, b);
1851
+ const l = c.match(/<body[^>]*>([\s\S]*)<\/body>/i), u = c.match(/<style[^>]*>([\s\S]*?)<\/style>/gi), h = [];
1852
+ return r.fontFamily?.url && h.push(r.fontFamily.url), {
1853
+ design: structuredClone(e),
1854
+ html: c,
1855
+ chunks: {
1856
+ body: l?.[1] ?? n,
1857
+ css: u?.map((g) => g.replace(/<\/?style[^>]*>/gi, "")).join(`
1858
+ `) ?? s,
1859
+ fonts: h,
1860
+ js: ""
1861
+ }
1862
+ };
1863
+ }
1864
+ function _t(e) {
1865
+ return typeof e == "number" ? e : typeof e == "string" && parseInt(e, 10) || 0;
1866
+ }
1867
+ function Tt(e) {
1868
+ return typeof e == "string" ? e.includes("px") ? e : e + "px" : typeof e == "number" ? e + "px" : "0px";
1869
+ }
1870
+ function me(e) {
1871
+ return e ? {
1872
+ url: e.url || "",
1873
+ fullWidth: e.fullWidth ?? !0,
1874
+ repeat: e.repeat ?? !1,
1875
+ center: e.position === "center" || e.center === !0,
1876
+ cover: e.size === "cover" || e.cover === !0
1877
+ } : { url: "", fullWidth: !0, repeat: !1, center: !0, cover: !1 };
1878
+ }
1879
+ function pe(e) {
1880
+ return {
1881
+ url: e.url || "",
1882
+ fullWidth: e.fullWidth,
1883
+ repeat: e.repeat,
1884
+ size: e.cover ? "cover" : "custom",
1885
+ position: e.center ? "center" : "top left"
1886
+ };
1887
+ }
1888
+ function St(e, t) {
1889
+ const o = { ...t };
1890
+ if (e === "image") {
1891
+ if (o.src && typeof o.src == "object") {
1892
+ const r = o.src;
1893
+ o.src = r.url || "", r.width && (o.maxWidth = r.width + "px");
1894
+ }
1895
+ if (o.altText !== void 0 && o.alt === void 0 && (o.alt = o.altText, delete o.altText), o.action && typeof o.action == "object") {
1896
+ const r = o.action;
1897
+ o.href = r.values?.href || "", o.target = r.values?.target || "_blank", delete o.action;
1898
+ }
1899
+ }
1900
+ if (e === "button") {
1901
+ if (o.href && typeof o.href == "object") {
1902
+ const r = o.href;
1903
+ o.href = r.values?.href || "", o.target = r.values?.target || "_blank";
1904
+ }
1905
+ if (o.buttonColors && typeof o.buttonColors == "object") {
1906
+ const r = o.buttonColors;
1907
+ o.backgroundColor = r.backgroundColor || o.backgroundColor, o.textColor = r.color || o.textColor, delete o.buttonColors;
1908
+ }
1909
+ if (o.size && typeof o.size == "object") {
1910
+ const r = o.size;
1911
+ o.buttonWidth = r.autoWidth ? "auto" : r.width ? Tt(r.width) : "auto", delete o.size;
1912
+ }
1913
+ if (o.padding !== void 0 && o.buttonPadding === void 0 && (o.buttonPadding = o.padding), o.border && typeof o.border == "object") {
1914
+ const r = o.border;
1915
+ o.borderColor = r.borderTopColor || "", o.borderWidth = r.borderTopWidth || "0px", delete o.border;
1916
+ }
1917
+ typeof o.fontWeight == "number" && (o.fontWeight = String(o.fontWeight));
1918
+ }
1919
+ if (e === "heading" && typeof o.fontWeight == "number" && (o.fontWeight = String(o.fontWeight)), e === "divider" && o.border && typeof o.border == "object") {
1920
+ const r = o.border;
1921
+ o.borderTopWidth = r.borderTopWidth || "1px", o.borderTopStyle = r.borderTopStyle || "solid", o.borderTopColor = r.borderTopColor || "#cccccc", delete o.border;
1922
+ }
1923
+ if (e === "menu") {
1924
+ if (o.menu && typeof o.menu == "object") {
1925
+ const r = o.menu;
1926
+ Array.isArray(r.items) && (o.items = JSON.stringify(r.items.map((i) => ({
1927
+ text: i.text || "",
1928
+ href: i.link?.values?.href || "#"
1929
+ })))), o.menu.separator && (o.separator = o.menu.separator), delete o.menu;
1930
+ }
1931
+ o.linkColor && !o.color && (o.color = o.linkColor);
1932
+ }
1933
+ return o;
1934
+ }
1935
+ function Rt(e, t) {
1936
+ const o = { ...t };
1937
+ if (e === "image") {
1938
+ const r = typeof o.src == "string" ? o.src : "";
1939
+ o.src = {
1940
+ url: r,
1941
+ width: _t(o.maxWidth) || null,
1942
+ height: null,
1943
+ autoWidth: !0
1944
+ }, o.alt !== void 0 && (o.altText = o.alt, delete o.alt), (o.href !== void 0 || o.target !== void 0) && (o.action = {
1945
+ name: "web",
1946
+ values: { href: o.href || "", target: o.target || "_blank" }
1947
+ }, delete o.href, delete o.target), delete o.maxWidth;
1948
+ }
1949
+ if (e === "button" && (typeof o.href == "string" && (o.href = {
1950
+ name: "web",
1951
+ values: { href: o.href, target: o.target || "_blank" }
1952
+ }, delete o.target), o.buttonColors = {
1953
+ color: o.textColor || "#ffffff",
1954
+ backgroundColor: o.backgroundColor || "#3b82f6",
1955
+ hoverColor: o.textColor || "#ffffff",
1956
+ hoverBackgroundColor: o.backgroundColor || "#3b82f6"
1957
+ }, o.buttonWidth !== void 0 && (o.size = {
1958
+ autoWidth: o.buttonWidth === "auto",
1959
+ width: o.buttonWidth === "auto" ? "100%" : o.buttonWidth
1960
+ }, delete o.buttonWidth), o.buttonPadding !== void 0 && (o.padding = o.buttonPadding, delete o.buttonPadding), (o.borderColor !== void 0 || o.borderWidth !== void 0) && (o.border = {
1961
+ borderTopWidth: o.borderWidth || "0px",
1962
+ borderTopStyle: "solid",
1963
+ borderTopColor: o.borderColor || "",
1964
+ borderRightWidth: o.borderWidth || "0px",
1965
+ borderRightStyle: "solid",
1966
+ borderRightColor: o.borderColor || "",
1967
+ borderBottomWidth: o.borderWidth || "0px",
1968
+ borderBottomStyle: "solid",
1969
+ borderBottomColor: o.borderColor || "",
1970
+ borderLeftWidth: o.borderWidth || "0px",
1971
+ borderLeftStyle: "solid",
1972
+ borderLeftColor: o.borderColor || ""
1973
+ }, delete o.borderColor, delete o.borderWidth), typeof o.fontWeight == "string" && (o.fontWeight = parseInt(o.fontWeight, 10) || 400)), e === "heading" && typeof o.fontWeight == "string" && (o.fontWeight = parseInt(o.fontWeight, 10) || 400), e === "divider" && (o.border = {
1974
+ borderTopWidth: o.borderTopWidth || "1px",
1975
+ borderTopStyle: o.borderTopStyle || "solid",
1976
+ borderTopColor: o.borderTopColor || "#cccccc"
1977
+ }, delete o.borderTopWidth, delete o.borderTopStyle, delete o.borderTopColor), e === "menu") {
1978
+ if (typeof o.items == "string") {
1979
+ try {
1980
+ const r = JSON.parse(o.items);
1981
+ o.menu = {
1982
+ items: r.map((i, n) => ({
1983
+ key: String(n),
1984
+ text: i.text || "",
1985
+ link: {
1986
+ name: "web",
1987
+ values: { href: i.href || "#", target: "_blank" }
1988
+ }
1989
+ }))
1990
+ };
1991
+ } catch {
1992
+ }
1993
+ delete o.items;
1994
+ }
1995
+ o.color && !o.linkColor && (o.linkColor = o.color, o.textColor = o.color);
1996
+ }
1997
+ return o;
1998
+ }
1999
+ function ue(e) {
2000
+ const t = e.values || {};
2001
+ t.backgroundImage && typeof t.backgroundImage == "object" && (t.backgroundImage = me(t.backgroundImage)), t.hideMobile === void 0 && (t.hideMobile = !1);
2002
+ const o = (e.columns || []).map((r) => Dt(r));
2003
+ return {
2004
+ id: e.id,
2005
+ cells: e.cells,
2006
+ columns: o,
2007
+ values: t
2008
+ };
2009
+ }
2010
+ function Dt(e) {
2011
+ const t = (e.contents || []).map((o) => {
2012
+ const r = o.type, i = St(r, o.values || {});
2013
+ return { id: o.id, type: r, values: i };
2014
+ });
2015
+ return {
2016
+ id: e.id,
2017
+ contents: t,
2018
+ values: e.values || {}
2019
+ };
2020
+ }
2021
+ function Pt(e) {
2022
+ const t = e.body || {}, o = t.values || {};
2023
+ if (typeof o.contentWidth == "number" && (o.contentWidth = o.contentWidth + "px"), o.backgroundImage && typeof o.backgroundImage == "object" && (o.backgroundImage = me(o.backgroundImage)), o.popupBackgroundImage && typeof o.popupBackgroundImage == "object" && (o.popupBackgroundImage = me(o.popupBackgroundImage)), o.linkStyle && typeof o.linkStyle == "object") {
2024
+ const a = o.linkStyle;
2025
+ delete a.inherit;
2026
+ }
2027
+ t.id || (t.id = "u_body");
2028
+ const r = (t.rows || []).map(ue), i = (t.headers || []).map(ue), n = (t.footers || []).map(ue);
2029
+ return {
2030
+ counters: At(r, i, n),
2031
+ body: {
2032
+ id: t.id || "u_body",
2033
+ rows: r,
2034
+ headers: i,
2035
+ footers: n,
2036
+ values: o
2037
+ },
2038
+ schemaVersion: 16
2039
+ };
2040
+ }
2041
+ function Et(e) {
2042
+ const t = { ...e.body.values };
2043
+ typeof t.contentWidth == "string" && (t.contentWidth = parseInt(t.contentWidth, 10) || 600), t.backgroundImage && typeof t.backgroundImage == "object" && (t.backgroundImage = pe(t.backgroundImage)), t.popupBackgroundImage && typeof t.popupBackgroundImage == "object" && (t.popupBackgroundImage = pe(t.popupBackgroundImage));
2044
+ const o = (r) => {
2045
+ const i = { ...r.values };
2046
+ return i.backgroundImage && typeof i.backgroundImage == "object" && (i.backgroundImage = pe(i.backgroundImage)), i.selectable === void 0 && (i.selectable = !0), i.draggable === void 0 && (i.draggable = !0), i.duplicatable === void 0 && (i.duplicatable = !0), i.deletable === void 0 && (i.deletable = !0), {
2047
+ id: r.id,
2048
+ cells: r.cells,
2049
+ columns: r.columns.map((n) => ({
2050
+ id: n.id,
2051
+ contents: n.contents.map((s) => {
2052
+ const a = s.type === "paragraph" ? "text" : s.type, c = Rt(a, { ...s.values });
2053
+ return c.selectable === void 0 && (c.selectable = !0), c.draggable === void 0 && (c.draggable = !0), c.duplicatable === void 0 && (c.duplicatable = !0), c.deletable === void 0 && (c.deletable = !0), { id: s.id, type: a, values: c };
2054
+ }),
2055
+ values: n.values
2056
+ })),
2057
+ values: i
2058
+ };
2059
+ };
2060
+ return {
2061
+ counters: e.counters,
2062
+ body: {
2063
+ id: e.body.id,
2064
+ rows: e.body.rows.map(o),
2065
+ headers: e.body.headers.map(o),
2066
+ footers: e.body.footers.map(o),
2067
+ values: t
2068
+ },
2069
+ schemaVersion: 12
2070
+ // Unlayer uses version 12
2071
+ };
2072
+ }
2073
+ function At(...e) {
2074
+ const t = {}, o = (r) => {
2075
+ const i = r.match(/^(.+?)(\d+)$/);
2076
+ if (i) {
2077
+ const n = i[1].replace(/_$/, ""), s = parseInt(i[2], 10);
2078
+ t[n] = Math.max(t[n] || 0, s);
2079
+ }
2080
+ };
2081
+ for (const r of e)
2082
+ for (const i of r) {
2083
+ o(i.id);
2084
+ for (const n of i.columns) {
2085
+ o(n.id);
2086
+ for (const s of n.contents)
2087
+ o(s.id);
2088
+ }
2089
+ }
2090
+ return t;
2091
+ }
2092
+ var Mt = Object.defineProperty, Lt = Object.getOwnPropertyDescriptor, se = (e, t, o, r) => {
2093
+ for (var i = r > 1 ? void 0 : r ? Lt(t, o) : t, n = e.length - 1, s; n >= 0; n--)
2094
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
2095
+ return r && i && Mt(t, o, i), i;
2096
+ };
2097
+ let j = class extends T {
2098
+ constructor() {
2099
+ super(...arguments), this.visible = !1, this.mergeTags = [], this.mergeDropdownOpen = !1, this._outsideClickHandler = (e) => {
2100
+ if (!this.mergeDropdownOpen) return;
2101
+ e.composedPath().includes(this) || (this.mergeDropdownOpen = !1);
2102
+ };
2103
+ }
2104
+ connectedCallback() {
2105
+ super.connectedCallback(), document.addEventListener("mousedown", this._outsideClickHandler, !0);
2106
+ }
2107
+ disconnectedCallback() {
2108
+ super.disconnectedCallback(), document.removeEventListener("mousedown", this._outsideClickHandler, !0);
2109
+ }
2110
+ exec(e, t) {
2111
+ document.execCommand(e, !1, t), this.requestUpdate();
2112
+ }
2113
+ isActive(e) {
2114
+ try {
2115
+ return document.queryCommandState(e);
2116
+ } catch {
2117
+ return !1;
2118
+ }
2119
+ }
2120
+ /** Position the toolbar above a given element */
2121
+ positionAbove(e) {
2122
+ const t = e.getBoundingClientRect();
2123
+ this.style.left = `${t.left + t.width / 2}px`, this.style.top = `${t.top - 8}px`, this.style.transform = "translate(-50%, -100%)", this.classList.add("visible");
2124
+ }
2125
+ hide() {
2126
+ this.classList.remove("visible"), this.mergeDropdownOpen = !1;
2127
+ }
2128
+ insertMergeTag(e) {
2129
+ document.execCommand("insertHTML", !1, e), this.mergeDropdownOpen = !1, this.requestUpdate();
2130
+ }
2131
+ toggleMergeDropdown(e) {
2132
+ e.preventDefault(), this.mergeDropdownOpen = !this.mergeDropdownOpen;
2133
+ }
2134
+ handleLink() {
2135
+ if (this.isActive("createLink"))
2136
+ this.exec("unlink");
2137
+ else {
2138
+ const e = prompt("Enter URL:");
2139
+ e && this.exec("createLink", e);
2140
+ }
2141
+ }
2142
+ render() {
2143
+ return d`
2144
+ <div class="toolbar" @mousedown=${(e) => e.preventDefault()}>
2145
+ <button class="btn ${this.isActive("bold") ? "active" : ""}"
2146
+ @click=${() => this.exec("bold")} title="Bold (Ctrl+B)">
2147
+ <strong>B</strong>
2148
+ </button>
2149
+ <button class="btn ${this.isActive("italic") ? "active" : ""}"
2150
+ @click=${() => this.exec("italic")} title="Italic (Ctrl+I)">
2151
+ <em>I</em>
2152
+ </button>
2153
+ <button class="btn ${this.isActive("underline") ? "active" : ""}"
2154
+ @click=${() => this.exec("underline")} title="Underline (Ctrl+U)">
2155
+ <u>U</u>
2156
+ </button>
2157
+ <button class="btn ${this.isActive("strikeThrough") ? "active" : ""}"
2158
+ @click=${() => this.exec("strikeThrough")} title="Strikethrough">
2159
+ <s>S</s>
2160
+ </button>
2161
+ <div class="separator"></div>
2162
+ <button class="btn ${this.isActive("createLink") ? "active" : ""}"
2163
+ @click=${this.handleLink} title="Link">
2164
+ <svg viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
2165
+ </button>
2166
+ <div class="separator"></div>
2167
+ <button class="btn" @click=${() => this.exec("justifyLeft")} title="Align Left">
2168
+ <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="18" y2="18"/></svg>
2169
+ </button>
2170
+ <button class="btn" @click=${() => this.exec("justifyCenter")} title="Align Center">
2171
+ <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg>
2172
+ </button>
2173
+ <button class="btn" @click=${() => this.exec("justifyRight")} title="Align Right">
2174
+ <svg viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="9" y1="12" x2="21" y2="12"/><line x1="6" y1="18" x2="21" y2="18"/></svg>
2175
+ </button>
2176
+ ${this.mergeTags.length > 0 ? d`
2177
+ <div class="separator"></div>
2178
+ <div class="merge-wrap">
2179
+ <button class="btn ${this.mergeDropdownOpen ? "active" : ""}"
2180
+ @click=${this.toggleMergeDropdown} title="Insert Merge Tag">
2181
+ <span style="font-size:13px;font-weight:700;letter-spacing:-0.5px;">{x}</span>
2182
+ </button>
2183
+ ${this.mergeDropdownOpen ? d`
2184
+ <div class="merge-dropdown" @mousedown=${(e) => e.preventDefault()}>
2185
+ ${this.mergeTags.map((e) => d`
2186
+ <div class="merge-group-name">${e.name}</div>
2187
+ ${Object.entries(e.mergeTags).map(([t, o]) => d`
2188
+ <button class="merge-item" @click=${() => this.insertMergeTag(o.value)}>
2189
+ ${o.name}
2190
+ </button>
2191
+ `)}
2192
+ `)}
2193
+ </div>
2194
+ ` : x}
2195
+ </div>
2196
+ ` : x}
2197
+ </div>
2198
+ `;
2199
+ }
2200
+ };
2201
+ j.styles = _`
2202
+ :host {
2203
+ position: fixed;
2204
+ z-index: 10000;
2205
+ display: none;
2206
+ }
2207
+ :host(.visible) {
2208
+ display: block;
2209
+ animation: fadeIn 0.15s ease;
2210
+ }
2211
+ @keyframes fadeIn {
2212
+ from { opacity: 0; transform: translateY(4px); }
2213
+ to { opacity: 1; transform: translateY(0); }
2214
+ }
2215
+ .toolbar {
2216
+ display: flex;
2217
+ align-items: center;
2218
+ gap: 2px;
2219
+ padding: 4px 6px;
2220
+ background: #1e293b;
2221
+ border-radius: 10px;
2222
+ box-shadow: 0 8px 24px rgba(0,0,0,0.2);
2223
+ }
2224
+ .btn {
2225
+ width: 32px;
2226
+ height: 32px;
2227
+ display: flex;
2228
+ align-items: center;
2229
+ justify-content: center;
2230
+ background: none;
2231
+ border: none;
2232
+ color: var(--me-grey-300);
2233
+ cursor: pointer;
2234
+ border-radius: 6px;
2235
+ font-size: 14px;
2236
+ font-weight: 600;
2237
+ transition: all 0.1s ease;
2238
+ padding: 0;
2239
+ }
2240
+ .btn:hover { background: rgba(255,255,255,0.08); color: white; }
2241
+ .btn.active { background: rgba(93,118,139,0.4); color: white; }
2242
+ .separator {
2243
+ width: 1px;
2244
+ height: 20px;
2245
+ background: #334155;
2246
+ margin: 0 2px;
2247
+ }
2248
+ .btn svg {
2249
+ width: 16px;
2250
+ height: 16px;
2251
+ stroke: currentColor;
2252
+ fill: none;
2253
+ stroke-width: 2;
2254
+ stroke-linecap: round;
2255
+ stroke-linejoin: round;
2256
+ }
2257
+ /* Merge tag dropdown */
2258
+ .merge-wrap { position: relative; }
2259
+ .merge-dropdown {
2260
+ position: absolute;
2261
+ top: calc(100% + 6px);
2262
+ left: 50%;
2263
+ transform: translateX(-50%);
2264
+ background: white;
2265
+ border: 1px solid var(--me-grey-200);
2266
+ border-radius: 8px;
2267
+ box-shadow: 0 8px 24px rgba(0,0,0,0.15);
2268
+ min-width: 200px;
2269
+ max-height: 260px;
2270
+ overflow-y: auto;
2271
+ z-index: 10;
2272
+ padding: 4px 0;
2273
+ }
2274
+ .merge-group-name {
2275
+ padding: 6px 12px 2px;
2276
+ font-size: 10px;
2277
+ font-weight: 700;
2278
+ text-transform: uppercase;
2279
+ letter-spacing: 0.05em;
2280
+ color: var(--me-grey-500);
2281
+ font-family: sans-serif;
2282
+ }
2283
+ .merge-item {
2284
+ display: block;
2285
+ width: 100%;
2286
+ padding: 6px 12px;
2287
+ background: none;
2288
+ border: none;
2289
+ color: var(--me-grey-600);
2290
+ font-size: 13px;
2291
+ text-align: left;
2292
+ cursor: pointer;
2293
+ white-space: nowrap;
2294
+ }
2295
+ .merge-item:hover {
2296
+ background: var(--me-blue-50);
2297
+ color: var(--me-grey-900);
2298
+ }
2299
+ `;
2300
+ se([
2301
+ v({ type: Boolean })
2302
+ ], j.prototype, "visible", 2);
2303
+ se([
2304
+ v({ attribute: !1 })
2305
+ ], j.prototype, "mergeTags", 2);
2306
+ se([
2307
+ E()
2308
+ ], j.prototype, "mergeDropdownOpen", 2);
2309
+ j = se([
2310
+ S("me-inline-toolbar")
2311
+ ], j);
2312
+ var zt = Object.defineProperty, Bt = Object.getOwnPropertyDescriptor, J = (e, t, o, r) => {
2313
+ for (var i = r > 1 ? void 0 : r ? Bt(t, o) : t, n = e.length - 1, s; n >= 0; n--)
2314
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
2315
+ return r && i && zt(t, o, i), i;
2316
+ };
2317
+ const Ae = /* @__PURE__ */ new Set(["text", "heading", "paragraph"]);
2318
+ function Me(e) {
2319
+ return e === "html" ? "html" : "text";
2320
+ }
2321
+ let M = class extends T {
2322
+ constructor() {
2323
+ super(...arguments), this.storeCtrl = new B(this, ["design", "selection", "viewMode"]), this.editing = !1, this.toolbar = null, this.editableEl = null, this.handleInlineInput = () => {
2324
+ this.showToolbar();
2325
+ }, this.handleInlineBlur = (e) => {
2326
+ const t = e.relatedTarget;
2327
+ t && (t.closest("me-inline-toolbar") || this.toolbar?.contains(t)) || setTimeout(() => {
2328
+ document.activeElement !== this.editableEl && !this.toolbar?.matches(":hover") && this.stopEditing();
2329
+ }, 150);
2330
+ }, this.handleOverlayBlur = (e) => {
2331
+ const t = e.relatedTarget;
2332
+ t && (t.closest("me-inline-toolbar") || this.toolbar?.contains(t)) || setTimeout(() => {
2333
+ document.activeElement !== this.editableEl && !this.toolbar?.matches(":hover") && this.stopEditing();
2334
+ }, 200);
2335
+ }, this.handleInlineKeydown = (e) => {
2336
+ e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), this.stopEditing());
2337
+ }, this.hoverUnsub = null;
2338
+ }
2339
+ set store(e) {
2340
+ this.storeCtrl.setStore(e);
2341
+ }
2342
+ get store() {
2343
+ return this.storeCtrl.store;
2344
+ }
2345
+ // ── Event handlers ───────────────────────────────────────
2346
+ handleClick(e) {
2347
+ e.stopPropagation(), this.editing || this.store.select(this.content.id);
2348
+ }
2349
+ handleDblClick(e) {
2350
+ e.stopPropagation(), Ae.has(this.content.type) && this.startEditing();
2351
+ }
2352
+ handleMouseEnter() {
2353
+ this.classList.add("hovered"), this.store.hover(this.content.id);
2354
+ }
2355
+ handleMouseLeave() {
2356
+ this.classList.remove("hovered"), this.store.hover(null);
2357
+ }
2358
+ handleDelete(e) {
2359
+ e.stopPropagation(), this.classList.add("removing");
2360
+ const t = this.content.id;
2361
+ this.addEventListener("animationend", () => this.store.removeContent(t), { once: !0 });
2362
+ }
2363
+ handleDuplicate(e) {
2364
+ e.stopPropagation();
2365
+ const t = this.store.duplicateContent(this.content.id);
2366
+ t && requestAnimationFrame(() => {
2367
+ const o = this.parentElement;
2368
+ if (o?.shadowRoot) {
2369
+ const r = o.shadowRoot.querySelector(`[data-content-id="${t.id}"]`);
2370
+ r?.classList.add("just-duplicated"), setTimeout(() => r?.classList.remove("just-duplicated"), 600);
2371
+ }
2372
+ });
2373
+ }
2374
+ /** Called from the drag handle only (not the whole block) */
2375
+ handleDragStart(e) {
2376
+ e.stopPropagation(), e.dataTransfer.setData("application/maileditor-content", this.content.id), e.dataTransfer.effectAllowed = "move", this.style.opacity = "0.4", this.style.transform = "scale(0.97)", D.startContentDrag(this.content.id, this);
2377
+ }
2378
+ handleDragEnd() {
2379
+ this.style.transform = "", D.reset();
2380
+ }
2381
+ // ── Inline editing ───────────────────────────────────────
2382
+ startEditing() {
2383
+ this.editing = !0, this.classList.add("editing"), this.storeCtrl.hostDisconnected();
2384
+ const e = this.getBoundingClientRect(), t = this.getInlineStyles(this.content.values), o = Me(this.content.type), r = this.content.values[o] || "", i = document.createElement("div");
2385
+ i.contentEditable = "true", i.innerHTML = r, Object.assign(i.style, {
2386
+ position: "fixed",
2387
+ left: e.left + "px",
2388
+ top: e.top + "px",
2389
+ width: e.width + "px",
2390
+ minHeight: e.height + "px",
2391
+ zIndex: "9999",
2392
+ background: "white",
2393
+ boxShadow: "0 0 0 2px #8b5cf6, 0 4px 16px rgba(0,0,0,0.15)",
2394
+ borderRadius: "2px",
2395
+ outline: "none",
2396
+ boxSizing: "border-box"
2397
+ }), t.split(";").forEach((a) => {
2398
+ const [c, l] = a.split(":").map((u) => u?.trim());
2399
+ c && l && i.style.setProperty(c, l);
2400
+ }), i.addEventListener("keydown", this.handleInlineKeydown), i.addEventListener("blur", this.handleOverlayBlur), document.body.appendChild(i), this.editableEl = i, i.focus();
2401
+ const n = window.getSelection(), s = document.createRange();
2402
+ s.selectNodeContents(i), s.collapse(!1), n?.removeAllRanges(), n?.addRange(s), this.showToolbar();
2403
+ }
2404
+ stopEditing() {
2405
+ this.editing && (this.saveInlineContent(), this.editableEl && (this.editableEl.removeEventListener("keydown", this.handleInlineKeydown), this.editableEl.removeEventListener("blur", this.handleOverlayBlur), this.editableEl.remove()), this.editing = !1, this.classList.remove("editing"), this.editableEl = null, this.storeCtrl.hostConnected(), this.requestUpdate(), this.toolbar?.hide());
2406
+ }
2407
+ saveInlineContent() {
2408
+ if (!this.editableEl) return;
2409
+ const e = this.editableEl.innerHTML, t = Me(this.content.type), o = this.content.values[t];
2410
+ e !== o && this.store.updateContentValues(this.content.id, { [t]: e });
2411
+ }
2412
+ showToolbar() {
2413
+ this.toolbar || (this.toolbar = document.createElement("me-inline-toolbar"), document.body.appendChild(this.toolbar)), this.toolbar.mergeTags = this.store.mergeTags, this.editableEl && this.toolbar.positionAbove(this.editableEl);
2414
+ }
2415
+ // ── Lifecycle ────────────────────────────────────────────
2416
+ connectedCallback() {
2417
+ super.connectedCallback(), this.classList.add("just-dropped"), setTimeout(() => this.classList.remove("just-dropped"), 400), this.store && (this.hoverUnsub = this.store.subscribeChannels(["hover"], () => {
2418
+ this.store.hoveredId === this.content?.id ? this.classList.add("hovered") : this.matches(":hover") || this.classList.remove("hovered");
2419
+ }));
2420
+ }
2421
+ disconnectedCallback() {
2422
+ super.disconnectedCallback(), this.editing && this.stopEditing(), this.hoverUnsub?.(), this.toolbar?.remove(), this.toolbar = null;
2423
+ }
2424
+ // ── Render ───────────────────────────────────────────────
2425
+ render() {
2426
+ if (!this.store) return d``;
2427
+ const e = this.store.selectedId === this.content.id, t = this.store.viewMode, o = !!this.content.values.hideDesktop, r = !!this.content.values.hideMobile, i = t === "desktop" && o || t === "mobile" && r, n = this.store.a11yIssueIds.has(this.content.id);
2428
+ this.classList.toggle("selected", e && !this.editing), this.classList.toggle("a11y-issue", n), this.classList.toggle("hidden-in-view", i), this.removeAttribute("draggable"), this.dataset.contentId = this.content.id, this.editing && !e && this.stopEditing();
2429
+ const s = this.toolRegistry.get(this.content.type);
2430
+ if (!s && this.toolRegistry.has(this.content.type))
2431
+ return this.toolRegistry.ensureLoaded(this.content.type).then(() => this.requestUpdate()), d`<div style="padding:16px;text-align:center;color:#9ca3af;font-size:13px;">Loading ${this.content.type}...</div>`;
2432
+ const a = o ? "Hidden on desktop" : r ? "Hidden on mobile" : "", c = Ae.has(this.content.type), l = d`
2433
+ <button class="drag-handle" draggable="true" title="Drag to move"
2434
+ @dragstart=${(g) => this.handleDragStart(g)}
2435
+ @dragend=${() => this.handleDragEnd()}
2436
+ @mousedown=${(g) => g.stopPropagation()}>
2437
+ <svg viewBox="0 0 16 16"><circle cx="5" cy="3" r="1.5"/><circle cx="11" cy="3" r="1.5"/><circle cx="5" cy="8" r="1.5"/><circle cx="11" cy="8" r="1.5"/><circle cx="5" cy="13" r="1.5"/><circle cx="11" cy="13" r="1.5"/></svg>
2438
+ </button>
2439
+ `, u = s?.renderer.renderEditor(this.content.values, {
2440
+ isSelected: e,
2441
+ isHovered: this.classList.contains("hovered"),
2442
+ columnWidth: 600,
2443
+ displayMode: "email"
2444
+ });
2445
+ if (this.editing && c)
2446
+ return d`${l}<div class="content-wrapper" style="visibility:hidden;">${u}</div>`;
2447
+ const h = this.content.values.containerPadding || "0px";
2448
+ return d`
2449
+ ${i ? d`<div class="hidden-badge">${a}</div>` : ""}
2450
+ ${l}
2451
+ <div class="action-bar">
2452
+ <span class="action-bar-label">${this.content.type}</span>
2453
+ <button class="action-btn" @click=${this.handleDuplicate} title="Duplicate">&#10697;</button>
2454
+ <button class="action-btn" @click=${this.handleDelete} title="Delete">&#10005;</button>
2455
+ </div>
2456
+ <div class="content-wrapper" style="padding:${h};"
2457
+ @click=${this.handleClick}
2458
+ @dblclick=${this.handleDblClick}
2459
+ @mouseenter=${this.handleMouseEnter}
2460
+ @mouseleave=${this.handleMouseLeave}>
2461
+ ${u ?? d`<div style="padding:10px;color:#999;font-style:italic;">Unknown tool: ${this.content.type}</div>`}
2462
+ </div>
2463
+ `;
2464
+ }
2465
+ /** Build inline CSS for the contenteditable area based on content values */
2466
+ getInlineStyles(e) {
2467
+ const t = [], o = (h, g = "") => {
2468
+ const b = e[h];
2469
+ return typeof b == "string" ? b : typeof b == "number" ? String(b) : g;
2470
+ };
2471
+ t.push(`padding:${o("containerPadding", "10px")}`);
2472
+ const r = o("backgroundColor");
2473
+ r && t.push(`background-color:${r}`);
2474
+ const i = o("color");
2475
+ i && t.push(`color:${i}`);
2476
+ const n = o("fontSize");
2477
+ n && t.push(`font-size:${n}`);
2478
+ const s = o("fontWeight");
2479
+ s && t.push(`font-weight:${s}`);
2480
+ const a = o("lineHeight", "140%");
2481
+ t.push(`line-height:${a}`);
2482
+ const c = o("textAlign");
2483
+ c && t.push(`text-align:${c}`);
2484
+ const l = o("letterSpacing");
2485
+ l && l !== "normal" && t.push(`letter-spacing:${l}`);
2486
+ const u = e.fontFamily;
2487
+ return typeof u == "string" ? t.push(`font-family:${u}`) : u && typeof u == "object" && t.push(`font-family:${u.value || "inherit"}`), t.push("word-break:break-word", "outline:none", "min-height:1em"), t.join(";");
2488
+ }
2489
+ };
2490
+ M.styles = _`
2491
+ :host {
2492
+ display: block; position: relative;
2493
+ transition: outline 0.15s ease, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
2494
+ }
2495
+ :host(.selected) { outline: 2px solid var(--me-primary); outline-offset: -1px; box-shadow: 0 0 0 4px var(--me-primary-10); }
2496
+ :host(.hovered:not(.selected)) { outline: 2px dashed var(--me-primary-30); outline-offset: -1px; box-shadow: 0 0 0 4px var(--me-primary-5); }
2497
+ :host(.hovered.a11y-issue) {
2498
+ outline: 2px solid var(--me-danger) !important; outline-offset: -1px;
2499
+ animation: a11yPulse 1s ease-in-out infinite;
2500
+ }
2501
+ @keyframes a11yPulse {
2502
+ 0%, 100% { box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.15); }
2503
+ 50% { box-shadow: 0 0 0 6px rgba(220, 38, 38, 0.25); }
2504
+ }
2505
+ :host(.editing) { outline: 2px solid #32769B; outline-offset: -1px; }
2506
+ :host(.just-dropped) {
2507
+ animation: dropIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
2508
+ }
2509
+ :host(.just-duplicated) {
2510
+ animation: duplicateFlash 0.5s ease-out;
2511
+ }
2512
+ @keyframes duplicateFlash {
2513
+ 0% { background: var(--me-primary-10, rgba(93,118,139,0.1)); transform: scale(1.01); }
2514
+ 100% { background: transparent; transform: scale(1); }
2515
+ }
2516
+ :host(.removing) {
2517
+ animation: fadeRemove 0.25s ease-out forwards;
2518
+ pointer-events: none;
2519
+ }
2520
+ @keyframes fadeRemove {
2521
+ to { opacity: 0; transform: scale(0.96) translateY(-4px); height: 0; margin: 0; padding: 0; overflow: hidden; }
2522
+ }
2523
+ :host(.hidden-in-view) { opacity: 0.3; position: relative; }
2524
+ .hidden-badge {
2525
+ position: absolute; top: 4px; right: 4px; z-index: 5;
2526
+ background: #f59e0b; color: white; font-size: 10px; font-weight: 600;
2527
+ padding: 2px 6px; border-radius: 3px; font-family: sans-serif;
2528
+ pointer-events: none;
2529
+ }
2530
+ @keyframes dropIn {
2531
+ 0% { opacity: 0; transform: scale(0.92) translateY(-8px); }
2532
+ 100% { opacity: 1; transform: scale(1) translateY(0); }
2533
+ }
2534
+ /* Drag handle — straddles the right border, vertically centered */
2535
+ .drag-handle {
2536
+ display: none;
2537
+ position: absolute;
2538
+ top: 50%;
2539
+ right: -12px;
2540
+ transform: translateY(-50%);
2541
+ width: 24px;
2542
+ height: 24px;
2543
+ background: var(--me-primary);
2544
+ color: white;
2545
+ border: 2px solid white;
2546
+ border-radius: 50%;
2547
+ cursor: grab;
2548
+ z-index: 20;
2549
+ align-items: center;
2550
+ justify-content: center;
2551
+ padding: 0;
2552
+ box-shadow: 0 1px 4px rgba(0,0,0,0.2);
2553
+ transition: background 0.15s ease, transform 0.15s ease;
2554
+ }
2555
+ .drag-handle:hover {
2556
+ background: var(--me-primary-dark);
2557
+ transform: translateY(-50%) scale(1.15);
2558
+ }
2559
+ .drag-handle:active { cursor: grabbing; }
2560
+ .drag-handle svg {
2561
+ width: 12px; height: 12px; fill: currentColor; stroke: none;
2562
+ }
2563
+ :host(.selected) .drag-handle,
2564
+ :host(.hovered) .drag-handle { display: flex; }
2565
+ :host(.editing) .drag-handle { display: none; }
2566
+ /* Action bar */
2567
+ .action-bar {
2568
+ display: none; position: absolute; top: -28px; right: 4px;
2569
+ background: var(--me-primary); border-radius: 6px; padding: 2px; gap: 2px; z-index: 10;
2570
+ align-items: center;
2571
+ }
2572
+ :host(.selected:not(.editing)) .action-bar { display: flex; }
2573
+ .action-bar-label {
2574
+ font-size: 10px; color: rgba(255,255,255,0.6); font-weight: 600;
2575
+ text-transform: uppercase; letter-spacing: 0.03em;
2576
+ padding: 0 4px;
2577
+ }
2578
+ .action-btn {
2579
+ background: none; border: none; color: white; cursor: pointer;
2580
+ padding: 2px 6px; font-size: 12px; line-height: 1; border-radius: 2px;
2581
+ }
2582
+ .action-btn:hover { background: rgba(255,255,255,0.15); }
2583
+ .content-wrapper { position: relative; cursor: default; box-sizing: border-box; }
2584
+ /* Reset paragraph margins in user content to match email rendering.
2585
+ Override inline margin from external editors (e.g. margin: 1.25em 0px). */
2586
+ .content-wrapper p { margin: 0 !important; }
2587
+ .inline-editable {
2588
+ outline: none; cursor: text; min-height: 1em;
2589
+ }
2590
+ .inline-editable:focus { outline: none; }
2591
+ `;
2592
+ J([
2593
+ v({ attribute: !1 })
2594
+ ], M.prototype, "content", 2);
2595
+ J([
2596
+ v({ attribute: !1 })
2597
+ ], M.prototype, "store", 1);
2598
+ J([
2599
+ v({ attribute: !1 })
2600
+ ], M.prototype, "toolRegistry", 2);
2601
+ J([
2602
+ E()
2603
+ ], M.prototype, "editing", 2);
2604
+ M = J([
2605
+ S("me-content-renderer")
2606
+ ], M);
2607
+ var Wt = Object.defineProperty, jt = Object.getOwnPropertyDescriptor, X = (e, t, o, r) => {
2608
+ for (var i = r > 1 ? void 0 : r ? jt(t, o) : t, n = e.length - 1, s; n >= 0; n--)
2609
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
2610
+ return r && i && Wt(t, o, i), i;
2611
+ };
2612
+ let L = class extends T {
2613
+ constructor() {
2614
+ super(...arguments), this.storeCtrl = new B(this, ["design"]), this.widthPercent = 100;
2615
+ }
2616
+ set store(e) {
2617
+ this.storeCtrl.setStore(e);
2618
+ }
2619
+ get store() {
2620
+ return this.storeCtrl.store;
2621
+ }
2622
+ render() {
2623
+ const e = this.column.values.padding || "0px", t = this.column.values.backgroundColor || "transparent", o = this.column.contents;
2624
+ return this.dataset.columnId = this.column.id, this.style.width = `${this.widthPercent}%`, this.style.padding = e, this.style.backgroundColor = t, this.style.verticalAlign = "top", this.style.boxSizing = "border-box", o.length === 0 ? d`
2625
+ <div class="empty-column" data-column-id=${this.column.id}>
2626
+ Drag content here
2627
+ </div>
2628
+ ` : d`
2629
+ ${ye(o, (r) => r.id, (r) => d`
2630
+ <me-content-renderer
2631
+ .content=${r}
2632
+ .store=${this.store}
2633
+ .toolRegistry=${this.toolRegistry}
2634
+ ></me-content-renderer>
2635
+ `)}
2636
+ `;
2637
+ }
2638
+ };
2639
+ L.styles = _`
2640
+ :host {
2641
+ display: block;
2642
+ min-height: 40px;
2643
+ position: relative;
2644
+ }
2645
+ .empty-column {
2646
+ display: flex;
2647
+ align-items: center;
2648
+ justify-content: center;
2649
+ min-height: 60px;
2650
+ border: 2px dashed var(--me-grey-300, #d1d5db);
2651
+ border-radius: 8px;
2652
+ color: var(--me-grey-400, #9ca3af);
2653
+ font-size: 12px;
2654
+ font-family: sans-serif;
2655
+ margin: 4px;
2656
+ animation: emptyBreath 3s ease-in-out infinite;
2657
+ }
2658
+ @keyframes emptyBreath {
2659
+ 0%, 100% { border-color: var(--me-grey-300, #d1d5db); opacity: 0.6; }
2660
+ 50% { border-color: var(--me-primary, #5d768b); opacity: 1; }
2661
+ }
2662
+ .drop-indicator {
2663
+ height: 3px;
2664
+ background: #3b82f6;
2665
+ border-radius: 2px;
2666
+ margin: 0 4px;
2667
+ opacity: 0;
2668
+ transition: opacity 0.15s ease;
2669
+ }
2670
+ .drop-indicator.active {
2671
+ opacity: 1;
2672
+ }
2673
+ `;
2674
+ X([
2675
+ v({ attribute: !1 })
2676
+ ], L.prototype, "column", 2);
2677
+ X([
2678
+ v({ attribute: !1 })
2679
+ ], L.prototype, "store", 1);
2680
+ X([
2681
+ v({ attribute: !1 })
2682
+ ], L.prototype, "toolRegistry", 2);
2683
+ X([
2684
+ v({ type: Number })
2685
+ ], L.prototype, "widthPercent", 2);
2686
+ L = X([
2687
+ S("me-column-renderer")
2688
+ ], L);
2689
+ var Ot = Object.defineProperty, Ht = Object.getOwnPropertyDescriptor, ae = (e, t, o, r) => {
2690
+ for (var i = r > 1 ? void 0 : r ? Ht(t, o) : t, n = e.length - 1, s; n >= 0; n--)
2691
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
2692
+ return r && i && Ot(t, o, i), i;
2693
+ };
2694
+ let O = class extends T {
2695
+ constructor() {
2696
+ super(...arguments), this.storeCtrl = new B(this, ["design", "viewMode"]), this.handleDragStart = (e) => {
2697
+ e.stopPropagation(), e.dataTransfer.setData("application/maileditor-row", this.row.id), e.dataTransfer.effectAllowed = "move", this.style.opacity = "0.4", this.style.transform = "scale(0.98)", D.startRowDrag(this.row.id, this);
2698
+ }, this.handleDragEnd = () => {
2699
+ this.style.transform = "", D.reset();
2700
+ };
2701
+ }
2702
+ set store(e) {
2703
+ this.storeCtrl.setStore(e);
2704
+ }
2705
+ get store() {
2706
+ return this.storeCtrl.store;
2707
+ }
2708
+ handleMoveUp(e) {
2709
+ e.stopPropagation();
2710
+ const t = this.store.getRowIndex(this.row.id);
2711
+ t > 0 && this.store.moveRow(t, t - 1);
2712
+ }
2713
+ handleMoveDown(e) {
2714
+ e.stopPropagation();
2715
+ const t = this.store.getRowIndex(this.row.id), o = this.store.getRows().length;
2716
+ t < o - 1 && this.store.moveRow(t, t + 1);
2717
+ }
2718
+ handleDuplicate(e) {
2719
+ e.stopPropagation(), this.store.duplicateRow(this.row.id);
2720
+ }
2721
+ handleDelete(e) {
2722
+ e.stopPropagation(), this.classList.add("removing");
2723
+ const t = this.row.id;
2724
+ this.addEventListener("animationend", () => this.store.removeRow(t), { once: !0 });
2725
+ }
2726
+ render() {
2727
+ if (!this.store) return d``;
2728
+ const { row: e, store: t, toolRegistry: o } = this, r = e.values.backgroundColor || "transparent", i = e.values.columnsBackgroundColor || "transparent", n = e.values.padding || "0px", s = e.cells.reduce((f, y) => f + y, 0), a = e.values.backgroundImage, c = typeof a == "object" && a?.url ? a.url : "";
2729
+ let l = "";
2730
+ if (c) {
2731
+ const f = a.repeat === !0 || a.repeat === "repeat" ? "repeat" : "no-repeat", y = a.cover === !0 ? "cover" : a.fullWidth === !0 ? "100% auto" : "auto", w = a.center !== !1 ? "center" : "top left";
2732
+ l = `background-image:url('${c}');background-size:${y};background-position:${w};background-repeat:${f};`;
2733
+ }
2734
+ const u = t.viewMode, h = !!e.values.hideDesktop, g = !!e.values.hideMobile, b = u === "desktop" && h || u === "mobile" && g, k = h ? "Hidden on desktop" : g ? "Hidden on mobile" : "";
2735
+ this.classList.toggle("hidden-in-view", b), this.removeAttribute("draggable"), this.dataset.rowId = e.id;
2736
+ const $ = t.getRowIndex(e.id), m = t.getRows().length;
2737
+ return d`
2738
+ ${b ? d`<div class="row-hidden-badge">${k}</div>` : ""}
2739
+ <button class="drag-handle" draggable="true" title="Drag to reorder"
2740
+ @dragstart=${this.handleDragStart}
2741
+ @dragend=${this.handleDragEnd}
2742
+ @mousedown=${(f) => f.stopPropagation()}>
2743
+ <svg viewBox="0 0 16 16"><circle cx="5" cy="3" r="1.5"/><circle cx="11" cy="3" r="1.5"/><circle cx="5" cy="8" r="1.5"/><circle cx="11" cy="8" r="1.5"/><circle cx="5" cy="13" r="1.5"/><circle cx="11" cy="13" r="1.5"/></svg>
2744
+ </button>
2745
+ <div class="action-bar">
2746
+ <span class="action-bar-label">Row</span>
2747
+ ${$ > 0 ? d`
2748
+ <button class="action-btn" @click=${this.handleMoveUp} title="Move Up">
2749
+ <svg viewBox="0 0 24 24"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg>
2750
+ </button>
2751
+ ` : ""}
2752
+ ${$ < m - 1 ? d`
2753
+ <button class="action-btn" @click=${this.handleMoveDown} title="Move Down">
2754
+ <svg viewBox="0 0 24 24"><path d="M12 5v14"/><path d="m19 12-7 7-7-7"/></svg>
2755
+ </button>
2756
+ ` : ""}
2757
+ <div class="action-separator"></div>
2758
+ <button class="action-btn danger" @click=${this.handleDelete} title="Delete Row">
2759
+ <svg viewBox="0 0 24 24"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
2760
+ </button>
2761
+ </div>
2762
+ <div
2763
+ class="row-wrapper"
2764
+ style="background-color:${r};padding:${n};${l}"
2765
+ >
2766
+ ${ye(e.columns, (f) => f.id, (f, y) => {
2767
+ const w = e.cells[y] / s * 100;
2768
+ return d`
2769
+ <me-column-renderer
2770
+ .column=${f}
2771
+ .store=${t}
2772
+ .toolRegistry=${o}
2773
+ .widthPercent=${w}
2774
+ style="background-color:${i};"
2775
+ ></me-column-renderer>
2776
+ `;
2777
+ })}
2778
+ </div>
2779
+ `;
2780
+ }
2781
+ };
2782
+ O.styles = _`
2783
+ :host {
2784
+ display: block;
2785
+ position: relative;
2786
+ transition: opacity 0.2s ease, transform 0.25s ease;
2787
+ }
2788
+ :host(.hidden-in-view) { opacity: 0.3; }
2789
+ :host(.removing) {
2790
+ animation: rowRemove 0.25s ease-out forwards;
2791
+ pointer-events: none;
2792
+ }
2793
+ @keyframes rowRemove {
2794
+ to { opacity: 0; transform: scaleY(0.8); height: 0; margin: 0; overflow: hidden; }
2795
+ }
2796
+ .row-hidden-badge {
2797
+ position: absolute; top: 4px; left: 4px; z-index: 5;
2798
+ background: #f59e0b; color: white; font-size: 10px; font-weight: 600;
2799
+ padding: 2px 6px; border-radius: 3px; font-family: sans-serif;
2800
+ pointer-events: none;
2801
+ }
2802
+ .row-wrapper {
2803
+ display: flex;
2804
+ flex-wrap: nowrap;
2805
+ position: relative;
2806
+ min-height: 30px;
2807
+ transition: box-shadow 0.15s ease;
2808
+ }
2809
+ :host(:hover) .row-wrapper {
2810
+ box-shadow: inset 0 0 0 2px var(--me-primary-30);
2811
+ }
2812
+
2813
+ /* Drop-in animation for newly added rows */
2814
+ :host(.just-added) {
2815
+ animation: rowSlideIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
2816
+ }
2817
+ @keyframes rowSlideIn {
2818
+ 0% { opacity: 0; transform: translateY(-10px) scaleY(0.95); }
2819
+ 100% { opacity: 1; transform: translateY(0) scaleY(1); }
2820
+ }
2821
+
2822
+ /* ── Drag handle — right side, vertically centered ── */
2823
+ .drag-handle {
2824
+ display: none;
2825
+ position: absolute;
2826
+ right: -12px;
2827
+ top: 50%;
2828
+ transform: translateY(-50%);
2829
+ width: 24px;
2830
+ height: 24px;
2831
+ background: var(--me-primary);
2832
+ color: white;
2833
+ border: 2px solid white;
2834
+ border-radius: 50%;
2835
+ cursor: grab;
2836
+ z-index: 20;
2837
+ align-items: center;
2838
+ justify-content: center;
2839
+ padding: 0;
2840
+ box-shadow: 0 1px 4px rgba(0,0,0,0.2);
2841
+ transition: background 0.15s ease, transform 0.15s ease;
2842
+ }
2843
+ .drag-handle:hover {
2844
+ background: var(--me-primary-dark);
2845
+ transform: translateY(-50%) scale(1.15);
2846
+ }
2847
+ .drag-handle:active { cursor: grabbing; }
2848
+ .drag-handle svg {
2849
+ width: 12px; height: 12px; fill: currentColor; stroke: none;
2850
+ }
2851
+ :host(:hover) .drag-handle { display: flex; }
2852
+
2853
+ /* ── Action bar — top-right ── */
2854
+ .action-bar {
2855
+ display: none;
2856
+ position: absolute;
2857
+ top: -28px;
2858
+ right: 4px;
2859
+ background: var(--me-primary);
2860
+ border-radius: 6px;
2861
+ padding: 2px;
2862
+ gap: 2px;
2863
+ z-index: 10;
2864
+ align-items: center;
2865
+ }
2866
+ :host(:hover) .action-bar { display: flex; }
2867
+ .action-bar-label {
2868
+ font-size: 10px; color: rgba(255,255,255,0.6); font-weight: 600;
2869
+ text-transform: uppercase; letter-spacing: 0.03em;
2870
+ padding: 0 4px;
2871
+ }
2872
+ .action-btn {
2873
+ background: none;
2874
+ border: none;
2875
+ color: white;
2876
+ cursor: pointer;
2877
+ padding: 2px 6px;
2878
+ font-size: 12px;
2879
+ line-height: 1;
2880
+ border-radius: 2px;
2881
+ display: flex;
2882
+ align-items: center;
2883
+ justify-content: center;
2884
+ }
2885
+ .action-btn:hover { background: rgba(255,255,255,0.15); }
2886
+ .action-btn.danger:hover { background: rgba(239,68,68,0.5); }
2887
+ .action-btn svg {
2888
+ width: 14px;
2889
+ height: 14px;
2890
+ stroke: currentColor;
2891
+ fill: none;
2892
+ stroke-width: 2;
2893
+ stroke-linecap: round;
2894
+ stroke-linejoin: round;
2895
+ }
2896
+ .action-separator {
2897
+ width: 1px;
2898
+ background: rgba(255,255,255,0.3);
2899
+ margin: 2px 1px;
2900
+ }
2901
+ `;
2902
+ ae([
2903
+ v({ attribute: !1 })
2904
+ ], O.prototype, "row", 2);
2905
+ ae([
2906
+ v({ attribute: !1 })
2907
+ ], O.prototype, "store", 1);
2908
+ ae([
2909
+ v({ attribute: !1 })
2910
+ ], O.prototype, "toolRegistry", 2);
2911
+ O = ae([
2912
+ S("me-row-renderer")
2913
+ ], O);
2914
+ var Vt = Object.defineProperty, Ft = Object.getOwnPropertyDescriptor, Z = (e, t, o, r) => {
2915
+ for (var i = r > 1 ? void 0 : r ? Ft(t, o) : t, n = e.length - 1, s; n >= 0; n--)
2916
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
2917
+ return r && i && Vt(t, o, i), i;
2918
+ };
2919
+ let z = class extends T {
2920
+ constructor() {
2921
+ super(...arguments), this.storeCtrl = new B(this, ["design", "viewMode"]), this.quickAddIndex = null, this.quickAddPos = { x: 0, y: 0 }, this.boundCloseQuickAdd = this.closeQuickAdd.bind(this);
2922
+ }
2923
+ set store(e) {
2924
+ this.storeCtrl.setStore(e);
2925
+ }
2926
+ get store() {
2927
+ return this.storeCtrl.store;
2928
+ }
2929
+ connectedCallback() {
2930
+ super.connectedCallback(), document.addEventListener("click", this.boundCloseQuickAdd);
2931
+ }
2932
+ disconnectedCallback() {
2933
+ super.disconnectedCallback(), document.removeEventListener("click", this.boundCloseQuickAdd);
2934
+ }
2935
+ handleCanvasClick() {
2936
+ this.store.select(null);
2937
+ }
2938
+ /** Flash the canvas border to confirm undo/redo */
2939
+ flashUndo() {
2940
+ const e = this.shadowRoot?.querySelector(".canvas-body");
2941
+ e && (e.classList.add("undo-flash"), e.addEventListener("animationend", () => e.classList.remove("undo-flash"), { once: !0 }));
2942
+ }
2943
+ setViewMode(e) {
2944
+ this.store.setViewMode(e);
2945
+ }
2946
+ // ── Quick-add methods ──────────────────────────────────────
2947
+ openQuickAdd(e, t) {
2948
+ e.stopPropagation();
2949
+ const r = e.currentTarget.getBoundingClientRect();
2950
+ this.quickAddPos = {
2951
+ x: r.left + r.width / 2,
2952
+ y: r.bottom + 6
2953
+ }, this.quickAddIndex = t;
2954
+ }
2955
+ closeQuickAdd() {
2956
+ this.quickAddIndex !== null && (this.quickAddIndex = null);
2957
+ }
2958
+ async quickAddTool(e, t) {
2959
+ await this.toolRegistry.ensureLoaded(e);
2960
+ const o = this.toolRegistry.getDefaultValues(e), r = this.store.createRow([1]), n = this.store.addRow(r, t).columns[0].id, s = this.store.createContent(e, o);
2961
+ this.store.addContent(n, s), this.quickAddIndex = null;
2962
+ }
2963
+ quickAddLayout(e, t) {
2964
+ const o = this.store.createRow(e);
2965
+ this.store.addRow(o, t), this.quickAddIndex = null;
2966
+ }
2967
+ renderRowGap(e) {
2968
+ return d`
2969
+ <div class="row-gap">
2970
+ <button
2971
+ class="add-btn"
2972
+ @click=${(t) => this.openQuickAdd(t, e)}
2973
+ title="Add row"
2974
+ >+</button>
2975
+ </div>
2976
+ `;
2977
+ }
2978
+ renderQuickAddMenu() {
2979
+ if (this.quickAddIndex === null) return "";
2980
+ const e = this.quickAddIndex, t = this.quickAddPos.x, o = this.quickAddPos.y;
2981
+ return d`
2982
+ <div
2983
+ class="quick-add-menu"
2984
+ style="position:fixed;left:${t}px;top:${o}px;transform:translateX(-50%)"
2985
+ @click=${(i) => i.stopPropagation()}
2986
+ >
2987
+ <div class="quick-tools">
2988
+ <button @click=${() => this.quickAddTool("text", e)} title="Text">T</button>
2989
+ <button @click=${() => this.quickAddTool("heading", e)} title="Heading">H</button>
2990
+ <button @click=${() => this.quickAddTool("image", e)} title="Image">
2991
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
2992
+ </button>
2993
+ <button @click=${() => this.quickAddTool("button", e)} title="Button">
2994
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="10" rx="3"/></svg>
2995
+ </button>
2996
+ <button @click=${() => this.quickAddTool("divider", e)} title="Divider">
2997
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="12" x2="21" y2="12"/></svg>
2998
+ </button>
2999
+ </div>
3000
+ <div class="quick-separator"></div>
3001
+ <div class="quick-layouts">
3002
+ ${[
3003
+ { cells: [1], label: "100%" },
3004
+ { cells: [1, 1], label: "50/50" },
3005
+ { cells: [1, 1, 1], label: "33/33/33" },
3006
+ { cells: [2, 1], label: "66/33" },
3007
+ { cells: [1, 2], label: "33/66" },
3008
+ { cells: [1, 1, 1, 1], label: "25x4" }
3009
+ ].map(
3010
+ (i) => {
3011
+ const n = i.cells.reduce((s, a) => s + a, 0);
3012
+ return d`
3013
+ <button
3014
+ @click=${() => this.quickAddLayout(i.cells, e)}
3015
+ title=${i.label}
3016
+ >
3017
+ ${i.cells.map((s) => d`<div class="ql-col" style="--col-flex:${s / n}"></div>`)}
3018
+ </button>
3019
+ `;
3020
+ }
3021
+ )}
3022
+ </div>
3023
+ </div>
3024
+ `;
3025
+ }
3026
+ render() {
3027
+ const e = this.store.getRows(), t = this.store.getBodyValues(), o = t.contentWidth || "600px", r = t.backgroundColor || "#e7e7e7", i = this.store.viewMode, n = i === "mobile" ? "375px" : o;
3028
+ return d`
3029
+ <div class="view-toggle">
3030
+ <button
3031
+ class="view-btn ${i === "desktop" ? "active" : ""}"
3032
+ @click=${() => this.setViewMode("desktop")}
3033
+ >Desktop</button>
3034
+ <button
3035
+ class="view-btn ${i === "mobile" ? "active" : ""}"
3036
+ @click=${() => this.setViewMode("mobile")}
3037
+ >Mobile</button>
3038
+ </div>
3039
+
3040
+ <div class="canvas-bg" style="background-color:${r};" @click=${this.handleCanvasClick}>
3041
+ <div
3042
+ class="canvas-body"
3043
+ style="max-width:${n};"
3044
+ >
3045
+ ${e.length === 0 ? d`
3046
+ <div class="empty-canvas">
3047
+ <div class="empty-icon">
3048
+ <svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
3049
+ </div>
3050
+ <div class="empty-title">Start building your email</div>
3051
+ <div class="empty-sub">Drag elements from the sidebar, use the + button, or start with a template</div>
3052
+ <div class="empty-actions">
3053
+ <button class="empty-action primary" @click=${() => this.quickAddTool("heading", 0)}>Add Heading</button>
3054
+ <button class="empty-action" @click=${() => this.quickAddTool("text", 0)}>Add Text</button>
3055
+ <button class="empty-action" @click=${() => this.quickAddLayout([1, 1], 0)}>2 Columns</button>
3056
+ </div>
3057
+ <div class="empty-shortcut">
3058
+ <kbd>Ctrl</kbd>+<kbd>Z</kbd> undo · <kbd>Ctrl</kbd>+<kbd>D</kbd> duplicate · <kbd>Del</kbd> remove
3059
+ </div>
3060
+ </div>
3061
+ ${this.renderRowGap(0)}
3062
+ ` : d`
3063
+ ${this.renderRowGap(0)}
3064
+ ${ye(e, (s) => s.id, (s, a) => d`
3065
+ <me-row-renderer
3066
+ .row=${s}
3067
+ .store=${this.store}
3068
+ .toolRegistry=${this.toolRegistry}
3069
+ ></me-row-renderer>
3070
+ ${this.renderRowGap(a + 1)}
3071
+ `)}
3072
+ `}
3073
+ </div>
3074
+ </div>
3075
+ ${this.renderQuickAddMenu()}
3076
+ `;
3077
+ }
3078
+ };
3079
+ z.styles = _`
3080
+ :host {
3081
+ display: block;
3082
+ flex: 1;
3083
+ overflow-y: auto;
3084
+ background: var(--me-grey-100);
3085
+ }
3086
+ /* Full-width body background — mirrors the email <body> */
3087
+ .canvas-bg {
3088
+ min-height: 100%;
3089
+ padding: 0 20px 20px;
3090
+ }
3091
+ .canvas-body {
3092
+ margin: 0 auto;
3093
+ background: #ffffff;
3094
+ min-height: 200px;
3095
+ position: relative;
3096
+ transition: max-width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
3097
+ }
3098
+ .canvas-body.undo-flash {
3099
+ animation: undoFlash 0.3s ease-out;
3100
+ }
3101
+ @keyframes undoFlash {
3102
+ 0% { box-shadow: inset 0 0 0 2px var(--me-primary-30, rgba(93,118,139,0.3)); }
3103
+ 100% { box-shadow: none; }
3104
+ }
3105
+ .empty-canvas {
3106
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
3107
+ min-height: 300px; color: var(--me-grey-400); font-family: sans-serif; gap: 12px;
3108
+ padding: 40px 20px; text-align: center;
3109
+ }
3110
+ .empty-icon {
3111
+ width: 56px; height: 56px; border-radius: 50%;
3112
+ background: var(--me-primary-5, rgba(93,118,139,0.05));
3113
+ display: flex; align-items: center; justify-content: center;
3114
+ animation: emptyPulse 3s ease-in-out infinite;
3115
+ }
3116
+ .empty-icon svg { width: 24px; height: 24px; stroke: var(--me-primary); fill: none; stroke-width: 1.5; }
3117
+ @keyframes emptyPulse {
3118
+ 0%, 100% { opacity: 0.6; transform: scale(0.95); }
3119
+ 50% { opacity: 1; transform: scale(1); }
3120
+ }
3121
+ .empty-title { font-size: 14px; font-weight: 600; color: var(--me-grey-600); }
3122
+ .empty-sub { font-size: 12px; color: var(--me-grey-400); max-width: 240px; line-height: 1.5; }
3123
+ .empty-actions { display: flex; gap: 8px; margin-top: 8px; }
3124
+ .empty-action {
3125
+ padding: 6px 14px; border-radius: var(--me-radius, 8px); border: 1px solid var(--me-grey-200);
3126
+ background: white; color: var(--me-grey-600); font-size: 11px; font-weight: 600;
3127
+ cursor: pointer; transition: all var(--me-transition, 150ms ease); font-family: sans-serif;
3128
+ }
3129
+ .empty-action:hover { border-color: var(--me-primary); color: var(--me-primary); background: var(--me-primary-5); }
3130
+ .empty-action.primary { background: var(--me-primary); color: white; border-color: var(--me-primary); }
3131
+ .empty-action.primary:hover { background: var(--me-primary-dark, #4a6070); }
3132
+ .empty-shortcut {
3133
+ font-size: 10px; color: var(--me-grey-300); margin-top: 12px; font-family: sans-serif;
3134
+ }
3135
+ .empty-shortcut kbd {
3136
+ background: var(--me-grey-100); border: 1px solid var(--me-grey-200);
3137
+ border-radius: 3px; padding: 1px 4px; font-size: 10px; font-family: monospace;
3138
+ }
3139
+ .view-toggle {
3140
+ display: flex;
3141
+ justify-content: center;
3142
+ margin: 8px 0 12px;
3143
+ gap: 4px;
3144
+ }
3145
+ .view-btn {
3146
+ padding: 6px 16px;
3147
+ border: 1px solid var(--me-grey-200);
3148
+ background: white;
3149
+ cursor: pointer;
3150
+ font-size: 11px;
3151
+ font-weight: 600;
3152
+ font-family: sans-serif;
3153
+ color: var(--me-grey-700);
3154
+ transition: all 0.15s ease;
3155
+ }
3156
+ .view-btn:hover { background: var(--me-grey-50); }
3157
+ .view-btn:first-child { border-radius: 8px 0 0 8px; }
3158
+ .view-btn:last-child { border-radius: 0 8px 8px 0; }
3159
+ .view-btn.active {
3160
+ background: var(--me-primary);
3161
+ border-color: var(--me-primary);
3162
+ color: white;
3163
+ }
3164
+
3165
+ /* ── Quick-add row gaps ─────────────────────────── */
3166
+ .row-gap {
3167
+ height: 20px;
3168
+ position: relative;
3169
+ display: flex;
3170
+ align-items: center;
3171
+ justify-content: center;
3172
+ z-index: 2;
3173
+ }
3174
+ .row-gap .add-btn {
3175
+ opacity: 0;
3176
+ width: 24px;
3177
+ height: 24px;
3178
+ border-radius: 50%;
3179
+ border: none;
3180
+ background: var(--me-primary);
3181
+ color: white;
3182
+ font-size: 16px;
3183
+ line-height: 1;
3184
+ cursor: pointer;
3185
+ display: flex;
3186
+ align-items: center;
3187
+ justify-content: center;
3188
+ transition: opacity 0.15s, transform 0.15s;
3189
+ padding: 0;
3190
+ font-family: sans-serif;
3191
+ }
3192
+ .row-gap:hover .add-btn {
3193
+ opacity: 1;
3194
+ }
3195
+ .row-gap .add-btn:hover {
3196
+ transform: scale(1.1);
3197
+ }
3198
+ .row-gap::before {
3199
+ content: '';
3200
+ position: absolute;
3201
+ left: 5%;
3202
+ right: 5%;
3203
+ top: 50%;
3204
+ height: 2px;
3205
+ background: transparent;
3206
+ border-radius: 1px;
3207
+ transition: background 0.2s ease;
3208
+ }
3209
+ .row-gap:hover::before {
3210
+ background: rgba(93, 118, 139, 0.2);
3211
+ }
3212
+
3213
+ /* ── Quick-add floating menu ───────────────────── */
3214
+ .quick-add-menu {
3215
+ background: white;
3216
+ border: 1px solid var(--me-grey-200);
3217
+ border-radius: 10px;
3218
+ box-shadow: 0 4px 20px rgba(0,0,0,0.12);
3219
+ padding: 8px;
3220
+ z-index: 100;
3221
+ animation: qaFadeIn 0.12s ease-out;
3222
+ font-family: sans-serif;
3223
+ }
3224
+ @keyframes qaFadeIn {
3225
+ from { opacity: 0; transform: scale(0.95); }
3226
+ to { opacity: 1; transform: scale(1); }
3227
+ }
3228
+ .quick-tools {
3229
+ display: flex;
3230
+ gap: 4px;
3231
+ }
3232
+ .quick-tools button {
3233
+ width: 36px;
3234
+ height: 36px;
3235
+ border: none;
3236
+ border-radius: 8px;
3237
+ background: var(--me-grey-50);
3238
+ cursor: pointer;
3239
+ font-size: 14px;
3240
+ font-family: sans-serif;
3241
+ color: var(--me-grey-700);
3242
+ display: flex;
3243
+ align-items: center;
3244
+ justify-content: center;
3245
+ transition: background 0.12s, color 0.12s;
3246
+ }
3247
+ .quick-tools button:hover {
3248
+ background: var(--me-primary);
3249
+ color: white;
3250
+ }
3251
+ .quick-separator {
3252
+ height: 1px;
3253
+ background: var(--me-grey-200);
3254
+ margin: 6px 0;
3255
+ }
3256
+ .quick-layouts {
3257
+ display: flex;
3258
+ gap: 4px;
3259
+ }
3260
+ .quick-layouts button {
3261
+ flex: 1;
3262
+ height: 28px;
3263
+ border: 1px solid var(--me-grey-200);
3264
+ border-radius: 6px;
3265
+ background: white;
3266
+ cursor: pointer;
3267
+ padding: 3px 4px;
3268
+ display: flex;
3269
+ gap: 2px;
3270
+ align-items: stretch;
3271
+ transition: border-color 0.12s;
3272
+ }
3273
+ .quick-layouts button:hover {
3274
+ border-color: var(--me-primary);
3275
+ }
3276
+ .quick-layouts button .ql-col {
3277
+ background: var(--me-grey-300);
3278
+ border-radius: 2px;
3279
+ flex-grow: var(--col-flex, 1);
3280
+ transition: background 0.12s;
3281
+ }
3282
+ .quick-layouts button:hover .ql-col {
3283
+ background: var(--me-primary);
3284
+ }
3285
+ `;
3286
+ Z([
3287
+ v({ attribute: !1 })
3288
+ ], z.prototype, "store", 1);
3289
+ Z([
3290
+ v({ attribute: !1 })
3291
+ ], z.prototype, "toolRegistry", 2);
3292
+ Z([
3293
+ E()
3294
+ ], z.prototype, "quickAddIndex", 2);
3295
+ Z([
3296
+ E()
3297
+ ], z.prototype, "quickAddPos", 2);
3298
+ z = Z([
3299
+ S("me-editor-canvas")
3300
+ ], z);
3301
+ var Ut = Object.defineProperty, qt = Object.getOwnPropertyDescriptor, je = (e, t, o, r) => {
3302
+ for (var i = r > 1 ? void 0 : r ? qt(t, o) : t, n = e.length - 1, s; n >= 0; n--)
3303
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
3304
+ return r && i && Ut(t, o, i), i;
3305
+ };
3306
+ const he = [
3307
+ // System / email-safe fonts
3308
+ { label: "Arial", value: "arial,helvetica,sans-serif", url: "" },
3309
+ { label: "Helvetica", value: "helvetica,sans-serif", url: "" },
3310
+ { label: "Georgia", value: "georgia,serif", url: "" },
3311
+ { label: "Times New Roman", value: "'times new roman',times,serif", url: "" },
3312
+ { label: "Trebuchet MS", value: "trebuchet ms,helvetica,sans-serif", url: "" },
3313
+ { label: "Verdana", value: "verdana,geneva,sans-serif", url: "" },
3314
+ { label: "Courier New", value: "'courier new',courier,monospace", url: "" },
3315
+ // Google Fonts
3316
+ { label: "Lato", value: "'Lato',sans-serif", url: "https://fonts.googleapis.com/css?family=Lato:400,700" },
3317
+ { label: "Montserrat", value: "'Montserrat',sans-serif", url: "https://fonts.googleapis.com/css?family=Montserrat:400,700" },
3318
+ { label: "Old Standard TT", value: "'Old Standard TT',serif", url: "https://fonts.googleapis.com/css?family=Old+Standard+TT:400,700" },
3319
+ { label: "Open Sans", value: "'Open Sans',sans-serif", url: "https://fonts.googleapis.com/css?family=Open+Sans:400,700" },
3320
+ { label: "Pacifico", value: "'Pacifico',cursive", url: "https://fonts.googleapis.com/css?family=Pacifico:400" },
3321
+ { label: "Playfair Display", value: "'Playfair Display',serif", url: "https://fonts.googleapis.com/css?family=Playfair+Display:400,700" },
3322
+ { label: "Raleway", value: "'Raleway',sans-serif", url: "https://fonts.googleapis.com/css?family=Raleway:400,700" },
3323
+ { label: "Roboto", value: "'Roboto',sans-serif", url: "https://fonts.googleapis.com/css?family=Roboto:400,700" },
3324
+ { label: "Rubik", value: "'Rubik',sans-serif", url: "https://fonts.googleapis.com/css?family=Rubik:400,700" },
3325
+ { label: "Source Sans Pro", value: "'Source Sans Pro',sans-serif", url: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700" }
3326
+ ];
3327
+ let K = class extends T {
3328
+ constructor() {
3329
+ super(...arguments), this.storeCtrl = new B(this, ["design"]);
3330
+ }
3331
+ set store(e) {
3332
+ this.storeCtrl.setStore(e);
3333
+ }
3334
+ get store() {
3335
+ return this.storeCtrl.store;
3336
+ }
3337
+ /** Scroll to preheader field, focus it, and pulse highlight */
3338
+ highlightPreheader() {
3339
+ const e = this.shadowRoot?.getElementById("preheader-field"), t = this.shadowRoot?.getElementById("preheader-input");
3340
+ e && (e.scrollIntoView({ behavior: "smooth", block: "center" }), e.classList.add("highlight"), t?.focus(), setTimeout(() => e.classList.remove("highlight"), 5e3));
3341
+ }
3342
+ update_(e, t) {
3343
+ this.store.updateBodyValues({ [e]: t });
3344
+ }
3345
+ updateLinkStyle(e, t) {
3346
+ const o = this.store.getBodyValues().linkStyle;
3347
+ this.store.updateBodyValues({ linkStyle: { ...o, [e]: t } });
3348
+ }
3349
+ updateFontFamily(e) {
3350
+ const t = he.find((r) => r.value === e), o = { label: t?.label || e, value: e };
3351
+ if (t?.url) {
3352
+ o.url = t.url;
3353
+ const r = `emabuild-font-${e.replace(/[^a-z]/gi, "")}`;
3354
+ if (!document.getElementById(r)) {
3355
+ const i = document.createElement("link");
3356
+ i.id = r, i.rel = "stylesheet", i.href = t.url, document.head.appendChild(i);
3357
+ }
3358
+ }
3359
+ this.store.updateBodyValues({ fontFamily: o });
3360
+ }
3361
+ render() {
3362
+ const e = this.store.getBodyValues();
3363
+ return d`
3364
+ ${this.renderColorField("Background Color", e.backgroundColor || "#e7e7e7", (t) => this.update_("backgroundColor", t))}
3365
+
3366
+ <p class="section-title" style="margin-top:16px;">Content</p>
3367
+ <div class="field">
3368
+ <label class="field-label">Content Width (px)</label>
3369
+ <input class="input" type="number" .value=${parseInt(e.contentWidth || "600")} min="320" max="960" step="10"
3370
+ @change=${(t) => this.update_("contentWidth", t.target.value + "px")} />
3371
+ </div>
3372
+ <div class="field">
3373
+ <label class="field-label">Content Align</label>
3374
+ <div class="align-group">
3375
+ ${["left", "center", "right"].map((t) => d`
3376
+ <button class="align-btn ${e.contentAlign === t ? "active" : ""}"
3377
+ @click=${() => this.update_("contentAlign", t)}>${t}</button>
3378
+ `)}
3379
+ </div>
3380
+ </div>
3381
+
3382
+ <p class="section-title" style="margin-top:16px;">Typography</p>
3383
+ <div class="field">
3384
+ <label class="field-label">Font Family</label>
3385
+ <select class="input" @change=${(t) => this.updateFontFamily(t.target.value)}>
3386
+ <optgroup label="System Fonts">
3387
+ ${he.filter((t) => !t.url).map((t) => d`<option value=${t.value} ?selected=${e.fontFamily?.value === t.value}>${t.label}</option>`)}
3388
+ </optgroup>
3389
+ <optgroup label="Google Fonts">
3390
+ ${he.filter((t) => !!t.url).map((t) => d`<option value=${t.value} ?selected=${e.fontFamily?.value === t.value}>${t.label}</option>`)}
3391
+ </optgroup>
3392
+ </select>
3393
+ </div>
3394
+ ${this.renderColorField("Text Color", e.textColor || "#000000", (t) => this.update_("textColor", t))}
3395
+
3396
+ <p class="section-title" style="margin-top:16px;">Links</p>
3397
+ ${this.renderColorField("Link Color", e.linkStyle?.linkColor || "#0000ee", (t) => this.updateLinkStyle("linkColor", t))}
3398
+ <div class="field">
3399
+ <label style="display:flex;align-items:center;gap:8px;font-size:12px;color:#6b7280;cursor:pointer;">
3400
+ <input type="checkbox" .checked=${e.linkStyle?.linkUnderline ?? !0}
3401
+ @change=${(t) => this.updateLinkStyle("linkUnderline", t.target.checked)} />
3402
+ Underline Links
3403
+ </label>
3404
+ </div>
3405
+
3406
+ <p class="section-title" style="margin-top:16px;">Email</p>
3407
+ <div class="field" id="preheader-field">
3408
+ <label class="field-label">Preheader Text</label>
3409
+ <textarea class="input" id="preheader-input" .value=${e.preheaderText || ""} placeholder="Preview text shown in inbox..."
3410
+ style="min-height:60px;resize:vertical;font-family:inherit;"
3411
+ @change=${(t) => this.update_("preheaderText", t.target.value)}></textarea>
3412
+ </div>
3413
+ `;
3414
+ }
3415
+ /** Reusable color field (swatch + hex input) */
3416
+ renderColorField(e, t, o) {
3417
+ return d`
3418
+ <div class="field">
3419
+ <label class="field-label">${e}</label>
3420
+ <div class="color-row">
3421
+ <input class="color-swatch" type="color" .value=${t} @input=${(r) => o(r.target.value)} />
3422
+ <input class="input" type="text" .value=${t} style="flex:1;" @change=${(r) => o(r.target.value)} />
3423
+ </div>
3424
+ </div>
3425
+ `;
3426
+ }
3427
+ };
3428
+ K.styles = _`
3429
+ :host { display: block; }
3430
+ .section-title {
3431
+ font-size: 11px; font-weight: 600; text-transform: uppercase;
3432
+ color: #9ca3af; letter-spacing: 0.05em; margin: 0 0 8px 0;
3433
+ }
3434
+ .field { margin-bottom: 12px; }
3435
+ .field-label { display: block; font-size: 12px; color: #6b7280; margin-bottom: 4px; }
3436
+ .input {
3437
+ width: 100%; padding: 5px 8px; border: 1px solid #d1d5db; border-radius: 4px;
3438
+ font-size: 12px; box-sizing: border-box;
3439
+ }
3440
+ .input:focus { outline: none; border-color: #3b82f6; }
3441
+ .color-row { display: flex; gap: 6px; align-items: center; }
3442
+ .color-swatch {
3443
+ width: 32px; height: 32px; border: 1px solid #d1d5db; border-radius: 4px;
3444
+ padding: 0; cursor: pointer;
3445
+ }
3446
+ .align-group { display: flex; gap: 2px; }
3447
+ .align-btn {
3448
+ flex: 1; padding: 5px; border: 1px solid #d1d5db; background: white;
3449
+ border-radius: 4px; cursor: pointer; font-size: 11px; color: #6b7280;
3450
+ }
3451
+ .align-btn.active { border-color: #3b82f6; background: #eff6ff; color: #3b82f6; }
3452
+ .field.highlight textarea {
3453
+ animation: preheaderPulse 0.6s ease-in-out 4;
3454
+ border-color: #dc2626;
3455
+ }
3456
+ @keyframes preheaderPulse {
3457
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0); }
3458
+ 50% { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.2); }
3459
+ }
3460
+ `;
3461
+ je([
3462
+ v({ attribute: !1 })
3463
+ ], K.prototype, "store", 1);
3464
+ K = je([
3465
+ S("me-body-settings")
3466
+ ], K);
3467
+ var Nt = Object.defineProperty, Gt = Object.getOwnPropertyDescriptor, le = (e, t, o, r) => {
3468
+ for (var i = r > 1 ? void 0 : r ? Gt(t, o) : t, n = e.length - 1, s; n >= 0; n--)
3469
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
3470
+ return r && i && Nt(t, o, i), i;
3471
+ };
3472
+ const Le = {
3473
+ text: { label: "Text", tools: ["text", "heading", "paragraph"] },
3474
+ media: { label: "Media", tools: ["image", "video"] },
3475
+ actions: { label: "Actions", tools: ["button", "social", "menu"] },
3476
+ structure: { label: "Structure", tools: ["divider", "html", "timer", "table", "form"] }
3477
+ };
3478
+ let H = class extends T {
3479
+ constructor() {
3480
+ super(...arguments), this.storeCtrl = new B(this, ["activeTab", "design"]), this.searchQuery = "";
3481
+ }
3482
+ set store(e) {
3483
+ this.storeCtrl.setStore(e);
3484
+ }
3485
+ get store() {
3486
+ return this.storeCtrl.store;
3487
+ }
3488
+ getA11ySeverities() {
3489
+ const e = ve(this.store);
3490
+ return {
3491
+ errors: e.some((t) => t.severity === "error"),
3492
+ warnings: e.some((t) => t.severity === "warning"),
3493
+ infos: e.some((t) => t.severity === "info")
3494
+ };
3495
+ }
3496
+ handleDragStart(e, t) {
3497
+ e.dataTransfer.setData("application/maileditor-tool", t), e.dataTransfer.effectAllowed = "copy";
3498
+ }
3499
+ handleLayoutDragStart(e, t) {
3500
+ e.dataTransfer.setData("application/maileditor-layout", JSON.stringify(t)), e.dataTransfer.effectAllowed = "copy";
3501
+ }
3502
+ addRowWithLayout(e) {
3503
+ const t = this.store.createRow(e);
3504
+ this.store.addRow(t);
3505
+ }
3506
+ render() {
3507
+ const e = this.store.activeTab;
3508
+ return d`
3509
+ <div class="tabs">
3510
+ <button class="tab ${e === "content" ? "active" : ""}"
3511
+ @click=${() => this.store.setActiveTab("content")}>Elements</button>
3512
+ <button class="tab ${e === "body" ? "active" : ""}"
3513
+ @click=${() => this.store.setActiveTab("body")}>Body</button>
3514
+ <button class="tab ${e === "a11y" ? "active" : ""}"
3515
+ @click=${() => this.store.setActiveTab("a11y")}>A11y${this.renderA11yBadge()}</button>
3516
+ </div>
3517
+
3518
+ ${e === "content" ? this.renderElementsTab() : ""}
3519
+ ${e === "body" ? d`<div class="tab-content" style="padding-top:12px;">${this.renderBodyTab()}</div>` : ""}
3520
+ ${e === "a11y" ? d`<div class="tab-content" style="padding-top:12px;"><me-a11y-checker .store=${this.store} .toolRegistry=${this.toolRegistry}></me-a11y-checker></div>` : ""}
3521
+ `;
3522
+ }
3523
+ renderElementsTab() {
3524
+ const e = this.toolRegistry.getAllMeta(), t = this.searchQuery.toLowerCase().trim(), o = /* @__PURE__ */ new Map();
3525
+ for (const l of e) o.set(l.name, l);
3526
+ const r = (l) => !t || l.label.toLowerCase().includes(t) || l.name.toLowerCase().includes(t);
3527
+ let i = !1;
3528
+ const n = Object.entries(Le).map(([, l]) => {
3529
+ const u = l.tools.map((h) => o.get(h)).filter((h) => !!h && r(h));
3530
+ return u.length > 0 && (i = !0), { label: l.label, tools: u };
3531
+ }), s = new Set(Object.values(Le).flatMap((l) => l.tools)), a = e.filter((l) => !s.has(l.name) && r(l));
3532
+ a.length > 0 && (i = !0);
3533
+ const c = !t || "layout".includes(t) || "row".includes(t) || "column".includes(t);
3534
+ return d`
3535
+ <div class="search-wrap">
3536
+ <input class="search-input"
3537
+ type="text"
3538
+ placeholder="Search elements..."
3539
+ .value=${this.searchQuery}
3540
+ @input=${(l) => {
3541
+ this.searchQuery = l.target.value;
3542
+ }}
3543
+ />
3544
+ </div>
3545
+ <div class="tab-content">
3546
+ ${n.map(
3547
+ (l) => l.tools.length === 0 ? x : d`
3548
+ <div class="category">
3549
+ <div class="category-title">${l.label}</div>
3550
+ <div class="tool-grid">
3551
+ ${l.tools.map((u) => this.renderToolItem(u))}
3552
+ </div>
3553
+ </div>
3554
+ `
3555
+ )}
3556
+ ${a.length > 0 ? d`
3557
+ <div class="category">
3558
+ <div class="category-title">Custom</div>
3559
+ <div class="tool-grid">
3560
+ ${a.map((l) => this.renderToolItem(l))}
3561
+ </div>
3562
+ </div>
3563
+ ` : ""}
3564
+ ${c ? d`
3565
+ <div class="layout-section">
3566
+ <div class="category-title">Rows</div>
3567
+ <div class="layout-grid">
3568
+ ${this.renderLayoutOption([1], "100%")}
3569
+ ${this.renderLayoutOption([1, 1], "50/50")}
3570
+ ${this.renderLayoutOption([1, 1, 1], "33/33/33")}
3571
+ ${this.renderLayoutOption([2, 1], "66/33")}
3572
+ ${this.renderLayoutOption([1, 2], "33/66")}
3573
+ ${this.renderLayoutOption([1, 1, 1, 1], "25x4")}
3574
+ </div>
3575
+ </div>
3576
+ ` : ""}
3577
+ ${!i && !c ? d`
3578
+ <div class="no-results">No elements match "${this.searchQuery}"</div>
3579
+ ` : ""}
3580
+ </div>
3581
+ `;
3582
+ }
3583
+ renderA11yBadge() {
3584
+ const { errors: e, warnings: t, infos: o } = this.getA11ySeverities();
3585
+ return !e && !t && !o ? x : d`<span class="tab-badge">${e ? d`<span class="tab-dot error"></span>` : x}${t ? d`<span class="tab-dot warning"></span>` : x}${o ? d`<span class="tab-dot info"></span>` : x}</span>`;
3586
+ }
3587
+ renderToolItem(e) {
3588
+ return d`
3589
+ <div class="tool-item"
3590
+ draggable="true"
3591
+ @dragstart=${(t) => this.handleDragStart(t, e.name)}>
3592
+ <div class="tool-icon">${ie(e.icon)}</div>
3593
+ <span>${e.label}</span>
3594
+ </div>
3595
+ `;
3596
+ }
3597
+ renderLayoutOption(e, t) {
3598
+ const o = e.reduce((r, i) => r + i, 0);
3599
+ return d`
3600
+ <div class="layout-item" draggable="true"
3601
+ @click=${() => this.addRowWithLayout(e)}
3602
+ @dragstart=${(r) => this.handleLayoutDragStart(r, e)}
3603
+ title=${t}>
3604
+ ${e.map((r) => d`<div class="layout-col" style="width:${r / o * 100}%;"></div>`)}
3605
+ </div>
3606
+ `;
3607
+ }
3608
+ renderBodyTab() {
3609
+ return d`<me-body-settings .store=${this.store}></me-body-settings>`;
3610
+ }
3611
+ };
3612
+ H.styles = _`
3613
+ :host {
3614
+ display: flex;
3615
+ flex-direction: column;
3616
+ width: 280px;
3617
+ min-width: 280px;
3618
+ background: #ffffff;
3619
+ border-right: 1px solid var(--me-grey-100);
3620
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3621
+ overflow-y: auto;
3622
+ transition: width 200ms ease, min-width 200ms ease;
3623
+ position: relative;
3624
+ }
3625
+ :host(.collapsed) {
3626
+ width: 0; min-width: 0; overflow: hidden; border-right: none;
3627
+ }
3628
+
3629
+ /* ── Tabs ── */
3630
+ .tabs {
3631
+ display: flex;
3632
+ border-bottom: 1px solid var(--me-grey-200);
3633
+ background: #ffffff;
3634
+ flex-shrink: 0;
3635
+ }
3636
+ .tab {
3637
+ flex: 1; padding: 10px 8px; border: none; background: none;
3638
+ cursor: pointer; font-size: 11px; font-weight: 600;
3639
+ text-transform: uppercase; letter-spacing: 0.05em;
3640
+ color: var(--me-grey-500); text-align: center;
3641
+ transition: all 150ms ease; border-bottom: 2px solid transparent;
3642
+ }
3643
+ .tab:hover { color: var(--me-grey-700); background: var(--me-grey-100); }
3644
+ .tab.active { color: var(--me-primary); border-bottom-color: var(--me-primary); }
3645
+
3646
+ /* ── Search ── */
3647
+ .search-wrap {
3648
+ padding: 10px 12px 6px;
3649
+ flex-shrink: 0;
3650
+ }
3651
+ .search-input {
3652
+ width: 100%;
3653
+ border: 1px solid var(--me-grey-200);
3654
+ border-radius: 8px;
3655
+ padding: 6px 10px 6px 30px;
3656
+ font-size: 12px;
3657
+ color: var(--me-grey-700);
3658
+ background: var(--me-grey-50) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") 10px center no-repeat;
3659
+ outline: none;
3660
+ transition: all 150ms ease;
3661
+ box-sizing: border-box;
3662
+ }
3663
+ .search-input::placeholder { color: var(--me-grey-400); }
3664
+ .search-input:focus {
3665
+ border-color: var(--me-primary);
3666
+ background-color: #fff;
3667
+ box-shadow: 0 0 0 2px rgba(93,118,139,0.15);
3668
+ }
3669
+
3670
+ /* ── Scrollable content ── */
3671
+ .tab-content {
3672
+ padding: 0 12px 16px;
3673
+ flex: 1;
3674
+ overflow-y: auto;
3675
+ animation: tabFadeIn 0.2s ease-out;
3676
+ }
3677
+ @keyframes tabFadeIn {
3678
+ from { opacity: 0; transform: translateY(4px); }
3679
+ to { opacity: 1; transform: translateY(0); }
3680
+ }
3681
+
3682
+ /* ── Category groups ── */
3683
+ .category {
3684
+ margin-top: 12px;
3685
+ }
3686
+ .category-title {
3687
+ font-size: 10px;
3688
+ font-weight: 700;
3689
+ text-transform: uppercase;
3690
+ color: var(--me-blue-600);
3691
+ letter-spacing: 0.06em;
3692
+ margin: 0 0 6px 2px;
3693
+ padding: 4px 0;
3694
+ border-bottom: 1px solid var(--me-blue-50);
3695
+ }
3696
+
3697
+ /* ── Tool grid (2 columns) ── */
3698
+ .tool-grid {
3699
+ display: grid;
3700
+ grid-template-columns: 1fr 1fr;
3701
+ gap: 4px;
3702
+ }
3703
+ .tool-item {
3704
+ display: flex;
3705
+ flex-direction: row;
3706
+ align-items: center;
3707
+ gap: 6px;
3708
+ padding: 6px 8px;
3709
+ border: 1px solid transparent;
3710
+ border-radius: 8px;
3711
+ cursor: grab;
3712
+ background: transparent;
3713
+ transition: all 150ms ease;
3714
+ font-size: 11px;
3715
+ color: var(--me-grey-600);
3716
+ }
3717
+ .tool-item:hover {
3718
+ border-color: var(--me-grey-200);
3719
+ background: var(--me-grey-50);
3720
+ }
3721
+ .tool-item:hover .tool-icon { color: var(--me-primary); }
3722
+ .tool-item:active { cursor: grabbing; background: var(--me-primary-5); }
3723
+ .tool-icon {
3724
+ width: 18px; height: 18px;
3725
+ display: flex; align-items: center; justify-content: center;
3726
+ color: var(--me-grey-400);
3727
+ transition: color 150ms ease;
3728
+ flex-shrink: 0;
3729
+ }
3730
+ .tool-icon svg { width: 18px; height: 18px; }
3731
+
3732
+ /* ── Layout presets ── */
3733
+ .layout-section { margin-top: 14px; }
3734
+ .layout-grid {
3735
+ display: grid;
3736
+ grid-template-columns: 1fr 1fr 1fr;
3737
+ gap: 6px;
3738
+ }
3739
+ .layout-item {
3740
+ display: flex; align-items: center; justify-content: center;
3741
+ gap: 2px; padding: 8px 4px;
3742
+ border: 1px solid var(--me-grey-200); border-radius: 8px;
3743
+ cursor: pointer; background: white;
3744
+ transition: all 150ms ease;
3745
+ }
3746
+ .layout-item:hover { border-color: var(--me-primary); background: var(--me-primary-5); }
3747
+ .layout-item:hover .layout-col { background: var(--me-primary); }
3748
+ .layout-item:active { background: var(--me-primary-10); }
3749
+ .layout-col {
3750
+ height: 18px; background: var(--me-grey-300); border-radius: 2px;
3751
+ transition: background 150ms ease;
3752
+ }
3753
+
3754
+ /* ── A11y tab badge ── */
3755
+ .tab-badge {
3756
+ display: inline-flex;
3757
+ gap: 3px;
3758
+ margin-left: 4px;
3759
+ vertical-align: middle;
3760
+ }
3761
+ .tab-dot {
3762
+ width: 6px; height: 6px; border-radius: 50%;
3763
+ }
3764
+ .tab-dot.error { background: var(--me-danger); }
3765
+ .tab-dot.warning { background: var(--me-warning); }
3766
+ .tab-dot.info { background: var(--me-blue-600); }
3767
+
3768
+ /* ── No results ── */
3769
+ .no-results {
3770
+ text-align: center; color: var(--me-grey-400); font-size: 12px;
3771
+ padding: 24px 0;
3772
+ }
3773
+ `;
3774
+ le([
3775
+ v({ attribute: !1 })
3776
+ ], H.prototype, "store", 1);
3777
+ le([
3778
+ v({ attribute: !1 })
3779
+ ], H.prototype, "toolRegistry", 2);
3780
+ le([
3781
+ E()
3782
+ ], H.prototype, "searchQuery", 2);
3783
+ H = le([
3784
+ S("me-editor-sidebar")
3785
+ ], H);
3786
+ function Yt(e, t, o) {
3787
+ const r = e && /^#[0-9a-fA-F]{3,8}$/.test(e) ? e : "#000000";
3788
+ return d`
3789
+ <div class="prop-row">
3790
+ <label class="prop-label">${o}</label>
3791
+ <div class="prop-color">
3792
+ <input class="color-swatch" type="color" .value=${r}
3793
+ @input=${(i) => t(i.target.value)} />
3794
+ <input class="prop-input" type="text" .value=${e ?? ""} style="flex:1;"
3795
+ @change=${(i) => t(i.target.value)} />
3796
+ </div>
3797
+ </div>
3798
+ `;
3799
+ }
3800
+ function Qt(e, t, o, r) {
3801
+ const i = r?.options || [];
3802
+ return d`
3803
+ <div class="prop-row">
3804
+ <label class="prop-label">${o}</label>
3805
+ <select class="prop-input" @change=${(n) => t(n.target.value)}>
3806
+ ${i.map((n) => d`<option value=${n.value} ?selected=${e === n.value}>${n.label}</option>`)}
3807
+ </select>
3808
+ </div>
3809
+ `;
3810
+ }
3811
+ function Kt(e, t, o) {
3812
+ return d`
3813
+ <div class="prop-row">
3814
+ <label class="prop-label">${o}</label>
3815
+ <div style="display:flex;gap:2px;">
3816
+ ${["left", "center", "right"].map((r) => d`
3817
+ <button
3818
+ style="flex:1;padding:6px;border:1px solid ${e === r ? "#3b82f6" : "#d1d5db"};background:${e === r ? "#eff6ff" : "white"};border-radius:4px;cursor:pointer;font-size:11px;text-transform:capitalize;color:${e === r ? "#3b82f6" : "#6b7280"};"
3819
+ @click=${() => t(r)}
3820
+ >${r}</button>
3821
+ `)}
3822
+ </div>
3823
+ </div>
3824
+ `;
3825
+ }
3826
+ function Jt(e) {
3827
+ const t = (e || "0px").split(/\s+/).map((s) => parseInt(s) || 0), o = t[0], r = t[1] ?? o, i = t[2] ?? o, n = t[3] ?? r;
3828
+ return [o, r, i, n];
3829
+ }
3830
+ function Xt(e, t, o, r) {
3831
+ return e === t && t === o && o === r ? `${e}px` : e === o && t === r ? `${e}px ${t}px` : `${e}px ${t}px ${o}px ${r}px`;
3832
+ }
3833
+ function Zt(e, t, o) {
3834
+ const [r, i, n, s] = Jt(e), a = (l, u, h, g) => t(Xt(l, u, h, g));
3835
+ return d`
3836
+ <div class="prop-row">
3837
+ <label class="prop-label">${o}</label>
3838
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;">
3839
+ ${[
3840
+ { label: "T", val: r, change: (l) => a(l, i, n, s) },
3841
+ { label: "R", val: i, change: (l) => a(r, l, n, s) },
3842
+ { label: "B", val: n, change: (l) => a(r, i, l, s) },
3843
+ { label: "L", val: s, change: (l) => a(r, i, n, l) }
3844
+ ].map((l) => d`
3845
+ <div style="display:flex;align-items:center;gap:4px;">
3846
+ <span style="font-size:10px;color:#9ca3af;width:12px;">${l.label}</span>
3847
+ <input type="number" .value=${l.val} min="0"
3848
+ @change=${(u) => l.change(parseInt(u.target.value) || 0)}
3849
+ style="flex:1;padding:4px 6px;border:1px solid #d1d5db;border-radius:3px;font-size:12px;width:50px;" />
3850
+ </div>
3851
+ `)}
3852
+ </div>
3853
+ </div>
3854
+ `;
3855
+ }
3856
+ function eo(e, t, o) {
3857
+ return d`
3858
+ <div class="prop-row">
3859
+ <div class="prop-toggle">
3860
+ <input type="checkbox" .checked=${!!e}
3861
+ @change=${(r) => t(r.target.checked)} />
3862
+ <label class="prop-label" style="margin:0;">${o}</label>
3863
+ </div>
3864
+ </div>
3865
+ `;
3866
+ }
3867
+ function to(e, t, o) {
3868
+ return d`
3869
+ <div class="prop-row">
3870
+ <label class="prop-label">${o}</label>
3871
+ <input class="prop-input" type="text" .value=${e ?? ""}
3872
+ @change=${(r) => t(r.target.value)} />
3873
+ </div>
3874
+ `;
3875
+ }
3876
+ function oo(e, t, o) {
3877
+ return d`
3878
+ <div class="prop-row">
3879
+ <label class="prop-label">${o}</label>
3880
+ <textarea class="prop-input"
3881
+ style="min-height:100px;font-family:'SF Mono',Menlo,monospace;font-size:12px;"
3882
+ .value=${e ?? ""}
3883
+ @change=${(r) => t(r.target.value)}
3884
+ ></textarea>
3885
+ </div>
3886
+ `;
3887
+ }
3888
+ function ro(e, t, o, r) {
3889
+ const i = r?.unit || "px", n = r?.min ?? 0, s = r?.max ?? 100, a = r?.step ?? 0.1, c = parseFloat(e) || 0, l = (u) => {
3890
+ const h = parseFloat(u.target.value) || 0;
3891
+ t(`${h}${i}`);
3892
+ };
3893
+ return d`
3894
+ <div class="prop-row">
3895
+ <label class="prop-label">${o}</label>
3896
+ <div style="display:flex;align-items:center;gap:4px;">
3897
+ <input
3898
+ type="number"
3899
+ .value=${String(c)}
3900
+ min=${n}
3901
+ max=${s}
3902
+ step=${a}
3903
+ @input=${l}
3904
+ style="flex:1;padding:6px 8px;border:1px solid #d1d5db;border-radius:4px;font-size:13px;color:#111827;"
3905
+ />
3906
+ <span style="font-size:12px;color:#9ca3af;min-width:20px;">${i}</span>
3907
+ </div>
3908
+ </div>
3909
+ `;
3910
+ }
3911
+ function io(e, t, o, r) {
3912
+ const i = () => {
3913
+ r && r({
3914
+ done: (n) => {
3915
+ n?.url && t(n.url);
3916
+ }
3917
+ });
3918
+ };
3919
+ return d`
3920
+ <div class="prop-row">
3921
+ <label class="prop-label">${o}</label>
3922
+ ${e ? d`
3923
+ <div style="margin-bottom:6px;border-radius:6px;overflow:hidden;border:1px solid var(--me-grey-200,#e5e7eb);background:var(--me-grey-50,#f9fafb);">
3924
+ <img src=${e} alt="Preview" style="display:block;width:100%;max-height:120px;object-fit:cover;" />
3925
+ </div>
3926
+ ` : x}
3927
+ <div style="display:flex;gap:4px;">
3928
+ <input class="prop-input" type="text" .value=${e ?? ""} placeholder="https://..."
3929
+ style="flex:1;${r ? "border-radius:8px 0 0 8px;" : ""}"
3930
+ @change=${(n) => t(n.target.value)} />
3931
+ ${r ? d`
3932
+ <button @click=${i}
3933
+ style="padding:0 10px;border:1px solid var(--me-grey-200,#e5e7eb);border-left:none;border-radius:0 8px 8px 0;background:var(--me-grey-50,#f9fafb);cursor:pointer;color:var(--me-grey-600,#4b5563);font-size:11px;font-weight:600;transition:all 150ms ease;white-space:nowrap;"
3934
+ title="Upload image">
3935
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
3936
+ </button>
3937
+ ` : x}
3938
+ </div>
3939
+ </div>
3940
+ `;
3941
+ }
3942
+ var no = Object.defineProperty, so = Object.getOwnPropertyDescriptor, we = (e, t, o, r) => {
3943
+ for (var i = r > 1 ? void 0 : r ? so(t, o) : t, n = e.length - 1, s; n >= 0; n--)
3944
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
3945
+ return r && i && no(t, o, i), i;
3946
+ };
3947
+ let q = class extends T {
3948
+ constructor() {
3949
+ super(...arguments), this.storeCtrl = new B(this, ["design", "selection"]);
3950
+ }
3951
+ set store(e) {
3952
+ this.storeCtrl.setStore(e);
3953
+ }
3954
+ get store() {
3955
+ return this.storeCtrl.store;
3956
+ }
3957
+ /** Create a change handler bound to a specific content ID and property key */
3958
+ onChange(e, t) {
3959
+ return (o) => {
3960
+ this.store.updateContentValues(e, { [t]: o });
3961
+ };
3962
+ }
3963
+ render() {
3964
+ const e = this.store.selectedId, t = e ? this.store.findContent(e) : void 0;
3965
+ if (this.classList.toggle("hidden", !t), !t)
3966
+ return d``;
3967
+ const o = this.toolRegistry.get(t.type);
3968
+ return o ? d`
3969
+ <div class="header">
3970
+ <p class="header-title">${o.label}</p>
3971
+ <p class="header-type">${t.type}</p>
3972
+ </div>
3973
+ ${Object.entries(o.options).map(([, r]) => this.renderGroup(t, r))}
3974
+ ` : d`<div class="no-selection">Unknown tool: ${t.type}</div>`;
3975
+ }
3976
+ renderGroup(e, t) {
3977
+ return d`
3978
+ <div class="group">
3979
+ <div class="group-title">${t.title}</div>
3980
+ <div class="group-body">
3981
+ ${Object.entries(t.options).map(
3982
+ ([o, r]) => this.renderWidget(e, o, r)
3983
+ )}
3984
+ </div>
3985
+ </div>
3986
+ `;
3987
+ }
3988
+ /** Delegate to the correct widget function based on the property's widget type */
3989
+ renderWidget(e, t, o) {
3990
+ const r = e.values[t] ?? o.defaultValue, i = this.onChange(e.id, t);
3991
+ switch (o.widget) {
3992
+ case "color_picker":
3993
+ return Yt(r, i, o.label);
3994
+ case "toggle":
3995
+ return eo(r, i, o.label);
3996
+ case "rich_text":
3997
+ return oo(r, i, o.label);
3998
+ case "dropdown":
3999
+ return Qt(r, i, o.label, o.widgetParams);
4000
+ case "alignment":
4001
+ return Kt(r, i, o.label);
4002
+ case "padding":
4003
+ return Zt(r, i, o.label);
4004
+ case "number_unit":
4005
+ return ro(r, i, o.label, o.widgetParams);
4006
+ case "image_upload":
4007
+ return io(r, i, o.label, this.store.getCallback("image"));
4008
+ case "text":
4009
+ default:
4010
+ return to(r, i, o.label);
4011
+ }
4012
+ }
4013
+ };
4014
+ q.styles = _`
4015
+ :host {
4016
+ display: flex; flex-direction: column; width: 280px; min-width: 280px;
4017
+ background: #fff; border-left: 1px solid var(--me-grey-100);
4018
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4019
+ overflow-y: auto;
4020
+ transition: width 200ms ease, min-width 200ms ease, opacity 200ms ease;
4021
+ }
4022
+ :host(.hidden) {
4023
+ width: 0; min-width: 0; opacity: 0; overflow: hidden; border-left: none;
4024
+ }
4025
+
4026
+ /* ── Panel header ── */
4027
+ .header {
4028
+ padding: 10px 12px; border-bottom: 1px solid var(--me-grey-100); background: #fff;
4029
+ flex-shrink: 0;
4030
+ }
4031
+ .header-title {
4032
+ font-size: 12px; font-weight: 700; color: var(--me-grey-900); margin: 0;
4033
+ }
4034
+ .header-type {
4035
+ font-size: 10px; color: var(--me-grey-400); text-transform: uppercase;
4036
+ letter-spacing: 0.03em; margin: 2px 0 0 0;
4037
+ }
4038
+
4039
+ /* ── Empty state ── */
4040
+ .no-selection {
4041
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
4042
+ flex: 1; color: var(--me-grey-400); font-size: 12px; gap: 8px; padding: 40px 16px; text-align: center;
4043
+ }
4044
+
4045
+ /* ── Property groups ── */
4046
+ .group { padding: 0 12px 4px; }
4047
+ .group-title {
4048
+ padding: 8px 0 4px; font-size: 10px; font-weight: 700; color: var(--me-blue-600);
4049
+ cursor: default; user-select: none;
4050
+ display: flex; align-items: center; justify-content: space-between;
4051
+ text-transform: uppercase; letter-spacing: 0.06em;
4052
+ border-bottom: 1px solid var(--me-blue-50);
4053
+ margin-bottom: 8px;
4054
+ }
4055
+ .group-body { display: flex; flex-direction: column; gap: 8px; padding-bottom: 4px; }
4056
+
4057
+ /* ── Property rows ── */
4058
+ .prop-row { margin-bottom: 0; display: flex; flex-direction: column; gap: 4px; }
4059
+ .prop-label {
4060
+ display: block; font-size: 11px; font-weight: 500; color: var(--me-grey-600);
4061
+ letter-spacing: 0.01em;
4062
+ }
4063
+
4064
+ /* ── Inputs & selects ── */
4065
+ .prop-input {
4066
+ width: 100%; padding: 6px 10px; border: 1px solid var(--me-grey-200); border-radius: 8px;
4067
+ font-size: 12px; color: var(--me-grey-700); background: #fff; box-sizing: border-box;
4068
+ transition: border-color 150ms ease, box-shadow 150ms ease;
4069
+ }
4070
+ .prop-input:focus {
4071
+ outline: none; border-color: var(--me-primary);
4072
+ box-shadow: 0 0 0 2px var(--me-primary-30);
4073
+ }
4074
+ select.prop-input {
4075
+ appearance: none; padding-right: 28px;
4076
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
4077
+ background-repeat: no-repeat; background-position: right 8px center;
4078
+ }
4079
+ textarea.prop-input { min-height: 80px; resize: vertical; font-family: monospace; }
4080
+
4081
+ /* ── Toggle ── */
4082
+ .prop-toggle { display: flex; align-items: center; gap: 8px; }
4083
+
4084
+ /* ── Color picker ── */
4085
+ .prop-color { display: flex; align-items: center; gap: 8px; }
4086
+ .color-swatch {
4087
+ width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--me-grey-200);
4088
+ cursor: pointer; padding: 0; transition: border-color 150ms ease;
4089
+ }
4090
+ .color-swatch:hover { border-color: var(--me-primary); }
4091
+ `;
4092
+ we([
4093
+ v({ attribute: !1 })
4094
+ ], q.prototype, "store", 1);
4095
+ we([
4096
+ v({ attribute: !1 })
4097
+ ], q.prototype, "toolRegistry", 2);
4098
+ q = we([
4099
+ S("me-property-panel")
4100
+ ], q);
4101
+ var ao = Object.defineProperty, lo = Object.getOwnPropertyDescriptor, Y = (e, t, o, r) => {
4102
+ for (var i = r > 1 ? void 0 : r ? lo(t, o) : t, n = e.length - 1, s; n >= 0; n--)
4103
+ (s = e[n]) && (i = (r ? s(t, o, i) : s(i)) || i);
4104
+ return r && i && ao(t, o, i), i;
4105
+ };
4106
+ let P = class extends T {
4107
+ constructor() {
4108
+ super(...arguments), this.options = {}, this.store = new Ze(), this.toolRegistry = new et(), this.dragManager = null, this.callbacks = /* @__PURE__ */ new Map(), this.unsubscribe = null, this.sidebarCollapsed = !1, this.a11yHasErrors = !1, this.a11yHasWarnings = !1, this.a11yHasInfos = !1, this.a11yDebounceTimer = null, this._handleKeydown = (e) => {
4109
+ const t = e.metaKey || e.ctrlKey, r = e.composedPath().some((n) => {
4110
+ const s = n, a = s?.tagName;
4111
+ return !!(a === "INPUT" || a === "TEXTAREA" || a === "SELECT" || s?.isContentEditable);
4112
+ });
4113
+ if (r && !t) return;
4114
+ const i = this.shadowRoot?.querySelector("me-editor-canvas");
4115
+ t && e.key === "z" && !e.shiftKey ? (e.preventDefault(), this.store.undo(), i?.flashUndo?.()) : t && (e.key === "y" || e.key === "z" && e.shiftKey) ? (e.preventDefault(), this.store.redo(), i?.flashUndo?.()) : t && e.key === "d" && !e.shiftKey && this.store.selectedId && !r ? (e.preventDefault(), this.store.duplicateContent(this.store.selectedId)) : (e.key === "Delete" || e.key === "Backspace") && this.store.selectedId && !r ? (e.preventDefault(), this.store.removeContent(this.store.selectedId)) : e.key === "Escape" && this.store.select(null);
4116
+ };
4117
+ }
4118
+ updateA11yState() {
4119
+ this.a11yDebounceTimer && clearTimeout(this.a11yDebounceTimer), this.a11yDebounceTimer = setTimeout(() => {
4120
+ const e = ve(this.store);
4121
+ this.a11yHasErrors = e.some((o) => o.severity === "error"), this.a11yHasWarnings = e.some((o) => o.severity === "warning"), this.a11yHasInfos = e.some((o) => o.severity === "info");
4122
+ const t = /* @__PURE__ */ new Set();
4123
+ for (const o of e)
4124
+ o.elementId && (o.severity === "error" || o.severity === "warning") && t.add(o.elementId);
4125
+ this.store.setA11yIssueIds(t);
4126
+ }, 300);
4127
+ }
4128
+ toggleSidebar() {
4129
+ this.sidebarCollapsed = !this.sidebarCollapsed, this.shadowRoot?.querySelector("me-editor-sidebar")?.classList.toggle("collapsed", this.sidebarCollapsed);
4130
+ }
4131
+ connectedCallback() {
4132
+ super.connectedCallback(), this.registerBuiltInTools(), this.applyOptions(), this.setAttribute("tabindex", "0"), this.addEventListener("keydown", this._handleKeydown);
4133
+ }
4134
+ firstUpdated() {
4135
+ this.dragManager = new tt(this.store, this.toolRegistry, this.shadowRoot), this.dragManager.attach(), this.store.events.on("design:loaded", (e) => {
4136
+ this.dispatchEvent(new CustomEvent("design:loaded", { detail: e, bubbles: !0, composed: !0 }));
4137
+ }), this.store.events.on("design:updated", (e) => {
4138
+ this.dispatchEvent(new CustomEvent("design:updated", { detail: e, bubbles: !0, composed: !0 }));
4139
+ }), this.unsubscribe = this.store.subscribeChannels(["design"], () => this.updateA11yState()), this.updateA11yState(), this.dispatchEvent(new CustomEvent("editor:ready", { bubbles: !0, composed: !0 })), this.preloadLazyTools();
4140
+ }
4141
+ disconnectedCallback() {
4142
+ super.disconnectedCallback(), this.dragManager?.detach(), this.unsubscribe?.(), this.store.events.removeAllListeners(), this.removeEventListener("keydown", this._handleKeydown);
4143
+ }
4144
+ // ----------------------------------------------------------
4145
+ // Public API — public API
4146
+ // ----------------------------------------------------------
4147
+ loadDesign(e) {
4148
+ this.store.loadDesign(e);
4149
+ }
4150
+ /** Load an Unlayer design JSON, converting it to Emabuild format */
4151
+ loadUnlayerDesign(e) {
4152
+ this.store.loadDesign(Pt(e));
4153
+ }
4154
+ saveDesign(e) {
4155
+ e(structuredClone(this.store.getDesign()));
4156
+ }
4157
+ /** Save the design as Unlayer-compatible JSON */
4158
+ saveUnlayerDesign(e) {
4159
+ e(Et(this.store.getDesign()));
4160
+ }
4161
+ exportHtml(e, t) {
4162
+ const o = this.store.getDesign(), r = /* @__PURE__ */ new Set();
4163
+ for (const n of o.body.rows)
4164
+ for (const s of n.columns)
4165
+ for (const a of s.contents)
4166
+ r.add(a.type);
4167
+ const i = Array.from(r).filter((n) => !this.toolRegistry.isLoaded(n)).map((n) => this.toolRegistry.ensureLoaded(n));
4168
+ i.length > 0 ? Promise.all(i).then(() => this.doExport(o, e, t)) : this.doExport(o, e, t);
4169
+ }
4170
+ doExport(e, t, o) {
4171
+ const r = /* @__PURE__ */ new Map();
4172
+ for (const i of this.toolRegistry.getAll())
4173
+ r.set(i.name, (n, s) => i.renderer.renderHtml(n, s));
4174
+ t(It(e, r, o));
4175
+ }
4176
+ async exportHtmlAsync(e) {
4177
+ return new Promise((t) => this.exportHtml(t, e));
4178
+ }
4179
+ registerTool(e) {
4180
+ this.toolRegistry.register(e), this.requestUpdate();
4181
+ }
4182
+ registerPropertyEditor(e, t) {
4183
+ }
4184
+ registerTab(e) {
4185
+ }
4186
+ registerCallback(e, t) {
4187
+ this.callbacks.set(e, t), this.store.setCallback(e, t);
4188
+ }
4189
+ setMergeTags(e) {
4190
+ this.store.setMergeTags(e);
4191
+ }
4192
+ undo() {
4193
+ this.store.undo();
4194
+ }
4195
+ redo() {
4196
+ this.store.redo();
4197
+ }
4198
+ setBodyValues(e) {
4199
+ this.store.updateBodyValues(e);
4200
+ }
4201
+ // ----------------------------------------------------------
4202
+ // Internal
4203
+ // ----------------------------------------------------------
4204
+ /**
4205
+ * Preload lazy tools during browser idle time.
4206
+ * Uses requestIdleCallback to avoid blocking the main thread.
4207
+ * Falls back to setTimeout(1000) for browsers without idle callback support.
4208
+ */
4209
+ preloadLazyTools() {
4210
+ const e = window.requestIdleCallback ?? ((t) => setTimeout(t, 1e3));
4211
+ for (const { meta: t } of Ee)
4212
+ e(() => {
4213
+ this.toolRegistry.ensureLoaded(t.name);
4214
+ });
4215
+ }
4216
+ registerBuiltInTools() {
4217
+ for (const e of bt)
4218
+ this.toolRegistry.register(e);
4219
+ for (const { meta: e, loader: t } of Ee)
4220
+ this.toolRegistry.registerLazy(e, t);
4221
+ }
4222
+ applyOptions() {
4223
+ if (this.options.design && this.store.loadDesign(this.options.design), this.options.mergeTags) {
4224
+ const e = this.options.mergeTags, t = Array.isArray(e) ? e : Object.values(e);
4225
+ this.store.setMergeTags(t);
4226
+ }
4227
+ }
4228
+ render() {
4229
+ return d`
4230
+ <me-editor-sidebar
4231
+ .store=${this.store}
4232
+ .toolRegistry=${this.toolRegistry}
4233
+ ></me-editor-sidebar>
4234
+ <button class="sidebar-toggle ${this.sidebarCollapsed ? "collapsed" : ""}"
4235
+ @click=${this.toggleSidebar}
4236
+ title="${this.sidebarCollapsed ? "Show tools" : "Hide tools"}"
4237
+ style="left:${this.sidebarCollapsed ? "10px" : "290px"}">
4238
+ ${this.sidebarCollapsed ? d`<svg viewBox="0 0 24 24"><path d="M13 18l-6-6 6-6"/><path d="M19 18l-6-6 6-6"/></svg>` : d`<svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6"/></svg>`}
4239
+ </button>
4240
+ ${this.sidebarCollapsed && (this.a11yHasErrors || this.a11yHasWarnings || this.a11yHasInfos) ? d`
4241
+ <div class="a11y-dots">
4242
+ ${this.a11yHasErrors ? d`<div class="a11y-dot error"></div>` : x}
4243
+ ${this.a11yHasWarnings ? d`<div class="a11y-dot warning"></div>` : x}
4244
+ ${this.a11yHasInfos ? d`<div class="a11y-dot info"></div>` : x}
4245
+ </div>
4246
+ ` : x}
4247
+ <me-editor-canvas
4248
+ .store=${this.store}
4249
+ .toolRegistry=${this.toolRegistry}
4250
+ ></me-editor-canvas>
4251
+ <me-property-panel
4252
+ .store=${this.store}
4253
+ .toolRegistry=${this.toolRegistry}
4254
+ ></me-property-panel>
4255
+ `;
4256
+ }
4257
+ };
4258
+ P.styles = _`
4259
+ :host {
4260
+ /* ── Design tokens ── */
4261
+ --me-primary: #5d768b;
4262
+ --me-primary-dark: #4a6070;
4263
+ --me-primary-5: rgba(93,118,139,0.05);
4264
+ --me-primary-10: rgba(93,118,139,0.1);
4265
+ --me-primary-30: rgba(93,118,139,0.3);
4266
+
4267
+ --me-grey-900: #111827;
4268
+ --me-grey-700: #374151;
4269
+ --me-grey-600: #4b5563;
4270
+ --me-grey-500: #6b7280;
4271
+ --me-grey-400: #9ca3af;
4272
+ --me-grey-300: #d1d5db;
4273
+ --me-grey-200: #e5e7eb;
4274
+ --me-grey-100: #f3f4f6;
4275
+ --me-grey-50: #f9fafb;
4276
+
4277
+ --me-blue-50: #EDF5FF;
4278
+ --me-blue-600: #245A7F;
4279
+
4280
+ --me-danger: #dc2626;
4281
+ --me-warning: #d97706;
4282
+ --me-success: #16a34a;
4283
+
4284
+ --me-radius-sm: 6px;
4285
+ --me-radius: 8px;
4286
+ --me-radius-lg: 10px;
4287
+
4288
+ --me-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4289
+ --me-transition: 150ms ease;
4290
+
4291
+ /* ── Layout ── */
4292
+ display: flex;
4293
+ width: 100%;
4294
+ height: 100%;
4295
+ flex: 1;
4296
+ min-height: 500px;
4297
+ font-family: var(--me-font);
4298
+ color: var(--me-grey-900);
4299
+ box-sizing: border-box;
4300
+ overflow: hidden;
4301
+ position: relative;
4302
+ }
4303
+ * { box-sizing: border-box; }
4304
+ .sidebar-toggle {
4305
+ position: absolute;
4306
+ top: 50%;
4307
+ transform: translateY(-50%);
4308
+ left: 10px;
4309
+ width: 28px;
4310
+ height: 28px;
4311
+ border-radius: var(--me-radius);
4312
+ border: 1px solid var(--me-grey-200);
4313
+ background: white;
4314
+ cursor: pointer;
4315
+ display: flex;
4316
+ align-items: center;
4317
+ justify-content: center;
4318
+ z-index: 40;
4319
+ box-shadow: 0 1px 4px rgba(0,0,0,0.08);
4320
+ transition: all var(--me-transition);
4321
+ padding: 0;
4322
+ color: var(--me-grey-500);
4323
+ }
4324
+ .sidebar-toggle:hover {
4325
+ background: var(--me-grey-50);
4326
+ color: var(--me-primary);
4327
+ box-shadow: 0 2px 8px rgba(0,0,0,0.12);
4328
+ }
4329
+ .sidebar-toggle svg {
4330
+ width: 14px;
4331
+ height: 14px;
4332
+ stroke: currentColor;
4333
+ fill: none;
4334
+ stroke-width: 2;
4335
+ stroke-linecap: round;
4336
+ stroke-linejoin: round;
4337
+ transition: transform 200ms ease;
4338
+ }
4339
+ .sidebar-toggle.collapsed {
4340
+ width: 20px;
4341
+ height: 20px;
4342
+ border: none;
4343
+ background: transparent;
4344
+ box-shadow: none;
4345
+ color: var(--me-primary);
4346
+ animation: pulse-hint 2s ease-in-out infinite;
4347
+ }
4348
+ .sidebar-toggle.collapsed:hover {
4349
+ background: transparent;
4350
+ color: var(--me-primary-dark);
4351
+ animation: none;
4352
+ transform: translateY(-50%) scale(1.2);
4353
+ }
4354
+ .sidebar-toggle.collapsed svg {
4355
+ width: 22px;
4356
+ height: 22px;
4357
+ transform: rotate(180deg);
4358
+ }
4359
+ @keyframes pulse-hint {
4360
+ 0%, 100% { opacity: 0.5; }
4361
+ 50% { opacity: 1; }
4362
+ }
4363
+
4364
+ /* ── A11y dots when sidebar collapsed ── */
4365
+ .a11y-dots {
4366
+ position: absolute;
4367
+ left: 14px;
4368
+ top: calc(50% + 18px);
4369
+ display: flex;
4370
+ flex-direction: column;
4371
+ gap: 6px;
4372
+ z-index: 40;
4373
+ }
4374
+ .a11y-dot {
4375
+ width: 8px; height: 8px; border-radius: 50%;
4376
+ }
4377
+ .a11y-dot.error { background: #dc2626; animation: pulse-e 1.8s ease-in-out infinite; }
4378
+ .a11y-dot.warning { background: #d97706; animation: pulse-w 2.3s ease-in-out infinite; }
4379
+ .a11y-dot.info { background: #5d768b; animation: pulse-i 2.7s ease-in-out infinite; }
4380
+ @keyframes pulse-e {
4381
+ 0%, 100% { opacity: 0.4; transform: scale(0.85); }
4382
+ 50% { opacity: 1; transform: scale(1.1); }
4383
+ }
4384
+ @keyframes pulse-w {
4385
+ 0%, 100% { opacity: 0.35; transform: scale(0.9); }
4386
+ 60% { opacity: 1; transform: scale(1.15); }
4387
+ }
4388
+ @keyframes pulse-i {
4389
+ 0%, 100% { opacity: 0.3; transform: scale(0.9); }
4390
+ 40% { opacity: 0.9; transform: scale(1.05); }
4391
+ }
4392
+ `;
4393
+ Y([
4394
+ v({ type: Object })
4395
+ ], P.prototype, "options", 2);
4396
+ Y([
4397
+ E()
4398
+ ], P.prototype, "sidebarCollapsed", 2);
4399
+ Y([
4400
+ E()
4401
+ ], P.prototype, "a11yHasErrors", 2);
4402
+ Y([
4403
+ E()
4404
+ ], P.prototype, "a11yHasWarnings", 2);
4405
+ Y([
4406
+ E()
4407
+ ], P.prototype, "a11yHasInfos", 2);
4408
+ P = Y([
4409
+ S("mail-editor")
4410
+ ], P);
4411
+ function R(e, t) {
4412
+ customElements.get(e) || customElements.define(e, t);
4413
+ }
4414
+ R("mail-editor", P);
4415
+ R("me-editor-canvas", z);
4416
+ R("me-row-renderer", O);
4417
+ R("me-column-renderer", L);
4418
+ R("me-content-renderer", M);
4419
+ R("me-editor-sidebar", H);
4420
+ R("me-body-settings", K);
4421
+ R("me-property-panel", q);
4422
+ R("me-inline-toolbar", j);
4423
+ R("me-a11y-checker", U);
4424
+ const mo = customElements.get("mail-editor") !== void 0;
4425
+ export {
4426
+ mo as E,
4427
+ P as M,
4428
+ et as T,
4429
+ Ze as a,
4430
+ N as e,
4431
+ Pt as f,
4432
+ bo as j,
4433
+ p as s,
4434
+ Et as t
4435
+ };
4436
+ //# sourceMappingURL=index-2S5kBS5_.js.map