@emabuild/core 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,410 @@
1
+ # @emabuild — Drag & Drop Email Editor
2
+
3
+ A fully embeddable drag-and-drop email editor Web Component. Cross-client email HTML export, and 13 built-in content blocks.
4
+
5
+ Built with [Lit 3](https://lit.dev/) — works in Angular, React, Vue, or plain HTML.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm install @emabuild/core lit
11
+ ```
12
+
13
+ ```html
14
+ <mail-editor id="editor" style="height:100vh;"></mail-editor>
15
+
16
+ <script type="module">
17
+ import '@emabuild/core/mail-editor';
18
+
19
+ const editor = document.getElementById('editor');
20
+
21
+ editor.addEventListener('editor:ready', () => {
22
+ console.log('Editor is ready');
23
+ });
24
+ </script>
25
+ ```
26
+
27
+ ## Packages
28
+
29
+ | Package | Description | Size |
30
+ |---------|-------------|------|
31
+ | [`@emabuild/core`](https://www.npmjs.com/package/@emabuild/core) | `<mail-editor>` Web Component | ~17KB gzip |
32
+ | [`@emabuild/email-renderer`](https://www.npmjs.com/package/@emabuild/email-renderer) | Standalone HTML export engine (works server-side) | ~3KB gzip |
33
+ | [`@emabuild/types`](https://www.npmjs.com/package/@emabuild/types) | TypeScript type definitions | types only |
34
+
35
+ ## Framework Integration
36
+
37
+ ### Angular
38
+
39
+ ```bash
40
+ npm install @emabuild/core lit
41
+ ```
42
+
43
+ ```typescript
44
+ // app.module.ts
45
+ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
46
+
47
+ @NgModule({
48
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
49
+ })
50
+ export class AppModule {}
51
+ ```
52
+
53
+ ```typescript
54
+ // app.component.ts
55
+ import '@emabuild/core/mail-editor';
56
+
57
+ @Component({
58
+ template: `<mail-editor #editor style="height:100vh;"></mail-editor>
59
+ <button (click)="exportHtml()">Export</button>`,
60
+ })
61
+ export class AppComponent {
62
+ @ViewChild('editor') editor!: ElementRef;
63
+
64
+ exportHtml() {
65
+ this.editor.nativeElement.exportHtml((result) => {
66
+ console.log(result.html);
67
+ });
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### React
73
+
74
+ ```tsx
75
+ import '@emabuild/core/mail-editor';
76
+ import { useRef, useEffect } from 'react';
77
+
78
+ export function EmailEditor() {
79
+ const editorRef = useRef<any>(null);
80
+
81
+ useEffect(() => {
82
+ const el = editorRef.current;
83
+ el?.addEventListener('editor:ready', () => {
84
+ el.loadDesign(myDesign);
85
+ });
86
+ }, []);
87
+
88
+ const handleExport = () => {
89
+ editorRef.current?.exportHtml((result) => {
90
+ console.log(result.html);
91
+ });
92
+ };
93
+
94
+ return (
95
+ <div style={{ height: '100vh' }}>
96
+ <mail-editor ref={editorRef} />
97
+ <button onClick={handleExport}>Export HTML</button>
98
+ </div>
99
+ );
100
+ }
101
+ ```
102
+
103
+ ### Vue
104
+
105
+ ```vue
106
+ <template>
107
+ <mail-editor ref="editor" style="height: 100vh" />
108
+ <button @click="exportHtml">Export</button>
109
+ </template>
110
+
111
+ <script setup>
112
+ import '@emabuild/core/mail-editor';
113
+ import { ref, onMounted } from 'vue';
114
+
115
+ const editor = ref(null);
116
+
117
+ onMounted(() => {
118
+ editor.value.addEventListener('editor:ready', () => {
119
+ editor.value.loadDesign(myDesign);
120
+ });
121
+ });
122
+
123
+ function exportHtml() {
124
+ editor.value.exportHtml((result) => {
125
+ console.log(result.html);
126
+ });
127
+ }
128
+ </script>
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ ### Methods
134
+
135
+ ```typescript
136
+ // Load a design JSON
137
+ editor.loadDesign(design: EmailDesign): void;
138
+
139
+ // Save the current design as JSON
140
+ editor.saveDesign(callback: (design: EmailDesign) => void): void;
141
+
142
+ // Export email-ready HTML
143
+ editor.exportHtml(callback: (result: ExportResult) => void, options?: ExportOptions): void;
144
+
145
+ // Promise-based export
146
+ const result = await editor.exportHtmlAsync(options?: ExportOptions);
147
+
148
+ // Undo / Redo
149
+ editor.undo(): void;
150
+ editor.redo(): void;
151
+
152
+ // Register a custom tool
153
+ editor.registerTool(definition: ToolDefinition): void;
154
+
155
+ // Register a callback (e.g. for image upload)
156
+ editor.registerCallback(type: string, callback: Function): void;
157
+
158
+ // Update body-level settings
159
+ editor.setBodyValues(values: Partial<BodyValues>): void;
160
+ ```
161
+
162
+ ### Events
163
+
164
+ ```typescript
165
+ editor.addEventListener('editor:ready', () => {
166
+ // Editor is fully initialized
167
+ });
168
+
169
+ editor.addEventListener('design:loaded', (e) => {
170
+ // A design was loaded via loadDesign()
171
+ console.log(e.detail.design);
172
+ });
173
+
174
+ editor.addEventListener('design:updated', (e) => {
175
+ // User made a change
176
+ console.log(e.detail.type); // 'content_updated' | 'row_added' | ...
177
+ });
178
+ ```
179
+
180
+ ### Export Result
181
+
182
+ ```typescript
183
+ editor.exportHtml((result) => {
184
+ result.design; // EmailDesign JSON — save this for future editing
185
+ result.html; // Complete HTML document (<!DOCTYPE> to </html>)
186
+ result.chunks; // { body, css, fonts[], js }
187
+ });
188
+ ```
189
+
190
+ ### Export Options
191
+
192
+ ```typescript
193
+ editor.exportHtml(callback, {
194
+ minify: true, // Minify HTML output
195
+ inlineStyles: true, // Inline CSS into style attributes
196
+ cleanup: true, // Remove unused CSS classes
197
+ mergeTags: { // Replace merge tags with values
198
+ first_name: 'John',
199
+ company: 'Acme',
200
+ },
201
+ });
202
+ ```
203
+
204
+ ## Built-in Content Blocks
205
+
206
+ | Block | Description | Email Safe |
207
+ |-------|-------------|:----------:|
208
+ | Text | Rich text with formatting | Yes |
209
+ | Heading | H1-H4 with size/weight/color | Yes |
210
+ | Paragraph | Block text with line-height | Yes |
211
+ | Image | Responsive image with link | Yes |
212
+ | Button | CTA button (VML Outlook fallback) | Yes |
213
+ | Divider | Horizontal line | Yes |
214
+ | HTML | Raw HTML injection | Yes |
215
+ | Social | Social media icon links | Yes |
216
+ | Menu | Horizontal navigation | Yes |
217
+ | Video | YouTube/Vimeo thumbnail + play | Yes |
218
+ | Timer | Countdown display | Yes |
219
+ | Table | Data table with headers | Yes |
220
+ | Form | Input form (web mode only) | Web only |
221
+
222
+ ## Layout Presets
223
+
224
+ Drag or click to add rows with predefined column layouts:
225
+
226
+ - `100%` — single column
227
+ - `50 / 50` — two equal columns
228
+ - `33 / 33 / 33` — three equal columns
229
+ - `66 / 33` — two-thirds + one-third
230
+ - `33 / 66` — one-third + two-thirds
231
+ - `25 / 25 / 25 / 25` — four equal columns
232
+
233
+ ## Custom Tools
234
+
235
+ Register custom content blocks with their own properties and renderers:
236
+
237
+ ```typescript
238
+ import { html } from 'lit';
239
+
240
+ editor.registerTool({
241
+ name: 'product_card',
242
+ label: 'Product Card',
243
+ icon: '<svg>...</svg>',
244
+ supportedDisplayModes: ['email'],
245
+ options: {
246
+ product: {
247
+ title: 'Product',
248
+ options: {
249
+ name: { label: 'Name', defaultValue: 'Product', widget: 'text' },
250
+ price: { label: 'Price', defaultValue: '$0.00', widget: 'text' },
251
+ image: { label: 'Image URL', defaultValue: '', widget: 'text' },
252
+ bgColor: { label: 'Background', defaultValue: '#ffffff', widget: 'color_picker' },
253
+ },
254
+ },
255
+ },
256
+ defaultValues: {
257
+ name: 'Product', price: '$0.00', image: '', bgColor: '#ffffff',
258
+ },
259
+ renderer: {
260
+ renderEditor(values) {
261
+ return html`
262
+ <div style="background:${values.bgColor};padding:16px;text-align:center;">
263
+ <img src="${values.image}" style="max-width:100%;border-radius:8px;" />
264
+ <h3 style="margin:12px 0 4px;">${values.name}</h3>
265
+ <p style="color:#3b82f6;font-weight:bold;">${values.price}</p>
266
+ </div>
267
+ `;
268
+ },
269
+ renderHtml(values, ctx) {
270
+ return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
271
+ <tr><td style="background-color:${values.bgColor};padding:16px;text-align:center;">
272
+ <img src="${values.image}" width="${ctx.columnWidth}" style="max-width:100%;" />
273
+ <h3 style="margin:12px 0 4px;">${values.name}</h3>
274
+ <p style="color:#3b82f6;font-weight:bold;">${values.price}</p>
275
+ </td></tr>
276
+ </table>`;
277
+ },
278
+ },
279
+ });
280
+ ```
281
+
282
+ ## Design JSON Structure
283
+
284
+ Designs are stored as structured JSON. The format is interoperable with other email editor tools.
285
+
286
+ ```json
287
+ {
288
+ "counters": { "u_row": 1, "u_column": 1, "u_content_text": 1 },
289
+ "body": {
290
+ "id": "u_body",
291
+ "rows": [
292
+ {
293
+ "id": "u_row_1",
294
+ "cells": [1],
295
+ "columns": [
296
+ {
297
+ "id": "u_column_1",
298
+ "contents": [
299
+ {
300
+ "id": "u_content_text_1",
301
+ "type": "text",
302
+ "values": {
303
+ "text": "<p>Hello World</p>",
304
+ "containerPadding": "10px",
305
+ "_meta": { "htmlID": "u_content_text_1", "htmlClassNames": "u_content_text" }
306
+ }
307
+ }
308
+ ],
309
+ "values": { "backgroundColor": "", "padding": "0px", "_meta": { "htmlID": "u_column_1", "htmlClassNames": "u_column" } }
310
+ }
311
+ ],
312
+ "values": { "backgroundColor": "", "columnsBackgroundColor": "#ffffff", "padding": "0px", "_meta": { "htmlID": "u_row_1", "htmlClassNames": "u_row" } }
313
+ }
314
+ ],
315
+ "headers": [],
316
+ "footers": [],
317
+ "values": {
318
+ "backgroundColor": "#e7e7e7",
319
+ "contentWidth": "600px",
320
+ "fontFamily": { "label": "Arial", "value": "arial,helvetica,sans-serif" },
321
+ "textColor": "#000000",
322
+ "preheaderText": "",
323
+ "_meta": { "htmlID": "u_body", "htmlClassNames": "u_body" }
324
+ }
325
+ },
326
+ "schemaVersion": 16
327
+ }
328
+ ```
329
+
330
+ ## Server-Side Rendering
331
+
332
+ Use `@emabuild/email-renderer` to generate HTML from design JSON on the server (Node.js):
333
+
334
+ ```typescript
335
+ import { renderDesignToHtml } from '@emabuild/email-renderer';
336
+
337
+ const toolRenderers = new Map();
338
+ // Register tool HTML renderers...
339
+
340
+ const result = renderDesignToHtml(designJson, toolRenderers, {
341
+ minify: true,
342
+ mergeTags: { first_name: 'John' },
343
+ });
344
+
345
+ console.log(result.html); // Complete email HTML
346
+ ```
347
+
348
+ ## Email Client Support
349
+
350
+ Exported HTML uses fluid hybrid design with MSO conditional comments for maximum compatibility:
351
+
352
+ - Gmail (Web, iOS, Android)
353
+ - Outlook (2016, 2019, 365, Outlook.com)
354
+ - Apple Mail (macOS, iOS)
355
+ - Yahoo Mail
356
+ - Thunderbird
357
+ - Samsung Mail
358
+
359
+ Key techniques used:
360
+ - Table-based layout with `role="presentation"`
361
+ - MSO ghost tables (`<!--[if mso]>`) for Outlook column rendering
362
+ - VML roundrect for Outlook button border-radius
363
+ - Responsive CSS with `@media` queries
364
+ - Fluid hybrid columns (`display:inline-block` + `max-width`)
365
+ - Dark mode support (`prefers-color-scheme`)
366
+ - Preheader text with invisible padding fill
367
+
368
+ ## Keyboard Shortcuts
369
+
370
+ | Shortcut | Action |
371
+ |----------|--------|
372
+ | `Ctrl/Cmd + Z` | Undo |
373
+ | `Ctrl/Cmd + Y` | Redo |
374
+ | `Ctrl/Cmd + Shift + Z` | Redo |
375
+ | `Delete` / `Backspace` | Delete selected element |
376
+ | `Escape` | Deselect |
377
+
378
+ ## Property Widgets
379
+
380
+ Available widget types for tool property definitions:
381
+
382
+ | Widget | Type | Description |
383
+ |--------|------|-------------|
384
+ | `text` | `string` | Single-line text input |
385
+ | `rich_text` | `string` | Multi-line textarea (HTML) |
386
+ | `color_picker` | `string` | Color swatch + hex input |
387
+ | `toggle` | `boolean` | Checkbox toggle |
388
+ | `dropdown` | `string` | Select dropdown (requires `widgetParams.options`) |
389
+ | `alignment` | `string` | Left/center/right visual picker |
390
+ | `padding` | `string` | 4-side padding editor (CSS shorthand) |
391
+
392
+ ## Development
393
+
394
+ ```bash
395
+ # Install dependencies
396
+ pnpm install
397
+
398
+ # Start the demo app
399
+ pnpm --filter @emabuild/demo dev
400
+
401
+ # Build all packages
402
+ pnpm build
403
+
404
+ # Publish to npm
405
+ pnpm -r publish --access public
406
+ ```
407
+
408
+ ## License
409
+
410
+ MIT
package/dist/index.d.ts CHANGED
@@ -2,5 +2,5 @@ export { MailEditorElement } from './mail-editor.js';
2
2
  export { EditorStore } from './state/editor-store.js';
3
3
  export { ToolRegistry } from './tools/tool-registry.js';
4
4
  export type { LitToolDefinition } from './tools/tool-registry.js';
5
- export type { UnlayerDesign, DesignBody, DesignRow, DesignColumn, DesignContent, MailEditorConfig, ExportResult, ExportOptions, ToolDefinition, ToolPropertyGroup, ToolProperty, } from '@emabuild/types';
5
+ export type { EmailDesign, DesignBody, DesignRow, DesignColumn, DesignContent, MailEditorConfig, ExportResult, ExportOptions, ToolDefinition, ToolPropertyGroup, ToolProperty, } from '@emabuild/types';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,YAAY,EACV,aAAa,EACb,UAAU,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,YAAY,GACb,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,YAAY,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,YAAY,GACb,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { E, M, T } from "./mail-editor-ClkIyPni.js";
1
+ import { E, M, T } from "./mail-editor-D0FbEUZu.js";
2
2
  export {
3
3
  E as EditorStore,
4
4
  M as MailEditorElement,
@@ -2135,7 +2135,7 @@ let MailEditorElement = class extends LitElement {
2135
2135
  this.removeEventListener("keydown", this._handleKeydown);
2136
2136
  }
2137
2137
  // ----------------------------------------------------------
2138
- // Public API — mirrors Unlayer
2138
+ // Public API — public API
2139
2139
  // ----------------------------------------------------------
2140
2140
  loadDesign(design) {
2141
2141
  this.store.loadDesign(design);
@@ -2242,4 +2242,4 @@ export {
2242
2242
  MailEditorElement as M,
2243
2243
  ToolRegistry as T
2244
2244
  };
2245
- //# sourceMappingURL=mail-editor-ClkIyPni.js.map
2245
+ //# sourceMappingURL=mail-editor-D0FbEUZu.js.map