@lab2view/vue-email-editor 0.2.7 → 0.2.10

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
@@ -10,129 +10,18 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- A professional, extensible <strong>drag-and-drop</strong> email editor with <strong>AI-assisted editing</strong>, built with <strong>Vue 3</strong> and <strong>MJML</strong>.<br/>
14
- Design responsive HTML emails visually or let AI generate complete templates from a text description. 43 pre-built blocks, a plugin system, full i18n support, and a complete imperative API.<br/>
15
- <!-- <strong>Free and open-source alternative to Unlayer, Beefree, and Stripo.</strong> -->
13
+ A professional <strong>drag-and-drop</strong> email editor built with <strong>Vue 3</strong> and <strong>MJML</strong>.<br/>
14
+ Design responsive HTML emails visually 43 blocks, AI generation, merge tags, plugins, i18n, and more.
16
15
  </p>
17
16
 
18
- <!-- <p align="center">
19
- <a href="https://lab2view.github.io/vue-email-editor/#templates"><strong>Try the editor live &rarr;</strong></a>
20
- </p> -->
21
-
22
- ## Screenshots
23
-
24
- | Blocks & Layout | Ready-made Templates |
25
- |:---:|:---:|
26
- | ![Blocks panel with layout and content blocks](src/assets/capture_blocs_layouts_1.png) | ![Ready-made composite blocks](src/assets/capture_blocs_ready_made_1.png) |
27
-
28
- | Property Editing | Layers Panel |
17
+ | Drag & Drop Builder | Ready-made Templates |
29
18
  |:---:|:---:|
30
- | ![Image properties and styling](src/assets/capture_blocs_styes_image_1.png) | ![Document tree layers view](src/assets/capture_layers_1.png) |
31
-
32
- | AI Chat & Preview |
33
- |:---:|
34
- | ![AI chat panel with template preview](src/assets/capture_ia_chat_preview_1.png) |
35
-
36
- ## Features
37
-
38
- ### Visual Drag & Drop Builder
39
- - Intuitive block-based editor with live preview
40
- - Drag blocks from the sidebar onto the canvas
41
- - Reorder and nest elements with visual drop indicators
42
- - Responsive preview in Desktop, Tablet, and Mobile modes
43
- - Iframe-isolated canvas for accurate email rendering
44
-
45
- ### 43 Pre-built Blocks
46
-
47
- | Category | Count | Examples |
48
- |----------|-------|---------|
49
- | **Layout** | 6 | 1-4 columns, sidebar left/right |
50
- | **Content** | 7 | Text, Image, Button, Divider, Spacer, Social, Hero |
51
- | **Composite** | 30 | Header, Hero Banner, Pricing, Testimonial, FAQ, Product Card, Footer, and more |
52
-
53
- ### Rich Text Editing
54
- Powered by [TipTap](https://tiptap.dev), with inline formatting:
55
- - Bold, Italic, Underline, Strikethrough
56
- - Text alignment (left, center, right)
57
- - Text color picker
58
- - Link insertion and editing
59
-
60
- ### MJML-Powered Rendering
61
- - Real-time MJML to HTML compilation via [mjml-browser](https://github.com/mjmlio/mjml)
62
- - 13 supported MJML node types
63
- - Export as MJML, compiled HTML, or design JSON
64
- - Legacy GrapesJS format detection for migration
65
-
66
- ### Undo / Redo
67
- - Full history with keyboard shortcuts (`Ctrl+Z` / `Ctrl+Shift+Z`)
68
- - Reactive `canUndo` / `canRedo` state
69
-
70
- ### Merge Tags
71
- - Insert dynamic variables into text content (`{{first_name}}`, `{{company}}`, etc.)
72
- - Categorized merge tag picker in the inline toolbar
73
- - Visual chip rendering in the editor
74
- - TipTap extension for atomic merge tag nodes
75
-
76
- ### Conditional Content
77
- - Show/hide email sections based on merge tag values
78
- - Visual condition builder on any node (variable, operator, value)
79
- - 6 operators: equals, not equals, contains, not contains, exists, not exists
80
- - Exports as HTML conditional comments for ESP processing
81
-
82
- ### AI Template Generation
83
- - Describe an email in plain language and get a complete, production-ready template
84
- - Built-in AI chat panel with multi-turn conversation for iterative refinement
85
- - Live HTML preview of the generated template before applying
86
- - Streaming support for real-time generation feedback
87
- - Automatic JSON repair for robust parsing of AI responses with auto-retry
88
- - BYOAI (Bring Your Own AI) — plug in OpenAI, Anthropic, or any LLM
89
-
90
- ### AI Text Generation
91
- - Inline text generation, improvement, shortening, expansion, and translation
92
- - Custom prompt input for freeform generation
93
- - Non-blocking async generation with loading state
94
-
95
- ### Dark Mode Preview
96
- - Toggle dark mode preview in the toolbar (Moon/Sun icon)
97
- - Simulates email client dark mode behavior in the canvas
98
- - Background inversion, text color adjustment, link color adaptation
99
-
100
- ### ESP Export Presets
101
- - Export HTML pre-formatted for 6 email service providers
102
- - **Mailchimp**: `*|FNAME|*` merge tags, auto-inject unsubscribe
103
- - **SendGrid**: `{{variable}}` handlebars format
104
- - **Brevo**: `{{ contact.ATTRIBUTE }}` format
105
- - **AWS SES**: `{{camelCase}}` template variables
106
- - **Postmark**: `{{variable}}` Mustache format
107
- - **Resend**: `{{variable}}` format
108
- - Custom preset support for any ESP
109
-
110
- ### Image Upload
111
- - Drag-and-drop image upload with progress indicator
112
- - Asset browser integration via callback
113
- - File type validation and size limits
114
- - Preview with change/remove actions
115
-
116
- ### Property Editing
117
- - 40+ editable MJML attributes across 11 property types
118
- - Color pickers, padding controls, alignment selectors
119
- - Global styles panel (email background, default font, preview text)
120
- - Custom fonts support
121
-
122
- ### 22 Starter Templates
123
- Professional email templates for common use cases: welcome, newsletter, e-commerce, abandoned cart, product launch, shipping notification, birthday, seasonal sale, SaaS onboarding, and more.
124
-
125
- ---
19
+ | ![Blocks panel](src/assets/capture_blocs_layouts_1.png) | ![Ready-made composite blocks](src/assets/capture_blocs_ready_made_1.png) |
126
20
 
127
21
  ## Installation
128
22
 
129
23
  ```bash
130
- npm install @lab2view/vue-email-editor
131
- ```
132
-
133
- **Peer dependencies:**
134
- ```bash
135
- npm install vue@^3.4 mjml-browser@^4.15
24
+ npm install @lab2view/vue-email-editor vue@^3.4 mjml-browser@^4.15
136
25
  ```
137
26
 
138
27
  ## Quick Start
@@ -140,9 +29,8 @@ npm install vue@^3.4 mjml-browser@^4.15
140
29
  ```vue
141
30
  <script setup lang="ts">
142
31
  import { ref } from 'vue'
143
- import { EmailEditor, FR_LABELS } from '@lab2view/vue-email-editor'
32
+ import { EmailEditor } from '@lab2view/vue-email-editor'
144
33
 
145
- const editorRef = ref()
146
34
  const mjml = ref('')
147
35
  const html = ref('')
148
36
  const designJson = ref()
@@ -153,399 +41,60 @@ const designJson = ref()
153
41
  ref="editorRef"
154
42
  v-model="mjml"
155
43
  :design-json="designJson"
156
- :labels="FR_LABELS"
157
44
  @update:compiled-html="html = $event"
158
45
  @update:design-json="designJson = $event"
159
46
  />
160
47
  </template>
161
48
  ```
162
49
 
163
- ## Theming
164
-
165
- Customize the editor appearance with the `theme` prop:
166
-
167
- ```vue
168
- <EmailEditor
169
- :theme="{
170
- primaryColor: '#7C3AED',
171
- primaryHover: '#6D28D9',
172
- borderRadius: '8px',
173
- fontFamily: 'Inter, sans-serif',
174
- }"
175
- />
176
- ```
177
-
178
- ### Available Theme Properties
179
-
180
- | Property | Default | Description |
181
- |----------|---------|-------------|
182
- | `primaryColor` | `#01A8AB` | Main accent color |
183
- | `primaryHover` | `#018F91` | Hover state |
184
- | `primaryActive` | `#017375` | Active/pressed state |
185
- | `backgroundColor` | `#ffffff` | Panel backgrounds |
186
- | `borderColor` | `#e5e7eb` | Border color |
187
- | `textPrimary` | `#1f2937` | Primary text |
188
- | `textSecondary` | `#6b7280` | Secondary text |
189
- | `canvasBg` | `#e5e7eb` | Canvas background |
190
- | `selectionColor` | `#01A8AB` | Selected node outline |
191
- | `fontFamily` | System stack | UI font family |
192
- | `fontSize` | `13px` | UI font size |
193
- | `borderRadius` | `6px` | UI border radius |
194
-
195
- See `ThemeConfig` in [src/types.ts](src/types.ts) for the full list of 25 properties.
196
-
197
- ## Internationalization (i18n)
198
-
199
- The editor ships with English defaults and a complete French translation. Pass custom labels via the `labels` prop:
200
-
201
- ```vue
202
- <script setup>
203
- import { EmailEditor, FR_LABELS } from '@lab2view/vue-email-editor'
204
- </script>
205
-
206
- <!-- French UI -->
207
- <EmailEditor :labels="FR_LABELS" />
208
-
209
- <!-- Custom labels (partial override) -->
210
- <EmailEditor :labels="{ editor_title: 'Mein Editor', undo: 'Ruckgangig' }" />
211
- ```
212
-
213
- 175+ label keys cover all UI elements: toolbar, sidebar tabs, block names, property labels, option values, and status messages. See `EditorLabels` in [src/labels.ts](src/labels.ts).
214
-
215
- ## Imperative API
216
-
217
- Access the editor programmatically via template ref:
218
-
219
- ```vue
220
- <script setup>
221
- import { ref, onMounted } from 'vue'
222
- import { EmailEditor } from '@lab2view/vue-email-editor'
223
-
224
- const editor = ref()
225
-
226
- onMounted(() => {
227
- // Export
228
- const mjml = editor.value.getMjml()
229
- const html = editor.value.getHtml()
230
- const json = editor.value.getDesignJson()
231
-
232
- // History
233
- editor.value.undo()
234
- editor.value.redo()
235
-
236
- // Selection
237
- editor.value.selectNode('node-id')
238
- const selected = editor.value.getSelectedNode()
239
- editor.value.clearSelection()
240
-
241
- // Manipulation
242
- editor.value.deleteNode('node-id')
243
- editor.value.duplicateNode('node-id')
244
- editor.value.updateNodeAttribute('node-id', 'color', '#ff0000')
245
-
246
- // Events
247
- editor.value.on('editor:change', ({ document }) => {
248
- console.log('Document changed', document)
249
- })
250
- })
251
- </script>
252
- ```
253
-
254
- ### Full API Reference
255
-
256
- | Method | Returns | Description |
257
- |--------|---------|-------------|
258
- | `getDocument()` | `EmailDocument` | Current document tree |
259
- | `setDocument(doc)` | `void` | Replace entire document |
260
- | `getMjml()` | `string` | Export as MJML string |
261
- | `getHtml()` | `string` | Export as compiled HTML |
262
- | `getDesignJson()` | `EmailDesignJson` | Export persisted format |
263
- | `undo()` | `void` | Undo last change |
264
- | `redo()` | `void` | Redo last undone change |
265
- | `canUndo()` | `boolean` | Whether undo is available |
266
- | `canRedo()` | `boolean` | Whether redo is available |
267
- | `selectNode(id)` | `void` | Select a node by ID |
268
- | `getSelectedNode()` | `EmailNode \| null` | Get selected node |
269
- | `clearSelection()` | `void` | Deselect current node |
270
- | `deleteNode(id)` | `void` | Remove a node |
271
- | `duplicateNode(id)` | `NodeId \| null` | Clone a node |
272
- | `insertBlock(block, parentId, index?)` | `NodeId \| null` | Insert a block |
273
- | `updateNodeAttribute(id, key, value)` | `void` | Update an attribute |
274
- | `loadTemplate(doc)` | `void` | Load an EmailDocument |
275
- | `on(event, handler)` | `void` | Subscribe to event |
276
- | `off(event, handler)` | `void` | Unsubscribe from event |
277
-
278
- ## Events
279
-
280
- Subscribe to editor events for real-time notifications:
281
-
282
- ```ts
283
- editor.value.on('node:selected', ({ nodeId, node }) => {
284
- console.log('Selected:', node.type)
285
- })
286
-
287
- editor.value.on('editor:change', ({ document }) => {
288
- // Auto-save
289
- saveToServer(document)
290
- })
291
- ```
292
-
293
- | Event | Payload | Trigger |
294
- |-------|---------|---------|
295
- | `editor:ready` | `{ document }` | Editor initialized |
296
- | `editor:change` | `{ document }` | Any document change |
297
- | `node:selected` | `{ nodeId, node }` | Node selected |
298
- | `node:deselected` | `{ nodeId }` | Selection cleared |
299
- | `node:deleted` | `{ nodeId }` | Node removed |
300
- | `node:moved` | `{ nodeId, fromParentId, toParentId }` | Node repositioned |
301
- | `node:duplicated` | `{ originalId, newId }` | Node cloned |
302
- | `block:dropped` | `{ blockId, parentId }` | Block added from panel |
303
- | `history:undo` | `{ canUndo, canRedo }` | Undo performed |
304
- | `history:redo` | `{ canUndo, canRedo }` | Redo performed |
305
- | `property:changed` | `{ nodeId, key, value }` | Attribute updated |
306
-
307
- ## Plugin System
308
-
309
- Extend the editor with custom blocks, property editors, toolbar actions, and sidebar panels:
310
-
311
- ```ts
312
- import type { Plugin } from '@lab2view/vue-email-editor'
313
- import { createText } from '@lab2view/vue-email-editor'
314
-
315
- const myPlugin: Plugin = (ctx) => {
316
- // Add a custom block
317
- ctx.registerBlock({
318
- id: 'custom-banner',
319
- label: 'Custom Banner',
320
- category: 'content',
321
- icon: 'Sparkles',
322
- factory: () => createText('<h1>My Custom Block</h1>', {
323
- align: 'center',
324
- 'font-size': '24px',
325
- }),
326
- })
327
-
328
- // Add a new block category
329
- ctx.registerBlockCategory({
330
- id: 'my-category',
331
- label: 'My Blocks',
332
- icon: 'Package',
333
- order: 50,
334
- })
335
-
336
- // Add a toolbar button
337
- ctx.registerToolbarAction({
338
- id: 'save',
339
- label: 'Save',
340
- icon: 'Save',
341
- handler: () => console.log('Saving...'),
342
- position: 'right',
343
- })
344
-
345
- // Listen to events
346
- ctx.on('editor:change', ({ document }) => {
347
- console.log('Auto-saving...')
348
- })
349
- }
350
- ```
351
-
352
- ```vue
353
- <EmailEditor :plugins="[myPlugin]" />
354
- ```
355
-
356
- ### Plugin Context API
357
-
358
- | Method | Description |
359
- |--------|-------------|
360
- | `registerBlock(block)` | Add a custom block to the blocks panel |
361
- | `registerBlockCategory(category)` | Define a new block category |
362
- | `registerPropertyEditor(type, component)` | Override a property editor component |
363
- | `registerToolbarAction(action)` | Add a button to the toolbar |
364
- | `registerSidebarPanel(panel)` | Add a custom sidebar tab/panel |
365
- | `on(event, handler)` | Subscribe to editor events |
366
- | `off(event, handler)` | Unsubscribe from events |
367
- | `labels` | Reactive reference to current labels |
368
-
369
- ## Exports
370
-
371
- The package provides 35+ exports for advanced usage:
372
-
373
- ```ts
374
- // Component
375
- import { EmailEditor } from '@lab2view/vue-email-editor'
376
-
377
- // Labels & i18n
378
- import { DEFAULT_LABELS, FR_LABELS } from '@lab2view/vue-email-editor'
379
-
380
- // Types
381
- import type {
382
- EmailDocument, EmailNode, EmailDesignJson, EmailEditorAPI,
383
- Plugin, PluginContext, ThemeConfig, EditorEventMap,
384
- BlockDefinition, PropertyDefinition,
385
- } from '@lab2view/vue-email-editor'
386
-
387
- // Serialization
388
- import { compileMjml, documentToMjml, mjmlToDocument } from '@lab2view/vue-email-editor'
389
-
390
- // Node factories
391
- import {
392
- createDefaultDocument, createSection, createColumn,
393
- createText, createImage, createButton, createDivider,
394
- createSpacer, createSocial, createHero, createWrapper,
395
- } from '@lab2view/vue-email-editor'
396
-
397
- // Tree utilities
398
- import { findNode, findParent, removeNode, moveNode, cloneSubtree } from '@lab2view/vue-email-editor'
399
-
400
- // Constants
401
- import {
402
- DEFAULT_THEME, STATIC_BLOCKS,
403
- CONTENT_NODE_TYPES, CONTAINER_NODE_TYPES, SELF_CLOSING_NODE_TYPES,
404
- } from '@lab2view/vue-email-editor'
405
-
406
- // ESP Export Presets
407
- import {
408
- exportForEsp, exportForMailchimp, exportForSendGrid,
409
- exportForBrevo, exportForAwsSes, exportForPostmark, exportForResend,
410
- ESP_PRESETS, MAILCHIMP_PRESET, SENDGRID_PRESET,
411
- } from '@lab2view/vue-email-editor'
412
- ```
413
-
414
- ## Keyboard Shortcuts
415
-
416
- | Shortcut | Action |
417
- |----------|--------|
418
- | `Ctrl+Z` | Undo |
419
- | `Ctrl+Shift+Z` | Redo |
420
- | `Ctrl+D` | Duplicate selected node |
421
- | `Delete` / `Backspace` | Delete selected node |
422
- | `Escape` | Deselect / close inline editor |
50
+ That's it. The editor renders a full drag-and-drop email builder with live preview, undo/redo, and HTML export.
423
51
 
424
- ## Props
52
+ ## Highlights
425
53
 
426
- | Prop | Type | Default | Description |
427
- |------|------|---------|-------------|
428
- | `modelValue` | `string` | `''` | MJML content (v-model) |
429
- | `designJson` | `Record<string, unknown>` | | Persisted design JSON |
430
- | `variables` | `string[]` | `[]` | Available merge variables |
431
- | `labels` | `Partial<EditorLabels>` | `DEFAULT_LABELS` | i18n label overrides |
432
- | `label` | `string` | | Form field label |
433
- | `required` | `boolean` | `false` | Form validation flag |
434
- | `theme` | `Partial<ThemeConfig>` | `DEFAULT_THEME` | Visual customization |
435
- | `plugins` | `Plugin[]` | `[]` | Editor extensions |
436
- | `mergeTags` | `MergeTag[]` | | Dynamic variable tags |
437
- | `aiProvider` | `AiProvider` | | AI template generation and inline text callbacks |
438
- | `onImageUpload` | `(file: File) => Promise<{ url }>` | — | Image upload handler |
439
- | `onBrowseAssets` | `() => Promise<string \| null>` | — | Asset browser handler |
54
+ - **43 blocks** layouts, content, and 30 ready-made composites (hero, pricing, testimonial, FAQ, etc.)
55
+ - **Inline editing** — double-click any text to edit with TipTap (bold, italic, links, colors)
56
+ - **AI generation** describe an email in plain language, get a production-ready template (BYOAI)
57
+ - **Merge tags** insert dynamic variables (`{{first_name}}`) with visual chips
58
+ - **Conditional content** show/hide sections based on merge tag values
59
+ - **22 starter templates** welcome, newsletter, e-commerce, abandoned cart, and more
60
+ - **ESP export** pre-formatted HTML for Mailchimp, SendGrid, Brevo, AWS SES, Postmark, Resend
61
+ - **Dark mode preview** simulate email client dark mode in the canvas
62
+ - **Plugin system** add custom blocks, toolbar actions, sidebar panels
63
+ - **i18n** English + French included, 175+ label keys for full translation
64
+ - **Theming** customize colors, fonts, border radius via `theme` prop
65
+ - **Undo/Redo**full history with `Ctrl+Z` / `Ctrl+Shift+Z`
66
+ - **Imperative API** `getMjml()`, `getHtml()`, `selectNode()`, `deleteNode()`, and more via ref
440
67
 
441
- ## Merge Tags
68
+ ## AI Template Generation
442
69
 
443
- Insert dynamic content with merge tag variables:
70
+ Plug in any LLM OpenAI, Anthropic, Gemini, or your own backend:
444
71
 
445
72
  ```vue
446
73
  <EmailEditor
447
- :merge-tags="[
448
- { name: 'First Name', value: '{{first_name}}', category: 'Contact' },
449
- { name: 'Company', value: '{{company}}', category: 'Contact' },
450
- { name: 'Unsubscribe', value: '{{unsubscribe_url}}', category: 'Links' },
451
- ]"
74
+ v-model="mjml"
75
+ :ai-provider="{
76
+ generateText: async (prompt, ctx) => { /* your API call */ },
77
+ generateTemplate: async (messages, systemPrompt) => { /* your API call */ },
78
+ }"
452
79
  />
453
80
  ```
454
81
 
455
- ## AI Template Generation
82
+ The editor handles JSON parsing, repair, and retry automatically.
456
83
 
457
- Generate complete email templates from natural language. Plug in any LLM — OpenAI, Anthropic, Google Gemini, or your own.
84
+ ## Theming & i18n
458
85
 
459
86
  ```vue
460
- <script setup lang="ts">
461
- import { ref } from 'vue'
462
- import { EmailEditor } from '@lab2view/vue-email-editor'
463
- import type { AiProvider } from '@lab2view/vue-email-editor'
464
-
465
- const mjml = ref('')
466
-
467
- const aiProvider: AiProvider = {
468
- generateText: async (prompt, context) => {
469
- const res = await fetch('/api/ai/text', {
470
- method: 'POST',
471
- headers: { 'Content-Type': 'application/json' },
472
- body: JSON.stringify({ prompt, context }),
473
- })
474
- return (await res.json()).text
475
- },
476
- generateTemplate: async (messages, systemPrompt) => {
477
- const res = await fetch('/api/ai/chat', {
478
- method: 'POST',
479
- headers: { 'Content-Type': 'application/json' },
480
- body: JSON.stringify({ messages, systemPrompt }),
481
- })
482
- return (await res.json()).content
483
- },
484
- }
485
- </script>
486
-
487
- <template>
488
- <EmailEditor v-model="mjml" :ai-provider="aiProvider" />
489
- </template>
490
- ```
491
-
492
- Your backend just forwards to any LLM and returns the raw response. The editor handles JSON parsing, repair, and retry automatically.
493
-
494
- **Want to test without an API key?** Use the [mock provider](https://lab2view.github.io/vue-email-editor/guide/ai#mock-provider-for-testing) — a static `AiProvider` that returns dummy responses for development and testing.
495
-
496
- **[Full AI integration guide →](https://lab2view.github.io/vue-email-editor/guide/ai)** — complete backend examples for OpenAI, Anthropic, and Google Gemini, streaming setup, error handling, and more.
497
-
498
- ## ESP Export
499
-
500
- Export HTML pre-formatted for your email service provider:
501
-
502
- ```ts
503
- import {
504
- exportForMailchimp,
505
- exportForSendGrid,
506
- exportForBrevo,
507
- exportForAwsSes,
508
- exportForEsp,
509
- } from '@lab2view/vue-email-editor'
510
-
511
- // Get the document from the editor
512
- const document = editorRef.value.getDocument()
513
-
514
- // Export for Mailchimp (transforms {{first_name}} → *|FNAME|*)
515
- const { html, mjml } = await exportForMailchimp(document)
516
-
517
- // Export for SendGrid
518
- const result = await exportForSendGrid(document)
519
-
520
- // Export with custom merge tag mappings
521
- const custom = await exportForEsp(document, 'brevo', {
522
- mergeTags: { plan_name: '{{ contact.PLAN }}' },
523
- })
87
+ <EmailEditor
88
+ :theme="{ primaryColor: '#7C3AED', borderRadius: '8px' }"
89
+ :labels="FR_LABELS"
90
+ />
524
91
  ```
525
92
 
526
- ## Development
527
-
528
- ```bash
529
- # Type check
530
- npm run typecheck
531
-
532
- # Run tests (147 tests)
533
- npm test
534
-
535
- # Build library
536
- npm run build
537
- ```
93
+ ## Documentation
538
94
 
539
- ## Tech Stack
95
+ Full API reference, plugin guide, AI integration examples, and more:
540
96
 
541
- - **[Vue 3](https://vuejs.org)** — Reactive component framework
542
- - **[MJML](https://mjml.io)** — Email markup language
543
- - **[TipTap](https://tiptap.dev)** — Rich text editing
544
- - **[Lucide](https://lucide.dev)** — Icon system (400+ icons)
545
- - **[VueUse](https://vueuse.org)** — Vue composable utilities
546
- - **[TypeScript](https://www.typescriptlang.org)** — Full type safety
547
- - **[Vite](https://vite.dev)** — Build tooling
548
- - **[Vitest](https://vitest.dev)** — Testing framework
97
+ **[Documentation ](https://lab2view.github.io/vue-email-editor/)**
549
98
 
550
99
  ## License
551
100
 
@@ -1,5 +1,5 @@
1
1
  import { ref as y, defineComponent as te, inject as B, computed as K, watch as J, openBlock as o, createElementBlock as r, createElementVNode as i, normalizeClass as Y, toDisplayString as _, unref as l, createCommentVNode as k, createVNode as v, createTextVNode as A, withDirectives as V, Fragment as I, renderList as R, vShow as ae, vModelText as se, nextTick as ne } from "vue";
2
- import { p as W, A as H, c as ie, d as le, e as ce, a as oe, D as re, E as ue, b as _e, _ as p } from "./index-D2Vk6Cbv.js";
2
+ import { p as W, A as H, c as ie, d as le, e as ce, a as oe, D as re, E as ue, b as _e, _ as p } from "./index-Rt0aY248.js";
3
3
  function de(w) {
4
4
  const c = w.trim();
5
5
  return !!(c.startsWith("{") || /```(?:json)?\s*\n?\s*\{/.test(c) || c.includes('"version"') && c.includes('"body"') && c.includes('"mj-body"') || c.includes('"headAttributes"') && c.includes('"mj-body"'));
@@ -462,4 +462,4 @@ const me = { class: "ebb-ai-chat" }, be = {
462
462
  export {
463
463
  Xe as default
464
464
  };
465
- //# sourceMappingURL=AiChatPanel-CKHjYcuP.js.map
465
+ //# sourceMappingURL=AiChatPanel-DBC4q8wx.js.map
@@ -1,6 +1,6 @@
1
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
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-D2Vk6Cbv.js";
3
+ import { E as S, m as b, d as B } from "./index-Rt0aY248.js";
4
4
  const A = /* @__PURE__ */ u({
5
5
  __name: "CodeEditor",
6
6
  setup(T) {
@@ -66,4 +66,4 @@ const A = /* @__PURE__ */ u({
66
66
  export {
67
67
  A as default
68
68
  };
69
- //# sourceMappingURL=CodeEditor-4dfVt7cm.js.map
69
+ //# sourceMappingURL=CodeEditor-BfGD5J7z.js.map
@@ -1,6 +1,6 @@
1
1
  import { defineComponent as F, inject as S, ref as A, computed as R, openBlock as c, createElementBlock as g, withModifiers as $, createElementVNode as a, normalizeClass as p, createVNode as r, createCommentVNode as h, Fragment as w, renderList as N, toDisplayString as y, createTextVNode as M, withDirectives as Y, withKeys as j, vModelText as G, markRaw as W, watch as q, nextTick as J, onBeforeUnmount as Q, normalizeStyle as V, unref as E, createBlock as X } from "vue";
2
2
  import { T as Z, i as ee, a as te, c as ne, d as ie, e as ae, u as le, E as re } from "./tiptap-gdUcLDLI.js";
3
- import { a as oe, D as se, b as O, _ as o, M as be } from "./index-D2Vk6Cbv.js";
3
+ import { a as oe, D as se, b as O, _ as o, M as be } from "./index-Rt0aY248.js";
4
4
  const ue = ["aria-pressed", "title", "aria-label"], de = ["aria-pressed", "title", "aria-label"], ce = ["aria-pressed", "title", "aria-label"], me = ["aria-pressed", "title", "aria-label"], ge = ["title", "aria-label"], _e = ["title", "aria-label"], ve = ["aria-pressed", "title", "aria-label"], fe = ["aria-pressed", "title", "aria-label"], pe = ["aria-pressed", "title", "aria-label"], ke = ["title"], xe = ["aria-label"], he = { class: "ebb-inline-toolbar__merge-wrap" }, ye = ["title", "aria-label", "aria-expanded"], Te = {
5
5
  key: 0,
6
6
  class: "ebb-merge-menu__category"
@@ -488,4 +488,4 @@ Text: ` + e), u && t.editor) {
488
488
  export {
489
489
  Re as default
490
490
  };
491
- //# sourceMappingURL=InlineTextEditor-DvzsFt8K.js.map
491
+ //# sourceMappingURL=InlineTextEditor-Dm2jkEsL.js.map
@@ -1,4 +1,4 @@
1
- import { f as s, A as r, B as E, C as o, g as t, D as S, h as T, i as c, j as m, F as p, k as _, M as i, P, R, S as n, l as A, n as N, o as d, c as l, q as D, e as F, r as L, s as x, t as M, u as O, v as u, w as C, x as I, y as B, z as f, G as v, H as g, d as j, I as G, J as H, K as w, L as K, N as U, O as W, Q as Y, T as b, U as h, V as k, m as y, W as J, p as V, X as q } from "./index-D2Vk6Cbv.js";
1
+ import { f as s, A as r, B as E, C as o, g as t, D as S, h as T, i as c, j as m, F as p, k as _, M as i, P, R, S as n, l as A, n as N, o as d, c as l, q as D, e as F, r as L, s as x, t as M, u as O, v as u, w as C, x as I, y as B, z as f, G as v, H as g, d as j, I as G, J as H, K as w, L as K, N as U, O as W, Q as Y, T as b, U as h, V as k, m as y, W as J, p as V, X as q } from "./index-Rt0aY248.js";
2
2
  export {
3
3
  s as AWS_SES_PRESET,
4
4
  r as AiParseError,