@emabuild/core 0.0.8 → 0.1.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 (33) hide show
  1. package/dist/canvas/content-renderer.d.ts.map +1 -1
  2. package/dist/canvas/row-renderer.d.ts.map +1 -1
  3. package/dist/{form-tool-C9ccGMTE.js → form-tool-DSXsWpjQ.js} +2 -2
  4. package/dist/{form-tool-C9ccGMTE.js.map → form-tool-DSXsWpjQ.js.map} +1 -1
  5. package/dist/{html-tool-Dx0bJnRa.js → html-tool-Be4HAhoj.js} +2 -2
  6. package/dist/{html-tool-Dx0bJnRa.js.map → html-tool-Be4HAhoj.js.map} +1 -1
  7. package/dist/index-CZt184D3.js +2695 -0
  8. package/dist/index-CZt184D3.js.map +1 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +5 -4
  12. package/dist/{menu-tool-D34uGd81.js → menu-tool-HPxxeiRV.js} +2 -2
  13. package/dist/{menu-tool-D34uGd81.js.map → menu-tool-HPxxeiRV.js.map} +1 -1
  14. package/dist/register-elements.d.ts +6 -0
  15. package/dist/register-elements.d.ts.map +1 -0
  16. package/dist/{social-tool-_2fRc5-k.js → social-tool-bc46FNgk.js} +2 -2
  17. package/dist/{social-tool-_2fRc5-k.js.map → social-tool-bc46FNgk.js.map} +1 -1
  18. package/dist/{table-tool-B_MVWfF5.js → table-tool-LzRcAPFb.js} +2 -2
  19. package/dist/{table-tool-B_MVWfF5.js.map → table-tool-LzRcAPFb.js.map} +1 -1
  20. package/dist/{timer-tool-BUl5TiH0.js → timer-tool-GPhdsvRE.js} +2 -2
  21. package/dist/{timer-tool-BUl5TiH0.js.map → timer-tool-GPhdsvRE.js.map} +1 -1
  22. package/dist/tools/built-in/image-tool.d.ts.map +1 -1
  23. package/dist/tools/helpers/index.d.ts +1 -1
  24. package/dist/tools/helpers/index.d.ts.map +1 -1
  25. package/dist/tools/helpers/value-extractor.d.ts +35 -0
  26. package/dist/tools/helpers/value-extractor.d.ts.map +1 -1
  27. package/dist/{video-tool-HOA8TCla.js → video-tool-CPjCWbTX.js} +2 -2
  28. package/dist/{video-tool-HOA8TCla.js.map → video-tool-CPjCWbTX.js.map} +1 -1
  29. package/package.json +3 -7
  30. package/dist/mail-editor-D3QbvBKs.js +0 -1569
  31. package/dist/mail-editor-D3QbvBKs.js.map +0 -1
  32. package/dist/mail-editor.js +0 -7
  33. package/dist/mail-editor.js.map +0 -1
@@ -0,0 +1,2695 @@
1
+ import { html as c, LitElement as v, css as $ } from "lit";
2
+ import { property as f, customElement as k } from "lit/decorators.js";
3
+ import { unsafeHTML as q } from "lit/directives/unsafe-html.js";
4
+ function lt() {
5
+ const t = {};
6
+ return {
7
+ getCounters() {
8
+ return { ...t };
9
+ },
10
+ setCounters(e) {
11
+ for (const o of Object.keys(t))
12
+ delete t[o];
13
+ Object.assign(t, e);
14
+ },
15
+ next(e) {
16
+ const o = t[e] ?? 0;
17
+ return t[e] = o + 1, t[e];
18
+ }
19
+ };
20
+ }
21
+ class dt {
22
+ constructor() {
23
+ this.listeners = /* @__PURE__ */ new Map();
24
+ }
25
+ /** Register a listener for an event */
26
+ on(e, o) {
27
+ this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(o);
28
+ }
29
+ /** Remove a specific listener */
30
+ off(e, o) {
31
+ this.listeners.get(e)?.delete(o);
32
+ }
33
+ /** Emit an event with a payload. Errors in listeners are caught and logged. */
34
+ emit(e, o) {
35
+ this.listeners.get(e)?.forEach((n) => {
36
+ try {
37
+ n(o);
38
+ } catch (i) {
39
+ console.error(`[emabuild] Error in "${e}" listener:`, i);
40
+ }
41
+ });
42
+ }
43
+ /** Remove all listeners, optionally scoped to a single event */
44
+ removeAllListeners(e) {
45
+ e ? this.listeners.delete(e) : this.listeners.clear();
46
+ }
47
+ }
48
+ class ct {
49
+ constructor(e = 50) {
50
+ this.undoStack = [], this.redoStack = [], this.maxHistory = e;
51
+ }
52
+ /** Whether there are states to undo to */
53
+ get canUndo() {
54
+ return this.undoStack.length > 0;
55
+ }
56
+ /** Whether there are states to redo to */
57
+ get canRedo() {
58
+ return this.redoStack.length > 0;
59
+ }
60
+ /** Save current design to the undo stack before a mutation */
61
+ push(e) {
62
+ this.undoStack.push(structuredClone(e)), this.undoStack.length > this.maxHistory && this.undoStack.shift(), this.redoStack = [];
63
+ }
64
+ /** Restore the previous state, pushing current state to redo. Returns the restored design or undefined. */
65
+ undo(e) {
66
+ const o = this.undoStack.pop();
67
+ if (o)
68
+ return this.redoStack.push(structuredClone(e)), o;
69
+ }
70
+ /** Restore the next state, pushing current state to undo. Returns the restored design or undefined. */
71
+ redo(e) {
72
+ const o = this.redoStack.pop();
73
+ if (o)
74
+ return this.undoStack.push(structuredClone(e)), o;
75
+ }
76
+ /** Clear all history (e.g. when loading a new design) */
77
+ clear() {
78
+ this.undoStack = [], this.redoStack = [];
79
+ }
80
+ }
81
+ function pt() {
82
+ return {
83
+ counters: { u_row: 0, u_column: 0 },
84
+ body: {
85
+ id: "u_body",
86
+ rows: [],
87
+ headers: [],
88
+ footers: [],
89
+ values: {
90
+ backgroundColor: "#e7e7e7",
91
+ contentAlign: "center",
92
+ contentVerticalAlign: "center",
93
+ contentWidth: "600px",
94
+ fontFamily: { label: "Arial", value: "arial,helvetica,sans-serif" },
95
+ textColor: "#000000",
96
+ linkStyle: {
97
+ body: !0,
98
+ linkColor: "#0000ee",
99
+ linkHoverColor: "#0000ee",
100
+ linkUnderline: !0,
101
+ linkHoverUnderline: !0
102
+ },
103
+ preheaderText: "",
104
+ popupPosition: "center",
105
+ popupWidth: "600px",
106
+ popupHeight: "auto",
107
+ borderRadius: "10px",
108
+ popupBackgroundColor: "#FFFFFF",
109
+ popupBackgroundImage: { url: "", fullWidth: !0, repeat: "no-repeat", center: !0, cover: !0 },
110
+ popupOverlay_backgroundColor: "rgba(0, 0, 0, 0.1)",
111
+ popupCloseButton_position: "top-right",
112
+ popupCloseButton_backgroundColor: "#DDDDDD",
113
+ popupCloseButton_iconColor: "#000000",
114
+ popupCloseButton_borderRadius: "0px",
115
+ popupCloseButton_margin: "0px",
116
+ popupCloseButton_action: {
117
+ name: "close_popup",
118
+ attrs: { onClick: "document.querySelector('.u-popup-container').style.display = 'none';" }
119
+ },
120
+ _meta: { htmlID: "u_body", htmlClassNames: "u_body" }
121
+ }
122
+ },
123
+ schemaVersion: 16
124
+ };
125
+ }
126
+ function ht(t, e) {
127
+ const o = t.next("u_row"), n = e.map(() => {
128
+ const i = t.next("u_column");
129
+ return {
130
+ id: `u_column_${i}`,
131
+ contents: [],
132
+ values: {
133
+ backgroundColor: "",
134
+ padding: "0px",
135
+ border: {},
136
+ borderRadius: "0px",
137
+ _meta: { htmlID: `u_column_${i}`, htmlClassNames: "u_column" }
138
+ }
139
+ };
140
+ });
141
+ return {
142
+ id: `u_row_${o}`,
143
+ cells: e,
144
+ columns: n,
145
+ values: {
146
+ displayCondition: null,
147
+ columns: !1,
148
+ backgroundColor: "",
149
+ columnsBackgroundColor: "",
150
+ backgroundImage: { url: "", fullWidth: !0, repeat: !1, center: !0, cover: !1 },
151
+ padding: "0px",
152
+ anchor: "",
153
+ hideDesktop: !1,
154
+ hideMobile: !1,
155
+ _meta: { htmlID: `u_row_${o}`, htmlClassNames: "u_row" }
156
+ }
157
+ };
158
+ }
159
+ function ut(t, e, o = {}) {
160
+ const n = t.next(`u_content_${e}`), i = `u_content_${e}_${n}`;
161
+ return {
162
+ id: i,
163
+ type: e,
164
+ values: {
165
+ containerPadding: "10px",
166
+ anchor: "",
167
+ hideDesktop: !1,
168
+ hideMobile: !1,
169
+ displayCondition: null,
170
+ _meta: { htmlID: i, htmlClassNames: `u_content_${e}` },
171
+ ...o
172
+ }
173
+ };
174
+ }
175
+ function U(t, e) {
176
+ return t.body.rows.find((o) => o.id === e);
177
+ }
178
+ function H(t, e) {
179
+ for (const o of t.body.rows) {
180
+ const n = o.columns.find((i) => i.id === e);
181
+ if (n) return n;
182
+ }
183
+ }
184
+ function O(t, e) {
185
+ for (const o of t.body.rows)
186
+ for (const n of o.columns) {
187
+ const i = n.contents.find((r) => r.id === e);
188
+ if (i) return i;
189
+ }
190
+ }
191
+ function gt(t, e) {
192
+ for (const o of t.body.rows)
193
+ for (const n of o.columns)
194
+ if (n.contents.some((i) => i.id === e)) return n;
195
+ }
196
+ function bt(t, e) {
197
+ for (const o of t.body.rows)
198
+ if (o.columns.some((n) => n.id === e)) return o;
199
+ }
200
+ function F(t, e) {
201
+ return t.body.rows.findIndex((o) => o.id === e);
202
+ }
203
+ class ft {
204
+ constructor() {
205
+ this.history = new ct(), this.counterManager = lt(), this.subscribers = /* @__PURE__ */ new Set(), this.channelSubscribers = /* @__PURE__ */ new Map(), this.events = new dt(), this._selectedId = null, this._hoveredId = null, this._viewMode = "desktop", this._activeTab = "content", this.design = pt();
206
+ }
207
+ // ── Subscriptions ──────────────────────────────────────────
208
+ /** Subscribe to ALL state changes (legacy). Returns an unsubscribe function. */
209
+ subscribe(e) {
210
+ return this.subscribers.add(e), () => this.subscribers.delete(e);
211
+ }
212
+ /**
213
+ * Subscribe to specific channels only. The callback is invoked only
214
+ * when one of the listed channels fires. Returns an unsubscribe function.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * // Only re-render when the design changes, not on hover/selection
219
+ * store.subscribeChannels(['design'], () => this.requestUpdate());
220
+ * ```
221
+ */
222
+ subscribeChannels(e, o) {
223
+ for (const n of e)
224
+ this.channelSubscribers.has(n) || this.channelSubscribers.set(n, /* @__PURE__ */ new Set()), this.channelSubscribers.get(n).add(o);
225
+ return () => {
226
+ for (const n of e)
227
+ this.channelSubscribers.get(n)?.delete(o);
228
+ };
229
+ }
230
+ /** Notify legacy (all) subscribers */
231
+ notify() {
232
+ this.subscribers.forEach((e) => e());
233
+ }
234
+ /** Notify only subscribers of specific channels + legacy subscribers */
235
+ notifyChannels(...e) {
236
+ const o = /* @__PURE__ */ new Set();
237
+ for (const n of this.subscribers) o.add(n);
238
+ for (const n of e) {
239
+ const i = this.channelSubscribers.get(n);
240
+ if (i) for (const r of i) o.add(r);
241
+ }
242
+ o.forEach((n) => n());
243
+ }
244
+ // ── Getters ────────────────────────────────────────────────
245
+ /** Get the full design document */
246
+ getDesign() {
247
+ return this.design;
248
+ }
249
+ /** Get the design body */
250
+ getBody() {
251
+ return this.design.body;
252
+ }
253
+ /** Get all rows */
254
+ getRows() {
255
+ return this.design.body.rows;
256
+ }
257
+ /** Get body-level values (background, fonts, etc.) */
258
+ getBodyValues() {
259
+ return this.design.body.values;
260
+ }
261
+ get selectedId() {
262
+ return this._selectedId;
263
+ }
264
+ get hoveredId() {
265
+ return this._hoveredId;
266
+ }
267
+ get viewMode() {
268
+ return this._viewMode;
269
+ }
270
+ get activeTab() {
271
+ return this._activeTab;
272
+ }
273
+ get canUndo() {
274
+ return this.history.canUndo;
275
+ }
276
+ get canRedo() {
277
+ return this.history.canRedo;
278
+ }
279
+ // ── Design Loading ─────────────────────────────────────────
280
+ /** Load a design document, resetting history and selection */
281
+ loadDesign(e) {
282
+ this.design = structuredClone(e), this.counterManager.setCounters(this.design.counters), this.history.clear(), this._selectedId = null, this.notifyChannels("design", "selection"), this.events.emit("design:loaded", { design: this.design });
283
+ }
284
+ // ── Undo / Redo ────────────────────────────────────────────
285
+ undo() {
286
+ const e = this.history.undo(this.design);
287
+ e && (this.design = e, this.counterManager.setCounters(this.design.counters), this.notifyChannels("design"), this.emitUpdate("content_updated"));
288
+ }
289
+ redo() {
290
+ const e = this.history.redo(this.design);
291
+ e && (this.design = e, this.counterManager.setCounters(this.design.counters), this.notifyChannels("design"), this.emitUpdate("content_updated"));
292
+ }
293
+ // ── Selection / UI State ───────────────────────────────────
294
+ select(e) {
295
+ this._selectedId = e, this.notifyChannels("selection");
296
+ }
297
+ hover(e) {
298
+ this._hoveredId = e, this.notifyChannels("hover");
299
+ }
300
+ setViewMode(e) {
301
+ this._viewMode = e, this.notifyChannels("viewMode");
302
+ }
303
+ setActiveTab(e) {
304
+ this._activeTab = e, this.notifyChannels("activeTab");
305
+ }
306
+ // ── Row Operations ─────────────────────────────────────────
307
+ /** Add a row at the given index (or at the end) */
308
+ addRow(e, o) {
309
+ this.history.push(this.design);
310
+ const n = this.design.body.rows, i = structuredClone(e);
311
+ o !== void 0 && o >= 0 && o <= n.length ? n.splice(o, 0, i) : n.push(i), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("row_added", i);
312
+ }
313
+ /** Remove a row by ID */
314
+ removeRow(e) {
315
+ const o = F(this.design, e);
316
+ o !== -1 && (this.history.push(this.design), this.design.body.rows.splice(o, 1), this._selectedId === e && (this._selectedId = null), this.notifyChannels("design"), this.emitUpdate("row_removed"));
317
+ }
318
+ /** Move a row from one index to another */
319
+ moveRow(e, o) {
320
+ this.history.push(this.design);
321
+ const n = this.design.body.rows, [i] = n.splice(e, 1);
322
+ n.splice(o, 0, i), this.notifyChannels("design"), this.emitUpdate("row_reordered");
323
+ }
324
+ /** Duplicate a row, assigning fresh IDs to all nested elements */
325
+ duplicateRow(e) {
326
+ const o = U(this.design, e);
327
+ if (!o) return;
328
+ this.history.push(this.design);
329
+ const n = structuredClone(o), i = this.counterManager.next("u_row");
330
+ n.id = `u_row_${i}`, n.values._meta = { htmlID: n.id, htmlClassNames: "u_row" };
331
+ for (const s of n.columns) {
332
+ const l = this.counterManager.next("u_column");
333
+ s.id = `u_column_${l}`, s.values._meta = { htmlID: s.id, htmlClassNames: "u_column" };
334
+ for (const d of s.contents) {
335
+ const p = this.counterManager.next(`u_content_${d.type}`);
336
+ d.id = `u_content_${d.type}_${p}`, d.values._meta = { htmlID: d.id, htmlClassNames: `u_content_${d.type}` };
337
+ }
338
+ }
339
+ const r = F(this.design, e);
340
+ this.design.body.rows.splice(r + 1, 0, n), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("row_added", n);
341
+ }
342
+ /** Get the index of a row */
343
+ getRowIndex(e) {
344
+ return F(this.design, e);
345
+ }
346
+ /** Update row-level values */
347
+ updateRowValues(e, o) {
348
+ const n = U(this.design, e);
349
+ n && (this.history.push(this.design), Object.assign(n.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"));
350
+ }
351
+ // ── Column Operations ──────────────────────────────────────
352
+ /** Update column-level values */
353
+ updateColumnValues(e, o) {
354
+ const n = H(this.design, e);
355
+ n && (this.history.push(this.design), Object.assign(n.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"));
356
+ }
357
+ // ── Content Operations ─────────────────────────────────────
358
+ /** Add content to a column at the given index */
359
+ addContent(e, o, n) {
360
+ const i = H(this.design, e);
361
+ if (!i) return;
362
+ this.history.push(this.design);
363
+ const r = structuredClone(o);
364
+ n !== void 0 && n >= 0 && n <= i.contents.length ? i.contents.splice(n, 0, r) : i.contents.push(r), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("content_added", r);
365
+ }
366
+ /** Remove a content block by ID */
367
+ removeContent(e) {
368
+ for (const o of this.design.body.rows)
369
+ for (const n of o.columns) {
370
+ const i = n.contents.findIndex((r) => r.id === e);
371
+ if (i !== -1) {
372
+ this.history.push(this.design), n.contents.splice(i, 1), this._selectedId === e && (this._selectedId = null), this.notifyChannels("design"), this.emitUpdate("content_removed");
373
+ return;
374
+ }
375
+ }
376
+ }
377
+ /** Update content values by ID */
378
+ updateContentValues(e, o) {
379
+ const n = O(this.design, e);
380
+ n && (this.history.push(this.design), Object.assign(n.values, o), this.notifyChannels("design"), this.emitUpdate("content_updated"));
381
+ }
382
+ /** Move a content block to a different column at a given index */
383
+ moveContent(e, o, n) {
384
+ const i = O(this.design, e);
385
+ if (!i) return;
386
+ this.history.push(this.design);
387
+ for (const s of this.design.body.rows)
388
+ for (const l of s.columns) {
389
+ const d = l.contents.findIndex((p) => p.id === e);
390
+ if (d !== -1) {
391
+ l.contents.splice(d, 1);
392
+ break;
393
+ }
394
+ }
395
+ const r = H(this.design, o);
396
+ r && r.contents.splice(n, 0, i), this.notifyChannels("design"), this.emitUpdate("content_reordered");
397
+ }
398
+ /** Duplicate a content block, inserting the copy right after the original */
399
+ duplicateContent(e) {
400
+ const o = O(this.design, e);
401
+ if (o)
402
+ for (const n of this.design.body.rows)
403
+ for (const i of n.columns) {
404
+ const r = i.contents.findIndex((s) => s.id === e);
405
+ if (r !== -1) {
406
+ this.history.push(this.design);
407
+ const s = structuredClone(o), l = this.counterManager.next(`u_content_${o.type}`);
408
+ s.id = `u_content_${o.type}_${l}`, s.values._meta = { htmlID: s.id, htmlClassNames: `u_content_${o.type}` }, i.contents.splice(r + 1, 0, s), this.syncCounters(), this.notifyChannels("design"), this.emitUpdate("content_added", s);
409
+ return;
410
+ }
411
+ }
412
+ }
413
+ // ── Body Values ────────────────────────────────────────────
414
+ /** Update body-level values (background, fonts, etc.) */
415
+ updateBodyValues(e) {
416
+ this.history.push(this.design), Object.assign(this.design.body.values, e), this.notifyChannels("design"), this.emitUpdate("body_updated");
417
+ }
418
+ // ── Lookups (delegate to design-lookup) ────────────────────
419
+ findRow(e) {
420
+ return U(this.design, e);
421
+ }
422
+ findColumn(e) {
423
+ return H(this.design, e);
424
+ }
425
+ findContent(e) {
426
+ return O(this.design, e);
427
+ }
428
+ findParentColumn(e) {
429
+ return gt(this.design, e);
430
+ }
431
+ findParentRow(e) {
432
+ return bt(this.design, e);
433
+ }
434
+ // ── Factory Methods (delegate to design-factory) ───────────
435
+ /** Create a new row with the given column layout */
436
+ createRow(e) {
437
+ const o = ht(this.counterManager, e);
438
+ return this.syncCounters(), o;
439
+ }
440
+ /** Create a new content block for the given tool type */
441
+ createContent(e, o = {}) {
442
+ const n = ut(this.counterManager, e, o);
443
+ return this.syncCounters(), n;
444
+ }
445
+ // ── Private Helpers ────────────────────────────────────────
446
+ syncCounters() {
447
+ this.design.counters = this.counterManager.getCounters();
448
+ }
449
+ emitUpdate(e, o) {
450
+ this.events.emit("design:updated", { type: e, item: o });
451
+ }
452
+ }
453
+ class mt {
454
+ constructor() {
455
+ this.tools = /* @__PURE__ */ new Map(), this.lazyLoaders = /* @__PURE__ */ new Map(), this.lazyMeta = /* @__PURE__ */ new Map(), this.loadingPromises = /* @__PURE__ */ new Map();
456
+ }
457
+ /** Register a tool eagerly (available immediately) */
458
+ register(e) {
459
+ this.tools.set(e.name, e), this.lazyLoaders.delete(e.name), this.lazyMeta.delete(e.name);
460
+ }
461
+ /**
462
+ * Register a tool lazily. The tool's code is only loaded when first needed.
463
+ * Provide metadata (name, label, icon) so the tool can appear in the sidebar.
464
+ *
465
+ * @param meta - Display metadata for the sidebar palette
466
+ * @param loader - Async function that imports and returns the tool definition
467
+ *
468
+ * @example
469
+ * ```ts
470
+ * registry.registerLazy(
471
+ * { name: 'timer', label: 'Timer', icon: '<svg>...</svg>', position: 11 },
472
+ * () => import('./built-in/timer-tool.js').then(m => m.timerTool),
473
+ * );
474
+ * ```
475
+ */
476
+ registerLazy(e, o) {
477
+ this.tools.has(e.name) || (this.lazyMeta.set(e.name, e), this.lazyLoaders.set(e.name, o));
478
+ }
479
+ /** Get a tool by name. Returns undefined if not loaded yet (use ensureLoaded for lazy tools). */
480
+ get(e) {
481
+ return this.tools.get(e);
482
+ }
483
+ /** Check if a tool is registered (eager or lazy) */
484
+ has(e) {
485
+ return this.tools.has(e) || this.lazyLoaders.has(e);
486
+ }
487
+ /** Check if a tool is fully loaded and ready to render */
488
+ isLoaded(e) {
489
+ return this.tools.has(e);
490
+ }
491
+ /**
492
+ * Ensure a lazy tool is loaded. Returns the tool definition.
493
+ * If the tool is already loaded, returns it immediately.
494
+ * If it's being loaded, returns the in-flight promise.
495
+ */
496
+ async ensureLoaded(e) {
497
+ if (this.tools.has(e)) return this.tools.get(e);
498
+ if (this.loadingPromises.has(e)) return this.loadingPromises.get(e);
499
+ const o = this.lazyLoaders.get(e);
500
+ if (!o) return;
501
+ const n = o().then((i) => (this.tools.set(e, i), this.lazyLoaders.delete(e), this.lazyMeta.delete(e), this.loadingPromises.delete(e), i));
502
+ return this.loadingPromises.set(e, n), n;
503
+ }
504
+ /**
505
+ * Get all tools for display in the sidebar palette.
506
+ * Returns both loaded tools and lazy tool metadata, sorted by position.
507
+ */
508
+ getAll() {
509
+ return Array.from(this.tools.values()).sort((e, o) => (e.position ?? 0) - (o.position ?? 0));
510
+ }
511
+ /**
512
+ * Get all tool names and display metadata (including lazy tools not yet loaded).
513
+ * Used by the sidebar to show all available tools.
514
+ */
515
+ getAllMeta() {
516
+ const e = [];
517
+ for (const o of this.tools.values())
518
+ e.push({ name: o.name, label: o.label, icon: o.icon, position: o.position });
519
+ for (const o of this.lazyMeta.values())
520
+ e.push(o);
521
+ return e.sort((o, n) => (o.position ?? 0) - (n.position ?? 0));
522
+ }
523
+ /** Get default values for a tool. Loads lazily if needed (sync — returns empty if not loaded). */
524
+ getDefaultValues(e) {
525
+ const o = this.tools.get(e);
526
+ if (!o) return {};
527
+ const n = { ...o.defaultValues };
528
+ for (const i of Object.values(o.options))
529
+ for (const [r, s] of Object.entries(i.options))
530
+ r in n || (n[r] = s.defaultValue);
531
+ return n;
532
+ }
533
+ /** Get property groups for a tool */
534
+ getPropertyGroups(e) {
535
+ return this.tools.get(e)?.options ?? {};
536
+ }
537
+ }
538
+ const E = {
539
+ /** ID of the content currently being dragged (null if not dragging content) */
540
+ draggingContentId: null,
541
+ startContentDrag(t) {
542
+ this.draggingContentId = t;
543
+ },
544
+ reset() {
545
+ this.draggingContentId = null;
546
+ }
547
+ };
548
+ function J(t) {
549
+ const e = document.createElement("div");
550
+ return Object.assign(e.style, {
551
+ position: "absolute",
552
+ left: "0",
553
+ right: "0",
554
+ height: "3px",
555
+ background: t,
556
+ borderRadius: "2px",
557
+ pointerEvents: "none",
558
+ zIndex: "1000",
559
+ display: "none",
560
+ boxShadow: `0 0 6px ${t}80`
561
+ }), e;
562
+ }
563
+ function X(t, e, o, n, i = "4px") {
564
+ t.parentNode !== e && (t.remove(), e.appendChild(t));
565
+ const s = (e instanceof ShadowRoot ? e.host : e).getBoundingClientRect();
566
+ let l;
567
+ o.length === 0 || n === 0 ? l = o.length === 0 ? 0 : o[0].getBoundingClientRect().top - s.top : n >= o.length ? l = o[o.length - 1].getBoundingClientRect().bottom - s.top : l = o[n].getBoundingClientRect().top - s.top, Object.assign(t.style, {
568
+ display: "block",
569
+ top: `${l}px`,
570
+ left: i,
571
+ right: i,
572
+ width: "auto"
573
+ });
574
+ }
575
+ function M(t) {
576
+ t && (t.style.display = "none");
577
+ }
578
+ function N(t, e) {
579
+ const o = (t instanceof ShadowRoot, t.children);
580
+ for (const n of Array.from(o)) {
581
+ const i = n;
582
+ e(i), i.shadowRoot && N(i.shadowRoot, e), i.children?.length && N(i, e);
583
+ }
584
+ }
585
+ function Q(t, e) {
586
+ const o = [];
587
+ return N(t, (n) => {
588
+ n.matches?.(e) && o.push(n);
589
+ }), o;
590
+ }
591
+ class xt {
592
+ constructor(e, o, n) {
593
+ this.currentDrop = null, this.contentIndicator = null, this.rowIndicator = null, this.onDragOver = (i) => {
594
+ const r = i.dataTransfer?.types || [], s = r.includes("application/maileditor-tool"), l = r.includes("application/maileditor-layout"), d = r.includes("application/maileditor-content") || !!E.draggingContentId;
595
+ !s && !d && !l || (i.preventDefault(), i.dataTransfer.dropEffect = s || l ? "copy" : "move", l ? (this.currentDrop = this.findRowDropTarget(i.clientY), M(this.contentIndicator), this.showRowIndicator()) : (this.currentDrop = this.findContentDropTarget(i.clientX, i.clientY), M(this.rowIndicator), this.showContentIndicator()));
596
+ }, this.onDrop = (i) => {
597
+ i.preventDefault(), this.hideAllIndicators();
598
+ const r = this.currentDrop, s = i.dataTransfer?.getData("application/maileditor-layout");
599
+ if (s) {
600
+ this.handleLayoutDrop(JSON.parse(s), r), this.reset();
601
+ return;
602
+ }
603
+ const l = i.dataTransfer?.getData("application/maileditor-tool");
604
+ if (l) {
605
+ this.handleToolDrop(l, r), this.reset();
606
+ return;
607
+ }
608
+ const d = i.dataTransfer?.getData("application/maileditor-content") || E.draggingContentId;
609
+ d && this.handleContentDrop(d, r), this.reset();
610
+ }, this.onDragEnd = () => {
611
+ this.hideAllIndicators(), this.reset();
612
+ }, this.onDragLeave = (i) => {
613
+ const r = i.relatedTarget;
614
+ (!r || !this.root.contains(r)) && (this.hideAllIndicators(), this.currentDrop = null);
615
+ }, this.store = e, this.toolRegistry = o, this.root = n;
616
+ }
617
+ /** Attach all drag event listeners to the shadow root */
618
+ attach() {
619
+ 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 = J("#3b82f6"), this.rowIndicator = J("#8b5cf6");
620
+ }
621
+ /** Remove all event listeners and clean up indicators */
622
+ detach() {
623
+ 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();
624
+ }
625
+ // ── Drop Handlers ──────────────────────────────────────────
626
+ handleLayoutDrop(e, o = this.currentDrop) {
627
+ const n = this.store.createRow(e), i = o?.type === "row" ? o.rowIndex : void 0;
628
+ this.store.addRow(n, i);
629
+ }
630
+ async handleToolDrop(e, o) {
631
+ if (await this.toolRegistry.ensureLoaded(e), o?.type === "content" && o.columnId) {
632
+ const n = this.toolRegistry.getDefaultValues(e), i = this.store.createContent(e, n);
633
+ this.store.addContent(o.columnId, i, o.contentIndex), this.store.select(i.id);
634
+ } else {
635
+ const n = this.store.createRow([1]);
636
+ this.store.addRow(n);
637
+ const i = this.toolRegistry.getDefaultValues(e), r = this.store.createContent(e, i);
638
+ this.store.addContent(n.columns[0].id, r), this.store.select(r.id);
639
+ }
640
+ }
641
+ handleContentDrop(e, o) {
642
+ o?.type === "content" && o.columnId && (this.store.moveContent(e, o.columnId, o.contentIndex), this.store.select(e));
643
+ }
644
+ // ── Drop Target Detection ─────────────────────────────────
645
+ findRowDropTarget(e) {
646
+ const o = this.root.querySelector("me-editor-canvas");
647
+ if (!o?.shadowRoot) return null;
648
+ const n = Array.from(o.shadowRoot.querySelectorAll("me-row-renderer"));
649
+ if (n.length === 0) return { type: "row", rowIndex: 0, y: 0 };
650
+ let i = Math.abs(e - n[0].getBoundingClientRect().top), r = { type: "row", rowIndex: 0, y: n[0].getBoundingClientRect().top };
651
+ for (let s = 0; s < n.length; s++) {
652
+ const l = n[s].getBoundingClientRect().bottom, d = Math.abs(e - l);
653
+ d < i && (i = d, r = { type: "row", rowIndex: s + 1, y: l });
654
+ }
655
+ return r;
656
+ }
657
+ findContentDropTarget(e, o) {
658
+ const n = Q(this.root, "me-column-renderer");
659
+ let i = null, r = 1 / 0;
660
+ for (const s of n) {
661
+ const l = s.dataset.columnId;
662
+ if (!l || !s.shadowRoot) continue;
663
+ const d = s.getBoundingClientRect();
664
+ if (e < d.left || e > d.right) continue;
665
+ const p = Array.from(s.shadowRoot.querySelectorAll("me-content-renderer"));
666
+ if (p.length === 0) {
667
+ const u = Math.abs(o - (d.top + d.height / 2));
668
+ u < r && (r = u, i = { type: "content", columnId: l, contentIndex: 0, y: d.top + d.height / 2 });
669
+ continue;
670
+ }
671
+ const h = p[0].getBoundingClientRect().top;
672
+ let g = Math.abs(o - h);
673
+ g < r && (r = g, i = { type: "content", columnId: l, contentIndex: 0, y: h });
674
+ for (let u = 0; u < p.length; u++) {
675
+ const m = p[u].getBoundingClientRect(), x = p[u + 1]?.getBoundingClientRect(), b = x ? (m.bottom + x.top) / 2 : m.bottom;
676
+ g = Math.abs(o - b), g < r && (r = g, i = { type: "content", columnId: l, contentIndex: u + 1, y: b });
677
+ }
678
+ }
679
+ return i;
680
+ }
681
+ // ── Indicator Positioning ──────────────────────────────────
682
+ showContentIndicator() {
683
+ if (!this.contentIndicator || !this.currentDrop?.columnId) {
684
+ M(this.contentIndicator);
685
+ return;
686
+ }
687
+ const o = Q(this.root, "me-column-renderer").find((i) => i.dataset.columnId === this.currentDrop.columnId);
688
+ if (!o?.shadowRoot) return;
689
+ const n = Array.from(o.shadowRoot.querySelectorAll("me-content-renderer"));
690
+ X(this.contentIndicator, o.shadowRoot, n, this.currentDrop.contentIndex ?? 0, "4px");
691
+ }
692
+ showRowIndicator() {
693
+ if (!this.rowIndicator || !this.currentDrop) {
694
+ M(this.rowIndicator);
695
+ return;
696
+ }
697
+ const e = this.root.querySelector("me-editor-canvas"), o = e?.shadowRoot?.querySelector(".canvas-body");
698
+ if (!o) return;
699
+ const n = Array.from(e.shadowRoot.querySelectorAll("me-row-renderer"));
700
+ X(this.rowIndicator, o, n, this.currentDrop.rowIndex ?? 0, "0");
701
+ }
702
+ hideAllIndicators() {
703
+ M(this.contentIndicator), M(this.rowIndicator);
704
+ }
705
+ reset() {
706
+ this.currentDrop = null, E.reset();
707
+ }
708
+ }
709
+ function a(t, e, o = "") {
710
+ const n = t[e];
711
+ return typeof n == "string" && n !== "" ? n : typeof n == "number" ? String(n) : o;
712
+ }
713
+ function Z(t) {
714
+ return typeof t == "string" ? t : t && typeof t == "object" && "url" in t && t.url || "";
715
+ }
716
+ function tt(t) {
717
+ if (t && typeof t == "object") {
718
+ const e = t;
719
+ return {
720
+ width: typeof e.width == "number" ? e.width : void 0,
721
+ maxWidth: typeof e.maxWidth == "string" ? e.maxWidth : void 0
722
+ };
723
+ }
724
+ return {};
725
+ }
726
+ function yt(t) {
727
+ const e = t.action;
728
+ if (e && typeof e == "object") {
729
+ const o = e.values;
730
+ return {
731
+ href: o?.href || "",
732
+ target: o?.target || "_blank"
733
+ };
734
+ }
735
+ return {
736
+ href: a(t, "href"),
737
+ target: a(t, "target", "_blank")
738
+ };
739
+ }
740
+ function et(t, e = "") {
741
+ const o = t.text;
742
+ if (typeof o == "string" && o !== "") return o;
743
+ const n = t.textJson;
744
+ if (typeof n == "string")
745
+ try {
746
+ const i = JSON.parse(n), r = [], s = (l) => {
747
+ typeof l.text == "string" && r.push(l.text), l.type === "linebreak" && r.push("<br/>");
748
+ const d = l.children;
749
+ d && d.forEach(s);
750
+ };
751
+ if (s(i.root || i), r.length > 0) return r.join("");
752
+ } catch {
753
+ }
754
+ return e;
755
+ }
756
+ function se(t, e) {
757
+ if (typeof t != "string") return e;
758
+ try {
759
+ return JSON.parse(t);
760
+ } catch {
761
+ return e;
762
+ }
763
+ }
764
+ function V(t, e) {
765
+ const { padding: o, align: n = "left", extraTdStyle: i = "" } = e;
766
+ return `<table role="presentation" cellpadding="0" cellspacing="0" width="100%" border="0">
767
+ <tbody><tr><td style="${`overflow-wrap:break-word;word-break:break-word;padding:${o};font-family:arial,helvetica,sans-serif;${i}`}" align="${n}">
768
+ ${t}
769
+ </td></tr></tbody>
770
+ </table>`;
771
+ }
772
+ function wt(t, e, o) {
773
+ const { bgColor: n, textColor: i, fontSize: r, fontWeight: s, borderRadius: l } = o, d = parseInt(l) || 0;
774
+ if (d <= 0) return "";
775
+ const p = Math.round(d / 20 * 100);
776
+ return `<!--[if mso]>
777
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${e}" style="height:auto;v-text-anchor:middle;width:auto;" arcsize="${p}%" stroke="f" fillcolor="${n}">
778
+ <w:anchorlock/>
779
+ <center style="color:${i};font-family:arial,helvetica,sans-serif;font-size:${r};font-weight:${s};">${t}</center>
780
+ </v:roundrect>
781
+ <![endif]-->`;
782
+ }
783
+ const vt = {
784
+ name: "text",
785
+ label: "Text",
786
+ 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>',
787
+ supportedDisplayModes: ["email", "web"],
788
+ position: 1,
789
+ options: {
790
+ text: {
791
+ title: "Text",
792
+ options: {
793
+ text: {
794
+ label: "Text Content",
795
+ defaultValue: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
796
+ widget: "rich_text"
797
+ }
798
+ }
799
+ },
800
+ style: {
801
+ title: "Style",
802
+ options: {
803
+ color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
804
+ backgroundColor: { label: "Background Color", defaultValue: "", widget: "color_picker" },
805
+ textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
806
+ lineHeight: {
807
+ label: "Line Height",
808
+ defaultValue: "140%",
809
+ widget: "dropdown",
810
+ widgetParams: { options: [
811
+ { label: "100%", value: "100%" },
812
+ { label: "120%", value: "120%" },
813
+ { label: "140%", value: "140%" },
814
+ { label: "160%", value: "160%" },
815
+ { label: "180%", value: "180%" },
816
+ { label: "200%", value: "200%" }
817
+ ] }
818
+ }
819
+ }
820
+ },
821
+ spacing: {
822
+ title: "Spacing",
823
+ options: {
824
+ containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
825
+ }
826
+ },
827
+ general: {
828
+ title: "General",
829
+ options: {
830
+ anchor: { label: "Anchor", defaultValue: "", widget: "text" },
831
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
832
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
833
+ }
834
+ }
835
+ },
836
+ defaultValues: {
837
+ text: '<p style="font-size: 14px;">This is a new text block. Change the text.</p>',
838
+ color: "#000000",
839
+ backgroundColor: "",
840
+ lineHeight: "140%",
841
+ containerPadding: "10px",
842
+ textAlign: "left"
843
+ },
844
+ renderer: {
845
+ renderEditor(t) {
846
+ const e = a(t, "containerPadding", "10px"), o = a(t, "backgroundColor", "transparent"), n = a(t, "color", "inherit"), i = a(t, "lineHeight", "140%"), r = a(t, "text");
847
+ return c`
848
+ <div style="padding:${e};background-color:${o};color:${n};line-height:${i};word-break:break-word;">
849
+ ${q(r)}
850
+ </div>
851
+ `;
852
+ },
853
+ renderHtml(t) {
854
+ const e = a(t, "containerPadding", "10px"), o = a(t, "backgroundColor"), n = a(t, "color", "#000000"), i = a(t, "lineHeight", "140%"), r = a(t, "textAlign", "left"), s = a(t, "text"), l = o ? `background-color:${o};` : "", d = `<div style="font-size:14px;color:${n};line-height:${i};text-align:${r};word-wrap:break-word;">${s}</div>`;
855
+ return V(d, { padding: e, extraTdStyle: l });
856
+ }
857
+ }
858
+ }, $t = {
859
+ name: "heading",
860
+ label: "Heading",
861
+ 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>',
862
+ supportedDisplayModes: ["email", "web"],
863
+ position: 2,
864
+ options: {
865
+ text: {
866
+ title: "Heading",
867
+ options: {
868
+ text: { label: "Text", defaultValue: "Heading", widget: "text" },
869
+ headingType: {
870
+ label: "Heading Type",
871
+ defaultValue: "h1",
872
+ widget: "dropdown",
873
+ widgetParams: { options: [
874
+ { label: "H1", value: "h1" },
875
+ { label: "H2", value: "h2" },
876
+ { label: "H3", value: "h3" },
877
+ { label: "H4", value: "h4" }
878
+ ] }
879
+ }
880
+ }
881
+ },
882
+ style: {
883
+ title: "Style",
884
+ options: {
885
+ fontSize: {
886
+ label: "Font Size",
887
+ defaultValue: "22px",
888
+ widget: "dropdown",
889
+ widgetParams: { options: [
890
+ { label: "14px", value: "14px" },
891
+ { label: "16px", value: "16px" },
892
+ { label: "18px", value: "18px" },
893
+ { label: "20px", value: "20px" },
894
+ { label: "22px", value: "22px" },
895
+ { label: "26px", value: "26px" },
896
+ { label: "30px", value: "30px" },
897
+ { label: "36px", value: "36px" },
898
+ { label: "48px", value: "48px" },
899
+ { label: "60px", value: "60px" }
900
+ ] }
901
+ },
902
+ color: { label: "Text Color", defaultValue: "#000000", widget: "color_picker" },
903
+ textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
904
+ fontWeight: {
905
+ label: "Font Weight",
906
+ defaultValue: "700",
907
+ widget: "dropdown",
908
+ widgetParams: { options: [
909
+ { label: "Normal", value: "400" },
910
+ { label: "Medium", value: "500" },
911
+ { label: "Semi Bold", value: "600" },
912
+ { label: "Bold", value: "700" },
913
+ { label: "Extra Bold", value: "800" }
914
+ ] }
915
+ },
916
+ lineHeight: {
917
+ label: "Line Height",
918
+ defaultValue: "140%",
919
+ widget: "dropdown",
920
+ widgetParams: { options: [
921
+ { label: "100%", value: "100%" },
922
+ { label: "120%", value: "120%" },
923
+ { label: "140%", value: "140%" },
924
+ { label: "160%", value: "160%" },
925
+ { label: "180%", value: "180%" },
926
+ { label: "200%", value: "200%" }
927
+ ] }
928
+ },
929
+ letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
930
+ }
931
+ },
932
+ spacing: {
933
+ title: "Spacing",
934
+ options: {
935
+ containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
936
+ }
937
+ },
938
+ general: {
939
+ title: "General",
940
+ options: {
941
+ anchor: { label: "Anchor", defaultValue: "", widget: "text" },
942
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
943
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
944
+ }
945
+ }
946
+ },
947
+ defaultValues: {
948
+ text: "Heading",
949
+ headingType: "h1",
950
+ fontSize: "22px",
951
+ color: "#000000",
952
+ textAlign: "left",
953
+ fontWeight: "700",
954
+ lineHeight: "140%",
955
+ letterSpacing: "normal",
956
+ containerPadding: "10px"
957
+ },
958
+ renderer: {
959
+ renderEditor(t) {
960
+ const e = a(t, "containerPadding", "10px"), o = a(t, "fontSize", "22px"), n = a(t, "color", "#000000"), i = a(t, "textAlign", "left"), r = a(t, "fontWeight", "700"), s = a(t, "lineHeight", "140%"), l = et(t, "Heading");
961
+ return c`
962
+ <div style="padding:${e};font-size:${o};color:${n};text-align:${i};font-weight:${r};line-height:${s};">
963
+ ${l}
964
+ </div>
965
+ `;
966
+ },
967
+ renderHtml(t) {
968
+ const e = a(t, "containerPadding", "10px"), o = a(t, "fontSize", "22px"), n = a(t, "color", "#000000"), i = a(t, "textAlign", "left"), r = a(t, "fontWeight", "700"), s = a(t, "lineHeight", "140%"), l = a(t, "letterSpacing", "normal"), d = a(t, "headingType", "h1"), p = et(t, "Heading"), h = `<${d} style="margin:0;font-size:${o};color:${n};text-align:${i};font-weight:${r};line-height:${s};letter-spacing:${l};">${p}</${d}>`;
969
+ return V(h, { padding: e });
970
+ }
971
+ }
972
+ }, kt = {
973
+ name: "paragraph",
974
+ label: "Paragraph",
975
+ 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>',
976
+ supportedDisplayModes: ["email", "web"],
977
+ position: 3,
978
+ options: {
979
+ text: {
980
+ title: "Paragraph",
981
+ options: {
982
+ text: {
983
+ label: "Text",
984
+ 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>',
985
+ widget: "rich_text"
986
+ }
987
+ }
988
+ },
989
+ style: {
990
+ title: "Style",
991
+ options: {
992
+ color: { label: "Text Color", defaultValue: "#374151", widget: "color_picker" },
993
+ textAlign: { label: "Text Align", defaultValue: "left", widget: "alignment" },
994
+ lineHeight: { label: "Line Height", defaultValue: "160%", widget: "text" },
995
+ letterSpacing: { label: "Letter Spacing", defaultValue: "normal", widget: "text" }
996
+ }
997
+ },
998
+ spacing: {
999
+ title: "Spacing",
1000
+ options: {
1001
+ containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" }
1002
+ }
1003
+ }
1004
+ },
1005
+ defaultValues: {
1006
+ text: '<p style="font-size:14px;line-height:1.6;">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>',
1007
+ color: "#374151",
1008
+ lineHeight: "160%",
1009
+ letterSpacing: "normal",
1010
+ textAlign: "left",
1011
+ containerPadding: "10px"
1012
+ },
1013
+ renderer: {
1014
+ renderEditor(t) {
1015
+ const e = a(t, "containerPadding", "10px"), o = a(t, "color", "#374151"), n = a(t, "lineHeight", "160%");
1016
+ return c`<div style="padding:${e};color:${o};line-height:${n};word-break:break-word;">${q(a(t, "text"))}</div>`;
1017
+ },
1018
+ renderHtml(t) {
1019
+ const e = a(t, "containerPadding", "10px"), o = a(t, "color", "#374151"), n = a(t, "lineHeight", "160%"), i = a(t, "textAlign", "left"), r = a(t, "letterSpacing", "normal"), s = `<div style="font-size:14px;color:${o};line-height:${n};text-align:${i};letter-spacing:${r};word-wrap:break-word;">${a(t, "text")}</div>`;
1020
+ return V(s, { padding: e });
1021
+ }
1022
+ }
1023
+ }, Ct = {
1024
+ name: "image",
1025
+ label: "Image",
1026
+ 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>',
1027
+ supportedDisplayModes: ["email", "web"],
1028
+ position: 3,
1029
+ options: {
1030
+ image: {
1031
+ title: "Image",
1032
+ options: {
1033
+ src: { label: "Image URL", defaultValue: "", widget: "text" },
1034
+ alt: { label: "Alt Text", defaultValue: "", widget: "text" },
1035
+ href: { label: "Link URL", defaultValue: "", widget: "text" },
1036
+ target: { label: "Link Target", defaultValue: "_blank", widget: "text" }
1037
+ }
1038
+ },
1039
+ style: {
1040
+ title: "Style",
1041
+ options: {
1042
+ width: {
1043
+ label: "Width",
1044
+ defaultValue: "100%",
1045
+ widget: "dropdown",
1046
+ widgetParams: { options: [
1047
+ { label: "Auto", value: "auto" },
1048
+ { label: "25%", value: "25%" },
1049
+ { label: "50%", value: "50%" },
1050
+ { label: "75%", value: "75%" },
1051
+ { label: "100%", value: "100%" }
1052
+ ] }
1053
+ },
1054
+ align: { label: "Align", defaultValue: "center", widget: "alignment" },
1055
+ borderRadius: { label: "Border Radius", defaultValue: "0px", widget: "text" }
1056
+ }
1057
+ },
1058
+ spacing: {
1059
+ title: "Spacing",
1060
+ options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
1061
+ },
1062
+ general: {
1063
+ title: "General",
1064
+ options: {
1065
+ anchor: { label: "Anchor", defaultValue: "", widget: "text" },
1066
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1067
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1068
+ }
1069
+ }
1070
+ },
1071
+ defaultValues: {
1072
+ src: "https://placehold.co/600x200/e2e8f0/64748b?text=Drop+Image+Here",
1073
+ alt: "Image",
1074
+ href: "",
1075
+ target: "_blank",
1076
+ width: "100%",
1077
+ maxWidth: "100%",
1078
+ align: "center",
1079
+ borderRadius: "0px",
1080
+ containerPadding: "10px"
1081
+ },
1082
+ renderer: {
1083
+ renderEditor(t) {
1084
+ const e = a(t, "containerPadding", "10px"), o = Z(t.src), n = a(t, "alt"), r = tt(t.src).maxWidth || a(t, "width", "100%"), s = a(t, "borderRadius", "0px"), l = a(t, "textAlign", a(t, "align", "center"));
1085
+ return o ? c`<div style="padding:${e};text-align:${l};"><img src=${o} alt=${n} style="display:inline-block;max-width:100%;width:${r};border-radius:${s};border:0;" /></div>` : c`<div style="padding:${e};text-align:${l};"><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>`;
1086
+ },
1087
+ renderHtml(t, e) {
1088
+ const o = a(t, "containerPadding", "10px"), n = Z(t.src), i = a(t, "alt"), r = yt(t), l = tt(t.src).maxWidth || a(t, "width", "100%"), d = a(t, "borderRadius", "0px"), p = a(t, "textAlign", a(t, "align", "center")), h = l === "100%" ? e.columnWidth : parseInt(l) || e.columnWidth, g = d !== "0px" ? `border-radius:${d};` : "", u = `<img align="${p}" border="0" src="${n}" alt="${i}" title="${i}" style="outline:none;text-decoration:none;clear:both;display:inline-block!important;border:none;height:auto;float:none;width:${l};max-width:${h}px;${g}" width="${h}" />`, m = r.href ? `<a href="${r.href}" target="${r.target}" style="text-decoration:none;">${u}</a>` : u;
1089
+ return V(m, { padding: o, align: p });
1090
+ }
1091
+ }
1092
+ }, _t = {
1093
+ name: "button",
1094
+ label: "Button",
1095
+ 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>',
1096
+ supportedDisplayModes: ["email", "web"],
1097
+ position: 4,
1098
+ options: {
1099
+ button: {
1100
+ title: "Button",
1101
+ options: {
1102
+ text: { label: "Button Text", defaultValue: "Click Me", widget: "text" },
1103
+ href: { label: "Link URL", defaultValue: "#", widget: "text" },
1104
+ target: { label: "Target", defaultValue: "_blank", widget: "text" }
1105
+ }
1106
+ },
1107
+ style: {
1108
+ title: "Style",
1109
+ options: {
1110
+ backgroundColor: { label: "Button Color", defaultValue: "#3b82f6", widget: "color_picker" },
1111
+ textColor: { label: "Text Color", defaultValue: "#ffffff", widget: "color_picker" },
1112
+ fontSize: {
1113
+ label: "Font Size",
1114
+ defaultValue: "14px",
1115
+ widget: "dropdown",
1116
+ widgetParams: { options: [
1117
+ { label: "12px", value: "12px" },
1118
+ { label: "13px", value: "13px" },
1119
+ { label: "14px", value: "14px" },
1120
+ { label: "16px", value: "16px" },
1121
+ { label: "18px", value: "18px" },
1122
+ { label: "20px", value: "20px" }
1123
+ ] }
1124
+ },
1125
+ fontWeight: {
1126
+ label: "Font Weight",
1127
+ defaultValue: "700",
1128
+ widget: "dropdown",
1129
+ widgetParams: { options: [{ label: "Normal", value: "400" }, { label: "Bold", value: "700" }] }
1130
+ },
1131
+ borderRadius: { label: "Border Radius", defaultValue: "4px", widget: "text" },
1132
+ buttonWidth: {
1133
+ label: "Width",
1134
+ defaultValue: "auto",
1135
+ widget: "dropdown",
1136
+ widgetParams: { options: [
1137
+ { label: "Auto", value: "auto" },
1138
+ { label: "100%", value: "100%" },
1139
+ { label: "50%", value: "50%" }
1140
+ ] }
1141
+ },
1142
+ textAlign: { label: "Align", defaultValue: "center", widget: "alignment" },
1143
+ buttonPadding: { label: "Button Padding", defaultValue: "10px 20px", widget: "padding" },
1144
+ borderColor: { label: "Border Color", defaultValue: "", widget: "color_picker" },
1145
+ borderWidth: { label: "Border Width", defaultValue: "0px", widget: "text" }
1146
+ }
1147
+ },
1148
+ spacing: {
1149
+ title: "Spacing",
1150
+ options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
1151
+ },
1152
+ general: {
1153
+ title: "General",
1154
+ options: {
1155
+ anchor: { label: "Anchor", defaultValue: "", widget: "text" },
1156
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1157
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1158
+ }
1159
+ }
1160
+ },
1161
+ defaultValues: {
1162
+ text: "Click Me",
1163
+ href: "#",
1164
+ target: "_blank",
1165
+ backgroundColor: "#3b82f6",
1166
+ textColor: "#ffffff",
1167
+ fontSize: "14px",
1168
+ fontWeight: "700",
1169
+ borderRadius: "4px",
1170
+ buttonWidth: "auto",
1171
+ textAlign: "center",
1172
+ buttonPadding: "10px 20px",
1173
+ borderColor: "",
1174
+ borderWidth: "0px",
1175
+ containerPadding: "10px"
1176
+ },
1177
+ renderer: {
1178
+ renderEditor(t) {
1179
+ const e = a(t, "containerPadding", "10px"), o = a(t, "backgroundColor", "#3b82f6"), n = a(t, "textColor", "#ffffff"), i = a(t, "fontSize", "14px"), r = a(t, "fontWeight", "700"), s = a(t, "borderRadius", "4px"), l = a(t, "buttonPadding", "10px 20px"), d = a(t, "text", "Click Me"), p = a(t, "textAlign", "center"), h = a(t, "buttonWidth", "auto"), g = a(t, "borderWidth", "0px"), u = a(t, "borderColor", o), m = g !== "0px" ? `border:${g} solid ${u};` : "border:none;", x = h === "auto" ? "display:inline-block;" : `display:block;width:${h};`;
1180
+ return c`
1181
+ <div style="padding:${e};text-align:${p};">
1182
+ <a style="${x}background-color:${o};color:${n};font-size:${i};font-weight:${r};border-radius:${s};padding:${l};text-decoration:none;text-align:center;${m}cursor:pointer;font-family:arial,helvetica,sans-serif;box-sizing:border-box;">${d}</a>
1183
+ </div>
1184
+ `;
1185
+ },
1186
+ renderHtml(t) {
1187
+ const e = a(t, "containerPadding", "10px"), o = a(t, "backgroundColor", "#3b82f6"), n = a(t, "textColor", "#ffffff"), i = a(t, "fontSize", "14px"), r = a(t, "fontWeight", "700"), s = a(t, "borderRadius", "4px"), l = a(t, "buttonPadding", "10px 20px"), d = a(t, "text", "Click Me"), p = a(t, "textAlign", "center"), h = a(t, "href", "#"), g = a(t, "target", "_blank"), u = a(t, "borderWidth", "0px"), m = a(t, "borderColor", o), x = u !== "0px" ? `border:${u} solid ${m};` : "border:none;", b = wt(d, h, { bgColor: o, textColor: n, fontSize: i, fontWeight: r, borderRadius: s }), y = b ? `${b}
1188
+ <!--[if !mso]><!-->` : "<!--[if !mso]><!-->", z = `<div align="${p}">
1189
+ ${y}
1190
+ <a href="${h}" target="${g}" style="box-sizing:border-box;display:inline-block;text-decoration:none;text-align:center;color:${n};background-color:${o};border-radius:${s};font-size:${i};font-weight:${r};padding:${l};font-family:arial,helvetica,sans-serif;${x}mso-border-alt:none;word-break:keep-all;"><span style="line-height:120%;">${d}</span></a>
1191
+ <!--<![endif]-->
1192
+ </div>`;
1193
+ return V(z, { padding: e, align: p });
1194
+ }
1195
+ }
1196
+ }, Dt = {
1197
+ name: "divider",
1198
+ label: "Divider",
1199
+ 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>',
1200
+ supportedDisplayModes: ["email", "web"],
1201
+ position: 5,
1202
+ options: {
1203
+ style: {
1204
+ title: "Style",
1205
+ options: {
1206
+ borderTopWidth: { label: "Width", defaultValue: "1px", widget: "text" },
1207
+ borderTopStyle: {
1208
+ label: "Style",
1209
+ defaultValue: "solid",
1210
+ widget: "dropdown",
1211
+ widgetParams: { options: [
1212
+ { label: "Solid", value: "solid" },
1213
+ { label: "Dashed", value: "dashed" },
1214
+ { label: "Dotted", value: "dotted" },
1215
+ { label: "Double", value: "double" }
1216
+ ] }
1217
+ },
1218
+ borderTopColor: { label: "Color", defaultValue: "#cccccc", widget: "color_picker" },
1219
+ width: { label: "Line Width", defaultValue: "100%", widget: "text" }
1220
+ }
1221
+ },
1222
+ spacing: {
1223
+ title: "Spacing",
1224
+ options: { containerPadding: { label: "Padding", defaultValue: "10px", widget: "padding" } }
1225
+ },
1226
+ general: {
1227
+ title: "General",
1228
+ options: {
1229
+ hideDesktop: { label: "Hide on Desktop", defaultValue: !1, widget: "toggle" },
1230
+ hideMobile: { label: "Hide on Mobile", defaultValue: !1, widget: "toggle" }
1231
+ }
1232
+ }
1233
+ },
1234
+ defaultValues: {
1235
+ borderTopWidth: "1px",
1236
+ borderTopStyle: "solid",
1237
+ borderTopColor: "#cccccc",
1238
+ width: "100%",
1239
+ containerPadding: "10px"
1240
+ },
1241
+ renderer: {
1242
+ renderEditor(t) {
1243
+ const e = a(t, "containerPadding", "10px"), o = a(t, "width", "100%"), n = `${a(t, "borderTopWidth", "1px")} ${a(t, "borderTopStyle", "solid")} ${a(t, "borderTopColor", "#cccccc")}`;
1244
+ return c`<div style="padding:${e};"><div style="border-top:${n};width:${o};margin:0 auto;"></div></div>`;
1245
+ },
1246
+ renderHtml(t) {
1247
+ const e = a(t, "containerPadding", "10px"), o = a(t, "width", "100%"), n = `${a(t, "borderTopWidth", "1px")} ${a(t, "borderTopStyle", "solid")} ${a(t, "borderTopColor", "#cccccc")}`, i = `<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="${o}" style="border-collapse:collapse;border-top:${n};"><tbody><tr><td style="font-size:0;line-height:0;">&nbsp;</td></tr></tbody></table>`;
1248
+ return V(i, { padding: e, align: "center" });
1249
+ }
1250
+ }
1251
+ }, St = [
1252
+ vt,
1253
+ $t,
1254
+ kt,
1255
+ Ct,
1256
+ _t,
1257
+ Dt
1258
+ ], ot = [
1259
+ {
1260
+ 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 },
1261
+ loader: () => import("./html-tool-Be4HAhoj.js").then((t) => t.htmlTool)
1262
+ },
1263
+ {
1264
+ 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 },
1265
+ loader: () => import("./social-tool-bc46FNgk.js").then((t) => t.socialTool)
1266
+ },
1267
+ {
1268
+ 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 },
1269
+ loader: () => import("./menu-tool-HPxxeiRV.js").then((t) => t.menuTool)
1270
+ },
1271
+ {
1272
+ 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 },
1273
+ loader: () => import("./video-tool-CPjCWbTX.js").then((t) => t.videoTool)
1274
+ },
1275
+ {
1276
+ 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 },
1277
+ loader: () => import("./timer-tool-GPhdsvRE.js").then((t) => t.timerTool)
1278
+ },
1279
+ {
1280
+ 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 },
1281
+ loader: () => import("./table-tool-LzRcAPFb.js").then((t) => t.tableTool)
1282
+ },
1283
+ {
1284
+ 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 },
1285
+ loader: () => import("./form-tool-DSXsWpjQ.js").then((t) => t.formTool)
1286
+ }
1287
+ ];
1288
+ function Rt(t, e, o) {
1289
+ const n = o.backgroundColor || "#e7e7e7", i = o.contentWidth || "600px", r = o.fontFamily?.value || "arial,helvetica,sans-serif", s = o.textColor || "#000000", l = o.preheaderText || "", d = l ? `<div style="display:none;font-size:1px;color:${n};line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">${l}${"&zwnj;&nbsp;".repeat(80)}</div>` : "";
1290
+ return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1291
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
1292
+ <head>
1293
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1294
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1295
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
1296
+ <meta name="x-apple-disable-message-reformatting">
1297
+ <meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
1298
+ <meta name="color-scheme" content="light dark">
1299
+ <meta name="supported-color-schemes" content="light dark">
1300
+ <title></title>
1301
+ <!--[if mso]>
1302
+ <noscript><xml>
1303
+ <o:OfficeDocumentSettings>
1304
+ <o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch>
1305
+ </o:OfficeDocumentSettings>
1306
+ </xml></noscript>
1307
+ <style type="text/css">
1308
+ table, td, th { font-family: ${r} !important; }
1309
+ </style>
1310
+ <![endif]-->
1311
+ <!--[if !mso]><!-->
1312
+ <style type="text/css">
1313
+ ${e}
1314
+ </style>
1315
+ <!--<![endif]-->
1316
+ <style type="text/css">
1317
+ body { margin: 0; padding: 0; }
1318
+ table, tr, td { vertical-align: top; border-collapse: collapse; }
1319
+ p { margin: 0; }
1320
+ .ie-container table, .mso-container table { table-layout: fixed; }
1321
+ * { line-height: inherit; }
1322
+ a[x-apple-data-detectors='true'] { color: inherit !important; text-decoration: none !important; }
1323
+ </style>
1324
+ </head>
1325
+ <body class="clean-body u_body" style="margin:0;padding:0;-webkit-text-size-adjust:100%;background-color:${n};color:${s};">
1326
+ ${d}
1327
+ <table id="u_body" style="border-collapse:collapse;table-layout:fixed;border-spacing:0;mso-table-lspace:0pt;mso-table-rspace:0pt;vertical-align:top;min-width:320px;margin:0 auto;background-color:${n};width:100%;" cellpadding="0" cellspacing="0" border="0">
1328
+ <tbody>
1329
+ <tr style="vertical-align:top;">
1330
+ <td style="word-break:break-word;border-collapse:collapse !important;vertical-align:top;">
1331
+ <!--[if (mso)|(IE)]><table width="${parseInt(i)}" align="center" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
1332
+ ${t}
1333
+ <!--[if (mso)|(IE)]></td></tr></table><![endif]-->
1334
+ </td>
1335
+ </tr>
1336
+ </tbody>
1337
+ </table>
1338
+ </body>
1339
+ </html>`;
1340
+ }
1341
+ function Mt(t, e, o) {
1342
+ const n = parseInt(e.contentWidth || "600"), i = t.values.backgroundColor || "", r = t.values.columnsBackgroundColor || "", s = t.values.padding || "0px", l = t.cells.reduce((b, y) => b + y, 0), d = i ? `background-color:${i};` : "", p = t.values.backgroundImage?.url ? `background-image:url('${t.values.backgroundImage.url}');background-repeat:${t.values.backgroundImage.repeat ? "repeat" : "no-repeat"};background-position:center top;background-size:${t.values.backgroundImage.cover ? "cover" : "auto"};` : "", h = t.columns.map((b, y) => {
1343
+ const R = Math.round(t.cells[y] / l * n);
1344
+ return nt(b, R, r, e, o);
1345
+ }), g = t.columns.length > 1;
1346
+ let u;
1347
+ if (g) {
1348
+ const b = t.columns.map((y, R) => {
1349
+ const z = Math.round(t.cells[R] / l * n), at = nt(y, z, r, e, o);
1350
+ return `<!--[if (mso)|(IE)]><td align="center" width="${z}" style="width:${z}px;padding:0px;border:none;" valign="top"><![endif]-->
1351
+ ${at}
1352
+ <!--[if (mso)|(IE)]></td><![endif]-->`;
1353
+ });
1354
+ u = `<!--[if (mso)|(IE)]><table role="presentation" width="${n}" cellpadding="0" cellspacing="0" border="0"><tr>${b.join(`
1355
+ `)}</tr></table><![endif]-->
1356
+
1357
+ <!--[if !mso]><!-->
1358
+ <div style="max-width:${n}px;margin:0 auto;">
1359
+ ${h.join(`
1360
+ `)}
1361
+ </div>
1362
+ <!--<![endif]-->`;
1363
+ } else
1364
+ u = h.join(`
1365
+ `);
1366
+ const m = t.values.hideDesktop ? " u_hide_desktop" : "", x = t.values.hideMobile ? " u_hide_mobile" : "";
1367
+ return `<div class="u_row${m}${x}" style="padding:${s};${d}${p}">
1368
+ <div style="margin:0 auto;min-width:320px;max-width:${n}px;overflow-wrap:break-word;word-wrap:break-word;word-break:break-word;background-color:transparent;">
1369
+ <div style="border-collapse:collapse;display:table;width:100%;height:100%;background-color:transparent;">
1370
+ ${u}
1371
+ </div>
1372
+ </div>
1373
+ </div>`;
1374
+ }
1375
+ function nt(t, e, o, n, i) {
1376
+ const r = t.values.backgroundColor || o || "", s = t.values.padding || "0px", l = t.values.borderRadius || "0px", d = r ? `background-color:${r};` : "", p = t.contents.map((h) => {
1377
+ const g = i.get(h.type);
1378
+ if (!g) return `<!-- unknown tool: ${h.type} -->`;
1379
+ const u = {
1380
+ columnWidth: e,
1381
+ displayMode: "email",
1382
+ contentWidth: parseInt(n.contentWidth || "600"),
1383
+ bodyValues: n
1384
+ };
1385
+ let m = g(h.values, u);
1386
+ const x = !!h.values.hideDesktop, b = !!h.values.hideMobile;
1387
+ return (x || b) && (m = `<div class="${[x && "u_hide_desktop", b && "u_hide_mobile"].filter(Boolean).join(" ")}">${m}</div>`), m;
1388
+ }).join(`
1389
+ `);
1390
+ return `<div class="u_column" style="max-width:${e}px;min-width:${Math.min(e, 320)}px;display:table-cell;vertical-align:top;">
1391
+ <div style="height:100%;width:100% !important;border-radius:${l};-webkit-border-radius:${l};${d}">
1392
+ <div style="box-sizing:border-box;height:100%;padding:${s};border:none;border-radius:${l};-webkit-border-radius:${l};">
1393
+ ${p || '<!--[if (!mso)&(!IE)]><!--><div style="height:0;min-height:1px;font-size:0;">&nbsp;</div><!--<![endif]-->'}
1394
+ </div>
1395
+ </div>
1396
+ </div>`;
1397
+ }
1398
+ function It(t) {
1399
+ return `
1400
+ @media only screen and (min-width: ${t + 20}px) {
1401
+ .u_row .u_column { display: table-cell; }
1402
+ }
1403
+
1404
+ @media only screen and (max-width: ${t + 20}px) {
1405
+ .u_row .u_column {
1406
+ display: block !important;
1407
+ width: 100% !important;
1408
+ min-width: 320px !important;
1409
+ max-width: 100% !important;
1410
+ }
1411
+ .u_row {
1412
+ width: 100% !important;
1413
+ }
1414
+ }
1415
+
1416
+ @media only screen and (max-width: 620px) {
1417
+ .u_row-container {
1418
+ max-width: 100% !important;
1419
+ padding-left: 0 !important;
1420
+ padding-right: 0 !important;
1421
+ }
1422
+ }
1423
+
1424
+ @media (prefers-color-scheme: dark) {
1425
+ /* Dark mode overrides — add per-client rules as needed */
1426
+ }
1427
+
1428
+ /* Outlook dark mode */
1429
+ [data-ogsb] body,
1430
+ [data-ogsb] table,
1431
+ [data-ogsb] td {
1432
+ /* Preserve original colors */
1433
+ }
1434
+
1435
+ .u_hide_desktop { display: block !important; }
1436
+ .u_hide_mobile { display: block !important; }
1437
+
1438
+ @media only screen and (max-width: ${t + 20}px) {
1439
+ .u_hide_desktop { display: block !important; }
1440
+ .u_hide_mobile { display: none !important; }
1441
+ }
1442
+
1443
+ @media only screen and (min-width: ${t + 21}px) {
1444
+ .u_hide_desktop { display: none !important; }
1445
+ .u_hide_mobile { display: block !important; }
1446
+ }`;
1447
+ }
1448
+ function Tt(t, e, o) {
1449
+ const n = t.body.values, i = parseInt(n.contentWidth || "600"), r = t.body.rows.map((g) => Mt(g, n, e)).join(`
1450
+ `), s = It(i);
1451
+ let l = Rt(r, s, n);
1452
+ if (o?.mergeTags)
1453
+ for (const [g, u] of Object.entries(o.mergeTags))
1454
+ l = l.replaceAll(`{{${g}}}`, u);
1455
+ const d = l.match(/<body[^>]*>([\s\S]*)<\/body>/i), p = l.match(/<style[^>]*>([\s\S]*?)<\/style>/gi), h = [];
1456
+ return n.fontFamily?.url && h.push(n.fontFamily.url), {
1457
+ design: structuredClone(t),
1458
+ html: l,
1459
+ chunks: {
1460
+ body: d?.[1] ?? r,
1461
+ css: p?.map((g) => g.replace(/<\/?style[^>]*>/gi, "")).join(`
1462
+ `) ?? s,
1463
+ fonts: h,
1464
+ js: ""
1465
+ }
1466
+ };
1467
+ }
1468
+ class S {
1469
+ /**
1470
+ * @param host - The Lit component that owns this controller
1471
+ * @param channels - Which store channels to subscribe to
1472
+ */
1473
+ constructor(e, o) {
1474
+ this.store = null, this.host = e, this.channels = o, e.addController(this);
1475
+ }
1476
+ /** Set or change the store reference. Re-subscribes automatically. */
1477
+ setStore(e) {
1478
+ this.store !== e && (this.unsub?.(), this.store = e, this.subscribe());
1479
+ }
1480
+ hostConnected() {
1481
+ this.subscribe();
1482
+ }
1483
+ hostDisconnected() {
1484
+ this.unsub?.(), this.unsub = void 0;
1485
+ }
1486
+ subscribe() {
1487
+ this.store && (this.unsub = this.store.subscribeChannels(this.channels, () => {
1488
+ this.host.requestUpdate();
1489
+ }));
1490
+ }
1491
+ }
1492
+ var Pt = Object.defineProperty, Vt = Object.getOwnPropertyDescriptor, j = (t, e, o, n) => {
1493
+ for (var i = n > 1 ? void 0 : n ? Vt(e, o) : e, r = t.length - 1, s; r >= 0; r--)
1494
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
1495
+ return n && i && Pt(e, o, i), i;
1496
+ };
1497
+ let _ = class extends v {
1498
+ constructor() {
1499
+ super(...arguments), this.storeCtrl = new S(this, ["design", "selection", "hover", "viewMode"]), this._onDragStart = (t) => {
1500
+ t.dataTransfer.setData("application/maileditor-content", this.content.id), t.dataTransfer.effectAllowed = "move", this.style.opacity = "0.4", E.startContentDrag(this.content.id);
1501
+ }, this._onDragEnd = () => {
1502
+ this.style.opacity = "1", E.reset();
1503
+ };
1504
+ }
1505
+ set store(t) {
1506
+ this.storeCtrl.setStore(t);
1507
+ }
1508
+ get store() {
1509
+ return this.storeCtrl.store;
1510
+ }
1511
+ handleClick(t) {
1512
+ t.stopPropagation(), this.store.select(this.content.id);
1513
+ }
1514
+ handleMouseEnter() {
1515
+ this.store.hover(this.content.id);
1516
+ }
1517
+ handleMouseLeave() {
1518
+ this.store.hover(null);
1519
+ }
1520
+ handleDelete(t) {
1521
+ t.stopPropagation(), this.store.removeContent(this.content.id);
1522
+ }
1523
+ handleDuplicate(t) {
1524
+ t.stopPropagation(), this.store.duplicateContent(this.content.id);
1525
+ }
1526
+ connectedCallback() {
1527
+ super.connectedCallback(), this.addEventListener("dragstart", this._onDragStart), this.addEventListener("dragend", this._onDragEnd);
1528
+ }
1529
+ disconnectedCallback() {
1530
+ super.disconnectedCallback(), this.removeEventListener("dragstart", this._onDragStart), this.removeEventListener("dragend", this._onDragEnd);
1531
+ }
1532
+ render() {
1533
+ if (!this.store) return c``;
1534
+ const t = this.store.selectedId === this.content.id, e = this.store.hoveredId === this.content.id, o = this.store.viewMode, n = !!this.content.values.hideDesktop, i = !!this.content.values.hideMobile, r = o === "desktop" && n || o === "mobile" && i;
1535
+ this.classList.toggle("selected", t), this.classList.toggle("hovered", e), this.classList.toggle("hidden-in-view", r), this.setAttribute("draggable", "true"), this.dataset.contentId = this.content.id;
1536
+ const s = this.toolRegistry.get(this.content.type);
1537
+ if (!s && this.toolRegistry.has(this.content.type))
1538
+ return this.toolRegistry.ensureLoaded(this.content.type).then(() => this.requestUpdate()), c`<div style="padding:16px;text-align:center;color:#9ca3af;font-size:13px;font-family:sans-serif;">Loading ${this.content.type}...</div>`;
1539
+ const l = s?.renderer.renderEditor(this.content.values, {
1540
+ isSelected: t,
1541
+ isHovered: e,
1542
+ columnWidth: 600,
1543
+ displayMode: "email"
1544
+ });
1545
+ return c`
1546
+ ${r ? c`<div class="hidden-badge">${n ? "Hidden on desktop" : i ? "Hidden on mobile" : ""}</div>` : ""}
1547
+ <div class="action-bar">
1548
+ <button class="action-btn" @click=${this.handleDuplicate} title="Duplicate">&#9851;</button>
1549
+ <button class="action-btn" @click=${this.handleDelete} title="Delete">&#10005;</button>
1550
+ </div>
1551
+ <div class="content-wrapper" @click=${this.handleClick}
1552
+ @mouseenter=${this.handleMouseEnter} @mouseleave=${this.handleMouseLeave}>
1553
+ ${l ?? c`<div style="padding:10px;color:#999;font-style:italic;">Unknown tool: ${this.content.type}</div>`}
1554
+ </div>
1555
+ `;
1556
+ }
1557
+ };
1558
+ _.styles = $`
1559
+ :host {
1560
+ display: block; position: relative; cursor: pointer;
1561
+ transition: outline 0.15s ease, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
1562
+ }
1563
+ :host(.selected) { outline: 2px solid #3b82f6; outline-offset: -1px; }
1564
+ :host(.hovered:not(.selected)) { outline: 2px dashed #93c5fd; outline-offset: -1px; }
1565
+ :host(.just-dropped) {
1566
+ animation: dropIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
1567
+ }
1568
+ :host(.hidden-in-view) {
1569
+ opacity: 0.3;
1570
+ position: relative;
1571
+ }
1572
+ .hidden-badge {
1573
+ position: absolute; top: 4px; right: 4px; z-index: 5;
1574
+ background: #f59e0b; color: white; font-size: 10px; font-weight: 600;
1575
+ padding: 2px 6px; border-radius: 3px; font-family: sans-serif;
1576
+ pointer-events: none;
1577
+ }
1578
+ @keyframes dropIn {
1579
+ 0% { opacity: 0; transform: scale(0.92) translateY(-8px); }
1580
+ 100% { opacity: 1; transform: scale(1) translateY(0); }
1581
+ }
1582
+ .action-bar {
1583
+ display: none; position: absolute; top: -28px; right: 4px;
1584
+ background: #3b82f6; border-radius: 4px; padding: 2px; gap: 2px; z-index: 10;
1585
+ }
1586
+ :host(.selected) .action-bar { display: flex; }
1587
+ .action-btn {
1588
+ background: none; border: none; color: white; cursor: pointer;
1589
+ padding: 2px 6px; font-size: 12px; line-height: 1; border-radius: 2px;
1590
+ }
1591
+ .action-btn:hover { background: rgba(255,255,255,0.2); }
1592
+ .content-wrapper { position: relative; }
1593
+ `;
1594
+ j([
1595
+ f({ attribute: !1 })
1596
+ ], _.prototype, "content", 2);
1597
+ j([
1598
+ f({ attribute: !1 })
1599
+ ], _.prototype, "store", 1);
1600
+ j([
1601
+ f({ attribute: !1 })
1602
+ ], _.prototype, "toolRegistry", 2);
1603
+ _ = j([
1604
+ k("me-content-renderer")
1605
+ ], _);
1606
+ var zt = Object.defineProperty, Et = Object.getOwnPropertyDescriptor, B = (t, e, o, n) => {
1607
+ for (var i = n > 1 ? void 0 : n ? Et(e, o) : e, r = t.length - 1, s; r >= 0; r--)
1608
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
1609
+ return n && i && zt(e, o, i), i;
1610
+ };
1611
+ let w = class extends v {
1612
+ constructor() {
1613
+ super(...arguments), this.storeCtrl = new S(this, ["design"]), this.widthPercent = 100;
1614
+ }
1615
+ set store(t) {
1616
+ this.storeCtrl.setStore(t);
1617
+ }
1618
+ get store() {
1619
+ return this.storeCtrl.store;
1620
+ }
1621
+ render() {
1622
+ const t = this.column.values.padding || "0px", e = this.column.values.backgroundColor || "transparent", o = this.column.contents;
1623
+ return this.dataset.columnId = this.column.id, this.style.width = `${this.widthPercent}%`, this.style.padding = t, this.style.backgroundColor = e, this.style.verticalAlign = "top", this.style.boxSizing = "border-box", o.length === 0 ? c`
1624
+ <div class="empty-column" data-column-id=${this.column.id}>
1625
+ Drag content here
1626
+ </div>
1627
+ ` : c`
1628
+ ${o.map(
1629
+ (n) => c`
1630
+ <me-content-renderer
1631
+ .content=${n}
1632
+ .store=${this.store}
1633
+ .toolRegistry=${this.toolRegistry}
1634
+ ></me-content-renderer>
1635
+ `
1636
+ )}
1637
+ `;
1638
+ }
1639
+ };
1640
+ w.styles = $`
1641
+ :host {
1642
+ display: block;
1643
+ min-height: 40px;
1644
+ position: relative;
1645
+ }
1646
+ .empty-column {
1647
+ display: flex;
1648
+ align-items: center;
1649
+ justify-content: center;
1650
+ min-height: 60px;
1651
+ border: 2px dashed #d1d5db;
1652
+ border-radius: 4px;
1653
+ color: #9ca3af;
1654
+ font-size: 13px;
1655
+ font-family: sans-serif;
1656
+ margin: 4px;
1657
+ }
1658
+ .drop-indicator {
1659
+ height: 3px;
1660
+ background: #3b82f6;
1661
+ border-radius: 2px;
1662
+ margin: 0 4px;
1663
+ opacity: 0;
1664
+ transition: opacity 0.15s ease;
1665
+ }
1666
+ .drop-indicator.active {
1667
+ opacity: 1;
1668
+ }
1669
+ `;
1670
+ B([
1671
+ f({ attribute: !1 })
1672
+ ], w.prototype, "column", 2);
1673
+ B([
1674
+ f({ attribute: !1 })
1675
+ ], w.prototype, "store", 1);
1676
+ B([
1677
+ f({ attribute: !1 })
1678
+ ], w.prototype, "toolRegistry", 2);
1679
+ B([
1680
+ f({ type: Number })
1681
+ ], w.prototype, "widthPercent", 2);
1682
+ w = B([
1683
+ k("me-column-renderer")
1684
+ ], w);
1685
+ var Lt = Object.defineProperty, At = Object.getOwnPropertyDescriptor, W = (t, e, o, n) => {
1686
+ for (var i = n > 1 ? void 0 : n ? At(e, o) : e, r = t.length - 1, s; r >= 0; r--)
1687
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
1688
+ return n && i && Lt(e, o, i), i;
1689
+ };
1690
+ let D = class extends v {
1691
+ constructor() {
1692
+ super(...arguments), this.storeCtrl = new S(this, ["design", "viewMode"]);
1693
+ }
1694
+ set store(t) {
1695
+ this.storeCtrl.setStore(t);
1696
+ }
1697
+ get store() {
1698
+ return this.storeCtrl.store;
1699
+ }
1700
+ handleMoveUp(t) {
1701
+ t.stopPropagation();
1702
+ const e = this.store.getRowIndex(this.row.id);
1703
+ e > 0 && this.store.moveRow(e, e - 1);
1704
+ }
1705
+ handleMoveDown(t) {
1706
+ t.stopPropagation();
1707
+ const e = this.store.getRowIndex(this.row.id), o = this.store.getRows().length;
1708
+ e < o - 1 && this.store.moveRow(e, e + 1);
1709
+ }
1710
+ handleDuplicate(t) {
1711
+ t.stopPropagation(), this.store.duplicateRow(this.row.id);
1712
+ }
1713
+ handleDelete(t) {
1714
+ t.stopPropagation(), this.store.removeRow(this.row.id);
1715
+ }
1716
+ render() {
1717
+ if (!this.store) return c``;
1718
+ const { row: t, store: e, toolRegistry: o } = this, n = t.values.backgroundColor || "transparent", i = t.values.columnsBackgroundColor || "transparent", r = t.values.padding || "0px", s = t.cells.reduce((b, y) => b + y, 0), l = t.values.backgroundImage, d = typeof l == "object" && l?.url ? l.url : "", p = d ? `background-image:url('${d}');background-size:${l?.cover ? "cover" : "contain"};background-position:${l?.center ? "center" : "top left"};background-repeat:${l?.repeat ? "repeat" : "no-repeat"};` : "", h = e.viewMode, g = !!t.values.hideDesktop, u = !!t.values.hideMobile, m = h === "desktop" && g || h === "mobile" && u, x = g ? "Hidden on desktop" : u ? "Hidden on mobile" : "";
1719
+ return this.classList.toggle("hidden-in-view", m), this.setAttribute("draggable", "true"), this.dataset.rowId = t.id, c`
1720
+ ${m ? c`<div class="row-hidden-badge">${x}</div>` : ""}
1721
+ <div class="row-actions">
1722
+ <button class="row-action-btn" @click=${this.handleMoveUp} title="Move Up">
1723
+ <svg viewBox="0 0 24 24"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg>
1724
+ </button>
1725
+ <button class="row-action-btn" @click=${this.handleMoveDown} title="Move Down">
1726
+ <svg viewBox="0 0 24 24"><path d="M12 5v14"/><path d="m19 12-7 7-7-7"/></svg>
1727
+ </button>
1728
+ <button class="row-action-btn" @click=${this.handleDuplicate} title="Duplicate Row">
1729
+ <svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
1730
+ </button>
1731
+ <button class="row-action-btn danger" @click=${this.handleDelete} title="Delete Row">
1732
+ <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>
1733
+ </button>
1734
+ </div>
1735
+ <div
1736
+ class="row-wrapper"
1737
+ style="background-color:${n};padding:${r};${p}"
1738
+ >
1739
+ ${t.columns.map((b, y) => {
1740
+ const R = t.cells[y] / s * 100;
1741
+ return c`
1742
+ <me-column-renderer
1743
+ .column=${b}
1744
+ .store=${e}
1745
+ .toolRegistry=${o}
1746
+ .widthPercent=${R}
1747
+ style="background-color:${i};"
1748
+ ></me-column-renderer>
1749
+ `;
1750
+ })}
1751
+ </div>
1752
+ `;
1753
+ }
1754
+ };
1755
+ D.styles = $`
1756
+ :host {
1757
+ display: block;
1758
+ position: relative;
1759
+ transition: opacity 0.2s ease;
1760
+ }
1761
+ :host(.hidden-in-view) { opacity: 0.3; }
1762
+ .row-hidden-badge {
1763
+ position: absolute; top: 4px; left: 4px; z-index: 5;
1764
+ background: #f59e0b; color: white; font-size: 10px; font-weight: 600;
1765
+ padding: 2px 6px; border-radius: 3px; font-family: sans-serif;
1766
+ pointer-events: none;
1767
+ }
1768
+ .row-wrapper {
1769
+ display: flex;
1770
+ flex-wrap: nowrap;
1771
+ position: relative;
1772
+ min-height: 30px;
1773
+ transition: box-shadow 0.15s ease;
1774
+ }
1775
+ :host(:hover) .row-wrapper {
1776
+ box-shadow: inset 0 0 0 1px #93c5fd;
1777
+ }
1778
+ .row-actions {
1779
+ display: none;
1780
+ position: absolute;
1781
+ right: -36px;
1782
+ top: 50%;
1783
+ transform: translateY(-50%);
1784
+ flex-direction: column;
1785
+ gap: 2px;
1786
+ z-index: 10;
1787
+ }
1788
+ :host(:hover) .row-actions {
1789
+ display: flex;
1790
+ }
1791
+ .row-action-btn {
1792
+ width: 28px;
1793
+ height: 28px;
1794
+ display: flex;
1795
+ align-items: center;
1796
+ justify-content: center;
1797
+ background: #6b7280;
1798
+ color: white;
1799
+ border: none;
1800
+ border-radius: 4px;
1801
+ cursor: pointer;
1802
+ font-size: 12px;
1803
+ line-height: 1;
1804
+ transition: background 0.15s ease;
1805
+ padding: 0;
1806
+ }
1807
+ .row-action-btn:hover { background: #3b82f6; }
1808
+ .row-action-btn.danger:hover { background: #ef4444; }
1809
+ .row-action-btn svg {
1810
+ width: 14px;
1811
+ height: 14px;
1812
+ stroke: currentColor;
1813
+ fill: none;
1814
+ stroke-width: 2;
1815
+ stroke-linecap: round;
1816
+ stroke-linejoin: round;
1817
+ }
1818
+ `;
1819
+ W([
1820
+ f({ attribute: !1 })
1821
+ ], D.prototype, "row", 2);
1822
+ W([
1823
+ f({ attribute: !1 })
1824
+ ], D.prototype, "store", 1);
1825
+ W([
1826
+ f({ attribute: !1 })
1827
+ ], D.prototype, "toolRegistry", 2);
1828
+ D = W([
1829
+ k("me-row-renderer")
1830
+ ], D);
1831
+ var Bt = Object.defineProperty, Ht = Object.getOwnPropertyDescriptor, G = (t, e, o, n) => {
1832
+ for (var i = n > 1 ? void 0 : n ? Ht(e, o) : e, r = t.length - 1, s; r >= 0; r--)
1833
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
1834
+ return n && i && Bt(e, o, i), i;
1835
+ };
1836
+ let I = class extends v {
1837
+ constructor() {
1838
+ super(...arguments), this.storeCtrl = new S(this, ["design", "viewMode"]);
1839
+ }
1840
+ set store(t) {
1841
+ this.storeCtrl.setStore(t);
1842
+ }
1843
+ get store() {
1844
+ return this.storeCtrl.store;
1845
+ }
1846
+ handleCanvasClick() {
1847
+ this.store.select(null);
1848
+ }
1849
+ setViewMode(t) {
1850
+ this.store.setViewMode(t);
1851
+ }
1852
+ render() {
1853
+ const t = this.store.getRows(), e = this.store.getBodyValues(), o = e.contentWidth || "600px", n = e.backgroundColor || "#e7e7e7", i = this.store.viewMode;
1854
+ return c`
1855
+ <div class="view-toggle">
1856
+ <button
1857
+ class="view-btn ${i === "desktop" ? "active" : ""}"
1858
+ @click=${() => this.setViewMode("desktop")}
1859
+ >Desktop</button>
1860
+ <button
1861
+ class="view-btn ${i === "mobile" ? "active" : ""}"
1862
+ @click=${() => this.setViewMode("mobile")}
1863
+ >Mobile</button>
1864
+ </div>
1865
+
1866
+ <div
1867
+ class="canvas-body"
1868
+ style="max-width:${i === "mobile" ? "375px" : o};background-color:${n};"
1869
+ @click=${this.handleCanvasClick}
1870
+ >
1871
+ ${t.length === 0 ? c`
1872
+ <div class="empty-canvas">
1873
+ <div class="empty-icon">&#9776;</div>
1874
+ <div>Drag a content block here</div>
1875
+ </div>
1876
+ ` : t.map(
1877
+ (s) => c`
1878
+ <me-row-renderer
1879
+ .row=${s}
1880
+ .store=${this.store}
1881
+ .toolRegistry=${this.toolRegistry}
1882
+ ></me-row-renderer>
1883
+ `
1884
+ )}
1885
+ </div>
1886
+ `;
1887
+ }
1888
+ };
1889
+ I.styles = $`
1890
+ :host {
1891
+ display: block;
1892
+ flex: 1;
1893
+ overflow-y: auto;
1894
+ background: #f3f4f6;
1895
+ padding: 20px;
1896
+ }
1897
+ .canvas-body {
1898
+ margin: 0 auto;
1899
+ background: #ffffff;
1900
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
1901
+ min-height: 200px;
1902
+ position: relative;
1903
+ }
1904
+ .empty-canvas {
1905
+ display: flex;
1906
+ flex-direction: column;
1907
+ align-items: center;
1908
+ justify-content: center;
1909
+ min-height: 300px;
1910
+ color: #9ca3af;
1911
+ font-family: sans-serif;
1912
+ font-size: 14px;
1913
+ gap: 8px;
1914
+ }
1915
+ .empty-icon {
1916
+ font-size: 40px;
1917
+ opacity: 0.4;
1918
+ }
1919
+ .view-toggle {
1920
+ display: flex;
1921
+ justify-content: center;
1922
+ margin-bottom: 12px;
1923
+ gap: 4px;
1924
+ }
1925
+ .view-btn {
1926
+ padding: 6px 16px;
1927
+ border: 1px solid #d1d5db;
1928
+ background: white;
1929
+ cursor: pointer;
1930
+ font-size: 13px;
1931
+ font-family: sans-serif;
1932
+ color: #374151;
1933
+ transition: all 0.15s ease;
1934
+ }
1935
+ .view-btn:first-child { border-radius: 6px 0 0 6px; }
1936
+ .view-btn:last-child { border-radius: 0 6px 6px 0; }
1937
+ .view-btn.active {
1938
+ background: #3b82f6;
1939
+ border-color: #3b82f6;
1940
+ color: white;
1941
+ }
1942
+ `;
1943
+ G([
1944
+ f({ attribute: !1 })
1945
+ ], I.prototype, "store", 1);
1946
+ G([
1947
+ f({ attribute: !1 })
1948
+ ], I.prototype, "toolRegistry", 2);
1949
+ I = G([
1950
+ k("me-editor-canvas")
1951
+ ], I);
1952
+ var Ot = Object.defineProperty, jt = Object.getOwnPropertyDescriptor, rt = (t, e, o, n) => {
1953
+ for (var i = n > 1 ? void 0 : n ? jt(e, o) : e, r = t.length - 1, s; r >= 0; r--)
1954
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
1955
+ return n && i && Ot(e, o, i), i;
1956
+ };
1957
+ const it = [
1958
+ { label: "Arial", value: "arial,helvetica,sans-serif" },
1959
+ { label: "Helvetica", value: "helvetica,sans-serif" },
1960
+ { label: "Georgia", value: "georgia,serif" },
1961
+ { label: "Times New Roman", value: "'times new roman',times,serif" },
1962
+ { label: "Trebuchet MS", value: "trebuchet ms,helvetica,sans-serif" },
1963
+ { label: "Verdana", value: "verdana,geneva,sans-serif" },
1964
+ { label: "Courier New", value: "'courier new',courier,monospace" }
1965
+ ];
1966
+ let L = class extends v {
1967
+ constructor() {
1968
+ super(...arguments), this.storeCtrl = new S(this, ["design"]);
1969
+ }
1970
+ set store(t) {
1971
+ this.storeCtrl.setStore(t);
1972
+ }
1973
+ get store() {
1974
+ return this.storeCtrl.store;
1975
+ }
1976
+ update_(t, e) {
1977
+ this.store.updateBodyValues({ [t]: e });
1978
+ }
1979
+ updateLinkStyle(t, e) {
1980
+ const o = this.store.getBodyValues().linkStyle;
1981
+ this.store.updateBodyValues({ linkStyle: { ...o, [t]: e } });
1982
+ }
1983
+ updateFontFamily(t) {
1984
+ const e = it.find((o) => o.value === t);
1985
+ this.store.updateBodyValues({ fontFamily: { label: e?.label || t, value: t } });
1986
+ }
1987
+ render() {
1988
+ const t = this.store.getBodyValues();
1989
+ return c`
1990
+ ${this.renderColorField("Background Color", t.backgroundColor || "#e7e7e7", (e) => this.update_("backgroundColor", e))}
1991
+
1992
+ <p class="section-title" style="margin-top:16px;">Content</p>
1993
+ <div class="field">
1994
+ <label class="field-label">Content Width (px)</label>
1995
+ <input class="input" type="number" .value=${parseInt(t.contentWidth || "600")} min="320" max="960" step="10"
1996
+ @change=${(e) => this.update_("contentWidth", e.target.value + "px")} />
1997
+ </div>
1998
+ <div class="field">
1999
+ <label class="field-label">Content Align</label>
2000
+ <div class="align-group">
2001
+ ${["left", "center", "right"].map((e) => c`
2002
+ <button class="align-btn ${t.contentAlign === e ? "active" : ""}"
2003
+ @click=${() => this.update_("contentAlign", e)}>${e}</button>
2004
+ `)}
2005
+ </div>
2006
+ </div>
2007
+
2008
+ <p class="section-title" style="margin-top:16px;">Typography</p>
2009
+ <div class="field">
2010
+ <label class="field-label">Font Family</label>
2011
+ <select class="input" @change=${(e) => this.updateFontFamily(e.target.value)}>
2012
+ ${it.map((e) => c`<option value=${e.value} ?selected=${t.fontFamily?.value === e.value}>${e.label}</option>`)}
2013
+ </select>
2014
+ </div>
2015
+ ${this.renderColorField("Text Color", t.textColor || "#000000", (e) => this.update_("textColor", e))}
2016
+
2017
+ <p class="section-title" style="margin-top:16px;">Links</p>
2018
+ ${this.renderColorField("Link Color", t.linkStyle?.linkColor || "#0000ee", (e) => this.updateLinkStyle("linkColor", e))}
2019
+ <div class="field">
2020
+ <label style="display:flex;align-items:center;gap:8px;font-size:12px;color:#6b7280;cursor:pointer;">
2021
+ <input type="checkbox" .checked=${t.linkStyle?.linkUnderline ?? !0}
2022
+ @change=${(e) => this.updateLinkStyle("linkUnderline", e.target.checked)} />
2023
+ Underline Links
2024
+ </label>
2025
+ </div>
2026
+
2027
+ <p class="section-title" style="margin-top:16px;">Email</p>
2028
+ <div class="field">
2029
+ <label class="field-label">Preheader Text</label>
2030
+ <textarea class="input" .value=${t.preheaderText || ""} placeholder="Preview text shown in inbox..."
2031
+ style="min-height:60px;resize:vertical;font-family:inherit;"
2032
+ @change=${(e) => this.update_("preheaderText", e.target.value)}></textarea>
2033
+ </div>
2034
+ `;
2035
+ }
2036
+ /** Reusable color field (swatch + hex input) */
2037
+ renderColorField(t, e, o) {
2038
+ return c`
2039
+ <div class="field">
2040
+ <label class="field-label">${t}</label>
2041
+ <div class="color-row">
2042
+ <input class="color-swatch" type="color" .value=${e} @input=${(n) => o(n.target.value)} />
2043
+ <input class="input" type="text" .value=${e} style="flex:1;" @change=${(n) => o(n.target.value)} />
2044
+ </div>
2045
+ </div>
2046
+ `;
2047
+ }
2048
+ };
2049
+ L.styles = $`
2050
+ :host { display: block; }
2051
+ .section-title {
2052
+ font-size: 11px; font-weight: 600; text-transform: uppercase;
2053
+ color: #9ca3af; letter-spacing: 0.05em; margin: 0 0 8px 0;
2054
+ }
2055
+ .field { margin-bottom: 12px; }
2056
+ .field-label { display: block; font-size: 12px; color: #6b7280; margin-bottom: 4px; }
2057
+ .input {
2058
+ width: 100%; padding: 5px 8px; border: 1px solid #d1d5db; border-radius: 4px;
2059
+ font-size: 12px; box-sizing: border-box;
2060
+ }
2061
+ .input:focus { outline: none; border-color: #3b82f6; }
2062
+ .color-row { display: flex; gap: 6px; align-items: center; }
2063
+ .color-swatch {
2064
+ width: 32px; height: 32px; border: 1px solid #d1d5db; border-radius: 4px;
2065
+ padding: 0; cursor: pointer;
2066
+ }
2067
+ .align-group { display: flex; gap: 2px; }
2068
+ .align-btn {
2069
+ flex: 1; padding: 5px; border: 1px solid #d1d5db; background: white;
2070
+ border-radius: 4px; cursor: pointer; font-size: 11px; color: #6b7280;
2071
+ }
2072
+ .align-btn.active { border-color: #3b82f6; background: #eff6ff; color: #3b82f6; }
2073
+ `;
2074
+ rt([
2075
+ f({ attribute: !1 })
2076
+ ], L.prototype, "store", 1);
2077
+ L = rt([
2078
+ k("me-body-settings")
2079
+ ], L);
2080
+ var Wt = Object.defineProperty, Ut = Object.getOwnPropertyDescriptor, Y = (t, e, o, n) => {
2081
+ for (var i = n > 1 ? void 0 : n ? Ut(e, o) : e, r = t.length - 1, s; r >= 0; r--)
2082
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
2083
+ return n && i && Wt(e, o, i), i;
2084
+ };
2085
+ let T = class extends v {
2086
+ constructor() {
2087
+ super(...arguments), this.storeCtrl = new S(this, ["activeTab"]);
2088
+ }
2089
+ set store(t) {
2090
+ this.storeCtrl.setStore(t);
2091
+ }
2092
+ get store() {
2093
+ return this.storeCtrl.store;
2094
+ }
2095
+ handleDragStart(t, e) {
2096
+ t.dataTransfer.setData("application/maileditor-tool", e), t.dataTransfer.effectAllowed = "copy";
2097
+ }
2098
+ handleLayoutDragStart(t, e) {
2099
+ t.dataTransfer.setData("application/maileditor-layout", JSON.stringify(e)), t.dataTransfer.effectAllowed = "copy";
2100
+ }
2101
+ addRowWithLayout(t) {
2102
+ const e = this.store.createRow(t);
2103
+ this.store.addRow(e);
2104
+ }
2105
+ render() {
2106
+ const t = this.store.activeTab, e = this.toolRegistry.getAllMeta();
2107
+ return c`
2108
+ <div class="tabs">
2109
+ <button class="tab ${t === "content" ? "active" : ""}" @click=${() => this.store.setActiveTab("content")}>Content</button>
2110
+ <button class="tab ${t === "blocks" ? "active" : ""}" @click=${() => this.store.setActiveTab("blocks")}>Blocks</button>
2111
+ <button class="tab ${t === "body" ? "active" : ""}" @click=${() => this.store.setActiveTab("body")}>Body</button>
2112
+ </div>
2113
+
2114
+ <div class="tab-content">
2115
+ ${t === "content" ? this.renderContentTab(e) : ""}
2116
+ ${t === "blocks" ? this.renderBlocksTab() : ""}
2117
+ ${t === "body" ? this.renderBodyTab() : ""}
2118
+ </div>
2119
+ `;
2120
+ }
2121
+ renderContentTab(t) {
2122
+ return c`
2123
+ <p class="section-title">Content</p>
2124
+ <div class="tool-grid">
2125
+ ${t.map(
2126
+ (e) => c`
2127
+ <div
2128
+ class="tool-item"
2129
+ draggable="true"
2130
+ @dragstart=${(o) => this.handleDragStart(o, e.name)}
2131
+ >
2132
+ <div class="tool-icon">${q(e.icon)}</div>
2133
+ <span>${e.label}</span>
2134
+ </div>
2135
+ `
2136
+ )}
2137
+ </div>
2138
+
2139
+ <p class="section-title" style="margin-top:20px;">Layout</p>
2140
+ <div class="layout-grid">
2141
+ ${this.renderLayoutOption([1], "100%")}
2142
+ ${this.renderLayoutOption([1, 1], "50/50")}
2143
+ ${this.renderLayoutOption([1, 1, 1], "33/33/33")}
2144
+ ${this.renderLayoutOption([2, 1], "66/33")}
2145
+ ${this.renderLayoutOption([1, 2], "33/66")}
2146
+ ${this.renderLayoutOption([1, 1, 1, 1], "25x4")}
2147
+ </div>
2148
+ `;
2149
+ }
2150
+ renderLayoutOption(t, e) {
2151
+ const o = t.reduce((n, i) => n + i, 0);
2152
+ return c`
2153
+ <div
2154
+ class="layout-item"
2155
+ draggable="true"
2156
+ @click=${() => this.addRowWithLayout(t)}
2157
+ @dragstart=${(n) => this.handleLayoutDragStart(n, t)}
2158
+ title=${e}
2159
+ >
2160
+ ${t.map(
2161
+ (n) => c`<div class="layout-col" style="width:${n / o * 100}%;"></div>`
2162
+ )}
2163
+ </div>
2164
+ `;
2165
+ }
2166
+ renderBlocksTab() {
2167
+ return c`
2168
+ <p style="color:#9ca3af;font-size:13px;text-align:center;margin-top:40px;">
2169
+ No saved blocks yet.<br/>Select a row in the editor and save it as a block.
2170
+ </p>
2171
+ `;
2172
+ }
2173
+ renderBodyTab() {
2174
+ return c`<me-body-settings .store=${this.store}></me-body-settings>`;
2175
+ }
2176
+ };
2177
+ T.styles = $`
2178
+ :host {
2179
+ display: flex;
2180
+ flex-direction: column;
2181
+ width: 280px;
2182
+ min-width: 280px;
2183
+ background: #ffffff;
2184
+ border-right: 1px solid #e5e7eb;
2185
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2186
+ overflow-y: auto;
2187
+ }
2188
+ .tabs {
2189
+ display: flex;
2190
+ border-bottom: 1px solid #e5e7eb;
2191
+ background: #f9fafb;
2192
+ }
2193
+ .tab {
2194
+ flex: 1;
2195
+ padding: 10px 8px;
2196
+ border: none;
2197
+ background: none;
2198
+ cursor: pointer;
2199
+ font-size: 12px;
2200
+ font-weight: 500;
2201
+ color: #6b7280;
2202
+ text-align: center;
2203
+ transition: all 0.15s ease;
2204
+ border-bottom: 2px solid transparent;
2205
+ }
2206
+ .tab:hover {
2207
+ color: #374151;
2208
+ background: #f3f4f6;
2209
+ }
2210
+ .tab.active {
2211
+ color: #3b82f6;
2212
+ border-bottom-color: #3b82f6;
2213
+ }
2214
+ .tab-content {
2215
+ padding: 12px;
2216
+ flex: 1;
2217
+ }
2218
+ .section-title {
2219
+ font-size: 11px;
2220
+ font-weight: 600;
2221
+ text-transform: uppercase;
2222
+ color: #9ca3af;
2223
+ letter-spacing: 0.05em;
2224
+ margin: 0 0 8px 0;
2225
+ }
2226
+ .tool-grid {
2227
+ display: grid;
2228
+ grid-template-columns: 1fr 1fr;
2229
+ gap: 8px;
2230
+ }
2231
+ .tool-item {
2232
+ display: flex;
2233
+ flex-direction: column;
2234
+ align-items: center;
2235
+ gap: 6px;
2236
+ padding: 12px 8px;
2237
+ border: 1px solid #e5e7eb;
2238
+ border-radius: 6px;
2239
+ cursor: grab;
2240
+ background: white;
2241
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2242
+ font-size: 12px;
2243
+ color: #374151;
2244
+ }
2245
+ .tool-item:hover {
2246
+ border-color: #3b82f6;
2247
+ background: #eff6ff;
2248
+ transform: translateY(-2px);
2249
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
2250
+ }
2251
+ .tool-item:hover .tool-icon {
2252
+ transform: scale(1.15);
2253
+ }
2254
+ .tool-item:active {
2255
+ transform: translateY(0px);
2256
+ box-shadow: 0 1px 4px rgba(59, 130, 246, 0.1);
2257
+ }
2258
+ .tool-item:active {
2259
+ cursor: grabbing;
2260
+ }
2261
+ .tool-icon {
2262
+ width: 20px;
2263
+ height: 20px;
2264
+ display: flex;
2265
+ align-items: center;
2266
+ transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2267
+ justify-content: center;
2268
+ color: #6b7280;
2269
+ }
2270
+ .tool-icon svg {
2271
+ width: 20px;
2272
+ height: 20px;
2273
+ }
2274
+ .layout-grid {
2275
+ display: grid;
2276
+ grid-template-columns: 1fr 1fr 1fr;
2277
+ gap: 8px;
2278
+ margin-top: 16px;
2279
+ }
2280
+ .layout-item {
2281
+ display: flex;
2282
+ align-items: center;
2283
+ justify-content: center;
2284
+ gap: 2px;
2285
+ padding: 10px 4px;
2286
+ border: 1px solid #e5e7eb;
2287
+ border-radius: 6px;
2288
+ cursor: pointer;
2289
+ background: white;
2290
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2291
+ }
2292
+ .layout-item:hover {
2293
+ border-color: #3b82f6;
2294
+ background: #eff6ff;
2295
+ transform: translateY(-2px);
2296
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
2297
+ }
2298
+ .layout-item:hover .layout-col {
2299
+ background: #93c5fd;
2300
+ }
2301
+ .layout-item:active {
2302
+ transform: translateY(0px);
2303
+ }
2304
+ .layout-col {
2305
+ height: 20px;
2306
+ background: #d1d5db;
2307
+ border-radius: 2px;
2308
+ transition: background 0.2s ease;
2309
+ }
2310
+ `;
2311
+ Y([
2312
+ f({ attribute: !1 })
2313
+ ], T.prototype, "store", 1);
2314
+ Y([
2315
+ f({ attribute: !1 })
2316
+ ], T.prototype, "toolRegistry", 2);
2317
+ T = Y([
2318
+ k("me-editor-sidebar")
2319
+ ], T);
2320
+ function Ft(t, e, o) {
2321
+ const n = t && /^#[0-9a-fA-F]{3,8}$/.test(t) ? t : "#000000";
2322
+ return c`
2323
+ <div class="prop-row">
2324
+ <label class="prop-label">${o}</label>
2325
+ <div class="prop-color">
2326
+ <input class="color-swatch" type="color" .value=${n}
2327
+ @input=${(i) => e(i.target.value)} />
2328
+ <input class="prop-input" type="text" .value=${t ?? ""} style="flex:1;"
2329
+ @change=${(i) => e(i.target.value)} />
2330
+ </div>
2331
+ </div>
2332
+ `;
2333
+ }
2334
+ function Nt(t, e, o, n) {
2335
+ const i = n?.options || [];
2336
+ return c`
2337
+ <div class="prop-row">
2338
+ <label class="prop-label">${o}</label>
2339
+ <select class="prop-input" @change=${(r) => e(r.target.value)}>
2340
+ ${i.map((r) => c`<option value=${r.value} ?selected=${t === r.value}>${r.label}</option>`)}
2341
+ </select>
2342
+ </div>
2343
+ `;
2344
+ }
2345
+ function qt(t, e, o) {
2346
+ return c`
2347
+ <div class="prop-row">
2348
+ <label class="prop-label">${o}</label>
2349
+ <div style="display:flex;gap:2px;">
2350
+ ${["left", "center", "right"].map((n) => c`
2351
+ <button
2352
+ style="flex:1;padding:6px;border:1px solid ${t === n ? "#3b82f6" : "#d1d5db"};background:${t === n ? "#eff6ff" : "white"};border-radius:4px;cursor:pointer;font-size:11px;text-transform:capitalize;color:${t === n ? "#3b82f6" : "#6b7280"};"
2353
+ @click=${() => e(n)}
2354
+ >${n}</button>
2355
+ `)}
2356
+ </div>
2357
+ </div>
2358
+ `;
2359
+ }
2360
+ function Gt(t) {
2361
+ const e = (t || "0px").split(/\s+/).map((s) => parseInt(s) || 0), o = e[0], n = e[1] ?? o, i = e[2] ?? o, r = e[3] ?? n;
2362
+ return [o, n, i, r];
2363
+ }
2364
+ function Yt(t, e, o, n) {
2365
+ return t === e && e === o && o === n ? `${t}px` : t === o && e === n ? `${t}px ${e}px` : `${t}px ${e}px ${o}px ${n}px`;
2366
+ }
2367
+ function Kt(t, e, o) {
2368
+ const [n, i, r, s] = Gt(t), l = (p, h, g, u) => e(Yt(p, h, g, u));
2369
+ return c`
2370
+ <div class="prop-row">
2371
+ <label class="prop-label">${o}</label>
2372
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;">
2373
+ ${[
2374
+ { label: "T", val: n, change: (p) => l(p, i, r, s) },
2375
+ { label: "R", val: i, change: (p) => l(n, p, r, s) },
2376
+ { label: "B", val: r, change: (p) => l(n, i, p, s) },
2377
+ { label: "L", val: s, change: (p) => l(n, i, r, p) }
2378
+ ].map((p) => c`
2379
+ <div style="display:flex;align-items:center;gap:4px;">
2380
+ <span style="font-size:10px;color:#9ca3af;width:12px;">${p.label}</span>
2381
+ <input type="number" .value=${p.val} min="0"
2382
+ @change=${(h) => p.change(parseInt(h.target.value) || 0)}
2383
+ style="flex:1;padding:4px 6px;border:1px solid #d1d5db;border-radius:3px;font-size:12px;width:50px;" />
2384
+ </div>
2385
+ `)}
2386
+ </div>
2387
+ </div>
2388
+ `;
2389
+ }
2390
+ function Jt(t, e, o) {
2391
+ return c`
2392
+ <div class="prop-row">
2393
+ <div class="prop-toggle">
2394
+ <input type="checkbox" .checked=${!!t}
2395
+ @change=${(n) => e(n.target.checked)} />
2396
+ <label class="prop-label" style="margin:0;">${o}</label>
2397
+ </div>
2398
+ </div>
2399
+ `;
2400
+ }
2401
+ function Xt(t, e, o) {
2402
+ return c`
2403
+ <div class="prop-row">
2404
+ <label class="prop-label">${o}</label>
2405
+ <input class="prop-input" type="text" .value=${t ?? ""}
2406
+ @change=${(n) => e(n.target.value)} />
2407
+ </div>
2408
+ `;
2409
+ }
2410
+ function Qt(t, e, o) {
2411
+ return c`
2412
+ <div class="prop-row">
2413
+ <label class="prop-label">${o}</label>
2414
+ <textarea class="prop-input"
2415
+ style="min-height:100px;font-family:'SF Mono',Menlo,monospace;font-size:12px;"
2416
+ .value=${t ?? ""}
2417
+ @change=${(n) => e(n.target.value)}
2418
+ ></textarea>
2419
+ </div>
2420
+ `;
2421
+ }
2422
+ var Zt = Object.defineProperty, te = Object.getOwnPropertyDescriptor, K = (t, e, o, n) => {
2423
+ for (var i = n > 1 ? void 0 : n ? te(e, o) : e, r = t.length - 1, s; r >= 0; r--)
2424
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
2425
+ return n && i && Zt(e, o, i), i;
2426
+ };
2427
+ let P = class extends v {
2428
+ constructor() {
2429
+ super(...arguments), this.storeCtrl = new S(this, ["design", "selection"]);
2430
+ }
2431
+ set store(t) {
2432
+ this.storeCtrl.setStore(t);
2433
+ }
2434
+ get store() {
2435
+ return this.storeCtrl.store;
2436
+ }
2437
+ /** Create a change handler bound to a specific content ID and property key */
2438
+ onChange(t, e) {
2439
+ return (o) => {
2440
+ this.store.updateContentValues(t, { [e]: o });
2441
+ };
2442
+ }
2443
+ render() {
2444
+ const t = this.store.selectedId;
2445
+ if (!t)
2446
+ return c`<div class="no-selection">Select an element to edit its properties</div>`;
2447
+ const e = this.store.findContent(t);
2448
+ if (!e)
2449
+ return c`<div class="no-selection">Select an element to edit its properties</div>`;
2450
+ const o = this.toolRegistry.get(e.type);
2451
+ return o ? c`
2452
+ <div class="header">
2453
+ <p class="header-title">${o.label}</p>
2454
+ <p class="header-type">${e.type}</p>
2455
+ </div>
2456
+ ${Object.entries(o.options).map(([, n]) => this.renderGroup(e, n))}
2457
+ ` : c`<div class="no-selection">Unknown tool: ${e.type}</div>`;
2458
+ }
2459
+ renderGroup(t, e) {
2460
+ return c`
2461
+ <div class="group">
2462
+ <div class="group-title">${e.title}</div>
2463
+ <div class="group-body">
2464
+ ${Object.entries(e.options).map(
2465
+ ([o, n]) => this.renderWidget(t, o, n)
2466
+ )}
2467
+ </div>
2468
+ </div>
2469
+ `;
2470
+ }
2471
+ /** Delegate to the correct widget function based on the property's widget type */
2472
+ renderWidget(t, e, o) {
2473
+ const n = t.values[e] ?? o.defaultValue, i = this.onChange(t.id, e);
2474
+ switch (o.widget) {
2475
+ case "color_picker":
2476
+ return Ft(n, i, o.label);
2477
+ case "toggle":
2478
+ return Jt(n, i, o.label);
2479
+ case "rich_text":
2480
+ return Qt(n, i, o.label);
2481
+ case "dropdown":
2482
+ return Nt(n, i, o.label, o.widgetParams);
2483
+ case "alignment":
2484
+ return qt(n, i, o.label);
2485
+ case "padding":
2486
+ return Kt(n, i, o.label);
2487
+ case "text":
2488
+ default:
2489
+ return Xt(n, i, o.label);
2490
+ }
2491
+ }
2492
+ };
2493
+ P.styles = $`
2494
+ :host {
2495
+ display: flex; flex-direction: column; width: 300px; min-width: 300px;
2496
+ background: #fff; border-left: 1px solid #e5e7eb;
2497
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2498
+ overflow-y: auto;
2499
+ }
2500
+ .header { padding: 12px 16px; border-bottom: 1px solid #e5e7eb; background: #f9fafb; }
2501
+ .header-title { font-size: 14px; font-weight: 600; color: #111827; margin: 0; }
2502
+ .header-type { font-size: 11px; color: #9ca3af; text-transform: uppercase; margin: 2px 0 0 0; }
2503
+ .no-selection {
2504
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
2505
+ flex: 1; color: #9ca3af; font-size: 13px; gap: 8px; padding: 40px 20px; text-align: center;
2506
+ }
2507
+ .group { border-bottom: 1px solid #f3f4f6; }
2508
+ .group-title {
2509
+ padding: 10px 16px; font-size: 12px; font-weight: 600; color: #6b7280;
2510
+ background: #f9fafb; cursor: pointer; user-select: none;
2511
+ display: flex; align-items: center; justify-content: space-between;
2512
+ }
2513
+ .group-title:hover { background: #f3f4f6; }
2514
+ .group-body { padding: 12px 16px; }
2515
+ .prop-row { margin-bottom: 12px; }
2516
+ .prop-label { display: block; font-size: 12px; color: #6b7280; margin-bottom: 4px; }
2517
+ .prop-input {
2518
+ width: 100%; padding: 6px 8px; border: 1px solid #d1d5db; border-radius: 4px;
2519
+ font-size: 13px; color: #111827; background: white; box-sizing: border-box;
2520
+ }
2521
+ .prop-input:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 2px rgba(59,130,246,0.15); }
2522
+ .prop-toggle { display: flex; align-items: center; gap: 8px; }
2523
+ .prop-color { display: flex; align-items: center; gap: 8px; }
2524
+ .color-swatch { width: 28px; height: 28px; border-radius: 4px; border: 1px solid #d1d5db; cursor: pointer; padding: 0; }
2525
+ textarea.prop-input { min-height: 80px; resize: vertical; font-family: monospace; }
2526
+ `;
2527
+ K([
2528
+ f({ attribute: !1 })
2529
+ ], P.prototype, "store", 1);
2530
+ K([
2531
+ f({ attribute: !1 })
2532
+ ], P.prototype, "toolRegistry", 2);
2533
+ P = K([
2534
+ k("me-property-panel")
2535
+ ], P);
2536
+ var ee = Object.defineProperty, oe = Object.getOwnPropertyDescriptor, st = (t, e, o, n) => {
2537
+ for (var i = n > 1 ? void 0 : n ? oe(e, o) : e, r = t.length - 1, s; r >= 0; r--)
2538
+ (s = t[r]) && (i = (n ? s(e, o, i) : s(i)) || i);
2539
+ return n && i && ee(e, o, i), i;
2540
+ };
2541
+ let A = class extends v {
2542
+ constructor() {
2543
+ super(...arguments), this.options = {}, this.store = new ft(), this.toolRegistry = new mt(), this.dragManager = null, this.callbacks = /* @__PURE__ */ new Map(), this.unsubscribe = null, this._handleKeydown = (t) => {
2544
+ const e = t.metaKey || t.ctrlKey, n = t.composedPath().some((i) => {
2545
+ const r = i?.tagName;
2546
+ return r === "INPUT" || r === "TEXTAREA" || r === "SELECT";
2547
+ });
2548
+ n && !e || (e && t.key === "z" && !t.shiftKey ? (t.preventDefault(), this.store.undo()) : e && (t.key === "y" || t.key === "z" && t.shiftKey) ? (t.preventDefault(), this.store.redo()) : (t.key === "Delete" || t.key === "Backspace") && this.store.selectedId && !n ? (t.preventDefault(), this.store.removeContent(this.store.selectedId)) : t.key === "Escape" && this.store.select(null));
2549
+ };
2550
+ }
2551
+ connectedCallback() {
2552
+ super.connectedCallback(), this.registerBuiltInTools(), this.applyOptions(), this.setAttribute("tabindex", "0"), this.addEventListener("keydown", this._handleKeydown);
2553
+ }
2554
+ firstUpdated() {
2555
+ this.dragManager = new xt(this.store, this.toolRegistry, this.shadowRoot), this.dragManager.attach(), this.store.events.on("design:loaded", (t) => {
2556
+ this.dispatchEvent(new CustomEvent("design:loaded", { detail: t, bubbles: !0, composed: !0 }));
2557
+ }), this.store.events.on("design:updated", (t) => {
2558
+ this.dispatchEvent(new CustomEvent("design:updated", { detail: t, bubbles: !0, composed: !0 }));
2559
+ }), this.dispatchEvent(new CustomEvent("editor:ready", { bubbles: !0, composed: !0 })), this.preloadLazyTools();
2560
+ }
2561
+ disconnectedCallback() {
2562
+ super.disconnectedCallback(), this.dragManager?.detach(), this.unsubscribe?.(), this.store.events.removeAllListeners(), this.removeEventListener("keydown", this._handleKeydown);
2563
+ }
2564
+ // ----------------------------------------------------------
2565
+ // Public API — public API
2566
+ // ----------------------------------------------------------
2567
+ loadDesign(t) {
2568
+ this.store.loadDesign(t);
2569
+ }
2570
+ saveDesign(t) {
2571
+ t(structuredClone(this.store.getDesign()));
2572
+ }
2573
+ exportHtml(t, e) {
2574
+ const o = this.store.getDesign(), n = /* @__PURE__ */ new Set();
2575
+ for (const r of o.body.rows)
2576
+ for (const s of r.columns)
2577
+ for (const l of s.contents)
2578
+ n.add(l.type);
2579
+ const i = Array.from(n).filter((r) => !this.toolRegistry.isLoaded(r)).map((r) => this.toolRegistry.ensureLoaded(r));
2580
+ i.length > 0 ? Promise.all(i).then(() => this.doExport(o, t, e)) : this.doExport(o, t, e);
2581
+ }
2582
+ doExport(t, e, o) {
2583
+ const n = /* @__PURE__ */ new Map();
2584
+ for (const i of this.toolRegistry.getAll())
2585
+ n.set(i.name, (r, s) => i.renderer.renderHtml(r, s));
2586
+ e(Tt(t, n, o));
2587
+ }
2588
+ async exportHtmlAsync(t) {
2589
+ return new Promise((e) => this.exportHtml(e, t));
2590
+ }
2591
+ registerTool(t) {
2592
+ this.toolRegistry.register(t), this.requestUpdate();
2593
+ }
2594
+ registerPropertyEditor(t, e) {
2595
+ }
2596
+ registerTab(t) {
2597
+ }
2598
+ registerCallback(t, e) {
2599
+ this.callbacks.set(t, e);
2600
+ }
2601
+ setMergeTags(t) {
2602
+ }
2603
+ undo() {
2604
+ this.store.undo();
2605
+ }
2606
+ redo() {
2607
+ this.store.redo();
2608
+ }
2609
+ setBodyValues(t) {
2610
+ this.store.updateBodyValues(t);
2611
+ }
2612
+ // ----------------------------------------------------------
2613
+ // Internal
2614
+ // ----------------------------------------------------------
2615
+ /**
2616
+ * Preload lazy tools during browser idle time.
2617
+ * Uses requestIdleCallback to avoid blocking the main thread.
2618
+ * Falls back to setTimeout(1000) for browsers without idle callback support.
2619
+ */
2620
+ preloadLazyTools() {
2621
+ const t = window.requestIdleCallback ?? ((e) => setTimeout(e, 1e3));
2622
+ for (const { meta: e } of ot)
2623
+ t(() => {
2624
+ this.toolRegistry.ensureLoaded(e.name);
2625
+ });
2626
+ }
2627
+ registerBuiltInTools() {
2628
+ for (const t of St)
2629
+ this.toolRegistry.register(t);
2630
+ for (const { meta: t, loader: e } of ot)
2631
+ this.toolRegistry.registerLazy(t, e);
2632
+ }
2633
+ applyOptions() {
2634
+ this.options.design && this.store.loadDesign(this.options.design);
2635
+ }
2636
+ render() {
2637
+ return c`
2638
+ <me-editor-sidebar
2639
+ .store=${this.store}
2640
+ .toolRegistry=${this.toolRegistry}
2641
+ ></me-editor-sidebar>
2642
+ <me-editor-canvas
2643
+ .store=${this.store}
2644
+ .toolRegistry=${this.toolRegistry}
2645
+ ></me-editor-canvas>
2646
+ <me-property-panel
2647
+ .store=${this.store}
2648
+ .toolRegistry=${this.toolRegistry}
2649
+ ></me-property-panel>
2650
+ `;
2651
+ }
2652
+ };
2653
+ A.styles = $`
2654
+ :host {
2655
+ display: flex;
2656
+ width: 100%;
2657
+ height: 100%;
2658
+ flex: 1;
2659
+ min-height: 500px;
2660
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2661
+ color: #111827;
2662
+ box-sizing: border-box;
2663
+ overflow: hidden;
2664
+ position: relative;
2665
+ }
2666
+ * { box-sizing: border-box; }
2667
+ `;
2668
+ st([
2669
+ f({ type: Object })
2670
+ ], A.prototype, "options", 2);
2671
+ A = st([
2672
+ k("mail-editor")
2673
+ ], A);
2674
+ function C(t, e) {
2675
+ customElements.get(t) || customElements.define(t, e);
2676
+ }
2677
+ C("mail-editor", A);
2678
+ C("me-editor-canvas", I);
2679
+ C("me-row-renderer", D);
2680
+ C("me-column-renderer", w);
2681
+ C("me-content-renderer", _);
2682
+ C("me-editor-sidebar", T);
2683
+ C("me-body-settings", L);
2684
+ C("me-property-panel", P);
2685
+ const ae = customElements.get("mail-editor") !== void 0;
2686
+ export {
2687
+ ae as E,
2688
+ A as M,
2689
+ mt as T,
2690
+ ft as a,
2691
+ V as e,
2692
+ se as j,
2693
+ a as s
2694
+ };
2695
+ //# sourceMappingURL=index-CZt184D3.js.map