@lab2view/vue-email-editor 0.1.0 → 0.1.1

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 CHANGED
@@ -1,6 +1,23 @@
1
- # @lab2view/vue-email-editor
2
-
3
- A professional, extensible drag-and-drop email editor built with **Vue 3** and **MJML**. Design responsive HTML emails visually with 43 pre-built blocks, a plugin system, full i18n support, and a complete imperative API.
1
+ <p align="center">
2
+ <img src="src/assets/logo.png" alt="@lab2view/vue-email-editor" width="200" />
3
+ </p>
4
+
5
+ <h1 align="center">@lab2view/vue-email-editor</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@lab2view/vue-email-editor"><img src="https://img.shields.io/npm/v/@lab2view/vue-email-editor.svg?style=flat-square" alt="npm version" /></a>
9
+ <a href="https://github.com/lab2view/vue-email-editor/actions"><img src="https://img.shields.io/github/actions/workflow/status/lab2view/vue-email-editor/ci.yml?style=flat-square" alt="CI" /></a>
10
+ <a href="https://github.com/lab2view/vue-email-editor/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@lab2view/vue-email-editor.svg?style=flat-square" alt="license" /></a>
11
+ <img src="https://img.shields.io/badge/vue-3.x-42b883?style=flat-square&logo=vue.js" alt="Vue 3" />
12
+ <img src="https://img.shields.io/badge/mjml-4.x-e54434?style=flat-square" alt="MJML" />
13
+ <img src="https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square&logo=typescript" alt="TypeScript" />
14
+ </p>
15
+
16
+ <p align="center">
17
+ A professional, extensible drag-and-drop email editor built with <strong>Vue 3</strong> and <strong>MJML</strong>.<br/>
18
+ Design responsive HTML emails visually with 43 pre-built blocks, a plugin system, full i18n support, and a complete imperative API.<br/>
19
+ <strong>Free and open-source alternative to Unlayer, Beefree, and Stripo.</strong>
20
+ </p>
4
21
 
5
22
  ## Screenshots
6
23
 
@@ -46,12 +63,54 @@ Powered by [TipTap](https://tiptap.dev), with inline formatting:
46
63
  - Full history with keyboard shortcuts (`Ctrl+Z` / `Ctrl+Shift+Z`)
47
64
  - Reactive `canUndo` / `canRedo` state
48
65
 
66
+ ### Merge Tags
67
+ - Insert dynamic variables into text content (`{{first_name}}`, `{{company}}`, etc.)
68
+ - Categorized merge tag picker in the inline toolbar
69
+ - Visual chip rendering in the editor
70
+ - TipTap extension for atomic merge tag nodes
71
+
72
+ ### Conditional Content
73
+ - Show/hide email sections based on merge tag values
74
+ - Visual condition builder on any node (variable, operator, value)
75
+ - 6 operators: equals, not equals, contains, not contains, exists, not exists
76
+ - Exports as HTML conditional comments for ESP processing
77
+
78
+ ### AI Text Generation
79
+ - BYOAI (Bring Your Own AI) pattern — plug in any AI provider
80
+ - Generate, improve, shorten, expand, and translate text
81
+ - Custom prompt input for freeform generation
82
+ - Non-blocking async generation with loading state
83
+
84
+ ### Dark Mode Preview
85
+ - Toggle dark mode preview in the toolbar (Moon/Sun icon)
86
+ - Simulates email client dark mode behavior in the canvas
87
+ - Background inversion, text color adjustment, link color adaptation
88
+
89
+ ### ESP Export Presets
90
+ - Export HTML pre-formatted for 6 email service providers
91
+ - **Mailchimp**: `*|FNAME|*` merge tags, auto-inject unsubscribe
92
+ - **SendGrid**: `{{variable}}` handlebars format
93
+ - **Brevo**: `{{ contact.ATTRIBUTE }}` format
94
+ - **AWS SES**: `{{camelCase}}` template variables
95
+ - **Postmark**: `{{variable}}` Mustache format
96
+ - **Resend**: `{{variable}}` format
97
+ - Custom preset support for any ESP
98
+
99
+ ### Image Upload
100
+ - Drag-and-drop image upload with progress indicator
101
+ - Asset browser integration via callback
102
+ - File type validation and size limits
103
+ - Preview with change/remove actions
104
+
49
105
  ### Property Editing
50
106
  - 40+ editable MJML attributes across 11 property types
51
107
  - Color pickers, padding controls, alignment selectors
52
108
  - Global styles panel (email background, default font, preview text)
53
109
  - Custom fonts support
54
110
 
111
+ ### 22 Starter Templates
112
+ Professional email templates for common use cases: welcome, newsletter, e-commerce, abandoned cart, product launch, shipping notification, birthday, seasonal sale, SaaS onboarding, and more.
113
+
55
114
  ---
56
115
 
57
116
  ## Installation
@@ -332,6 +391,13 @@ import {
332
391
  DEFAULT_THEME, STATIC_BLOCKS,
333
392
  CONTENT_NODE_TYPES, CONTAINER_NODE_TYPES, SELF_CLOSING_NODE_TYPES,
334
393
  } from '@lab2view/vue-email-editor'
394
+
395
+ // ESP Export Presets
396
+ import {
397
+ exportForEsp, exportForMailchimp, exportForSendGrid,
398
+ exportForBrevo, exportForAwsSes, exportForPostmark, exportForResend,
399
+ ESP_PRESETS, MAILCHIMP_PRESET, SENDGRID_PRESET,
400
+ } from '@lab2view/vue-email-editor'
335
401
  ```
336
402
 
337
403
  ## Keyboard Shortcuts
@@ -356,6 +422,77 @@ import {
356
422
  | `required` | `boolean` | `false` | Form validation flag |
357
423
  | `theme` | `Partial<ThemeConfig>` | `DEFAULT_THEME` | Visual customization |
358
424
  | `plugins` | `Plugin[]` | `[]` | Editor extensions |
425
+ | `mergeTags` | `MergeTag[]` | — | Dynamic variable tags |
426
+ | `aiProvider` | `AiProvider` | — | AI text generation callbacks |
427
+ | `onImageUpload` | `(file: File) => Promise<{ url }>` | — | Image upload handler |
428
+ | `onBrowseAssets` | `() => Promise<string \| null>` | — | Asset browser handler |
429
+
430
+ ## Merge Tags
431
+
432
+ Insert dynamic content with merge tag variables:
433
+
434
+ ```vue
435
+ <EmailEditor
436
+ :merge-tags="[
437
+ { name: 'First Name', value: '{{first_name}}', category: 'Contact' },
438
+ { name: 'Company', value: '{{company}}', category: 'Contact' },
439
+ { name: 'Unsubscribe', value: '{{unsubscribe_url}}', category: 'Links' },
440
+ ]"
441
+ />
442
+ ```
443
+
444
+ ## AI Text Generation
445
+
446
+ Bring your own AI provider for inline text generation:
447
+
448
+ ```vue
449
+ <EmailEditor
450
+ :ai-provider="{
451
+ generateText: async (prompt, context) => {
452
+ const response = await fetch('/api/ai/generate', {
453
+ method: 'POST',
454
+ body: JSON.stringify({ prompt, context }),
455
+ })
456
+ return (await response.json()).text
457
+ },
458
+ improveText: async (text, instruction) => {
459
+ const response = await fetch('/api/ai/improve', {
460
+ method: 'POST',
461
+ body: JSON.stringify({ text, instruction }),
462
+ })
463
+ return (await response.json()).text
464
+ },
465
+ }"
466
+ />
467
+ ```
468
+
469
+ ## ESP Export
470
+
471
+ Export HTML pre-formatted for your email service provider:
472
+
473
+ ```ts
474
+ import {
475
+ exportForMailchimp,
476
+ exportForSendGrid,
477
+ exportForBrevo,
478
+ exportForAwsSes,
479
+ exportForEsp,
480
+ } from '@lab2view/vue-email-editor'
481
+
482
+ // Get the document from the editor
483
+ const document = editorRef.value.getDocument()
484
+
485
+ // Export for Mailchimp (transforms {{first_name}} → *|FNAME|*)
486
+ const { html, mjml } = await exportForMailchimp(document)
487
+
488
+ // Export for SendGrid
489
+ const result = await exportForSendGrid(document)
490
+
491
+ // Export with custom merge tag mappings
492
+ const custom = await exportForEsp(document, 'brevo', {
493
+ mergeTags: { plan_name: '{{ contact.PLAN }}' },
494
+ })
495
+ ```
359
496
 
360
497
  ## Development
361
498
 
@@ -363,7 +500,7 @@ import {
363
500
  # Type check
364
501
  npm run typecheck
365
502
 
366
- # Run tests (64 tests)
503
+ # Run tests (147 tests)
367
504
  npm test
368
505
 
369
506
  # Build library
@@ -0,0 +1,69 @@
1
+ import { defineComponent as u, inject as f, ref as p, watch as h, onMounted as _, onBeforeUnmount as g, openBlock as E, createElementBlock as M } from "vue";
2
+ import { E as j, o as x, l as C, h as k, k as v, d as y, i as w, x as D, a as c } from "./codemirror-CWpT3Qh8.js";
3
+ import { E as S, m as b, d as B } from "./index-CT-stdGQ.js";
4
+ const A = /* @__PURE__ */ u({
5
+ __name: "CodeEditor",
6
+ setup(T) {
7
+ const n = f(S), r = p(null);
8
+ let e = null, a = !1;
9
+ function i() {
10
+ return B(n.document.value);
11
+ }
12
+ function l(t) {
13
+ const o = j.create({
14
+ doc: i(),
15
+ extensions: [
16
+ C(),
17
+ k(),
18
+ v.of([...y, w]),
19
+ D(),
20
+ x,
21
+ c.updateListener.of((s) => {
22
+ if (s.docChanged) {
23
+ a = !0;
24
+ const m = s.state.doc.toString();
25
+ try {
26
+ const d = b(m);
27
+ n.replaceDocument(d);
28
+ } catch {
29
+ }
30
+ }
31
+ }),
32
+ c.theme({
33
+ "&": { height: "100%", fontSize: "13px" },
34
+ ".cm-scroller": { overflow: "auto" },
35
+ ".cm-content": { fontFamily: "'JetBrains Mono', 'Fira Code', monospace" }
36
+ })
37
+ ]
38
+ });
39
+ e = new c({ state: o, parent: t });
40
+ }
41
+ return h(
42
+ () => n.document.value,
43
+ () => {
44
+ if (a) {
45
+ a = !1;
46
+ return;
47
+ }
48
+ if (!e) return;
49
+ const t = i(), o = e.state.doc.toString();
50
+ t !== o && e.dispatch({
51
+ changes: { from: 0, to: e.state.doc.length, insert: t }
52
+ });
53
+ },
54
+ { deep: !0 }
55
+ ), _(() => {
56
+ r.value && l(r.value);
57
+ }), g(() => {
58
+ e == null || e.destroy(), e = null;
59
+ }), (t, o) => (E(), M("div", {
60
+ ref_key: "editorContainer",
61
+ ref: r,
62
+ class: "ebb-code-editor"
63
+ }, null, 512));
64
+ }
65
+ });
66
+ export {
67
+ A as default
68
+ };
69
+ //# sourceMappingURL=CodeEditor-DMzmmIKl.js.map