dragble-vue-editor 1.0.3 → 1.0.5

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 (2) hide show
  1. package/README.md +538 -54
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -11,9 +11,9 @@
11
11
 
12
12
  # dragble-vue-editor
13
13
 
14
- AI-powered Vue 3 component for building **email templates** with drag-and-drop. Embed a full-featured **AI-powered email editor** into your Vue app create responsive HTML emails, newsletters, transactional email templates, and email marketing campaigns visually without writing code.
14
+ The **fully AI-powered** Vue 3 editor for **email templates** and **landing pages**. Your end-users design visually with drag-and-drop — or describe what they want and watch AI agents build it live on the canvas. Powered by the built-in **Model Context Protocol (MCP)** server, connect [Claude Code](https://claude.com/code), [OpenCode](https://opencode.ai), [Codex](https://github.com/openai/codex), [Cursor](https://cursor.com), or your own AI backend directly to the editor. Structured tool calls mean guaranteed-valid output no prompt engineering, no JSON hallucination, no broken layouts.
15
15
 
16
- [Dragble](https://dragble.com) is a modern **AI-powered email builder** and **email template editor** that lets your users design professional emails with a visual drag-and-drop interface.
16
+ [Dragble](https://dragble.com) brings two design experiences together in one Vue component: a polished visual editor for designers and a conversational AI surface for everyone else backed by structured tool calls that produce guaranteed-valid HTML emails and landing pages every time.
17
17
 
18
18
  [Website](https://dragble.com) | [Documentation](https://docs.dragble.com) | [Dashboard](https://developers.dragble.com)
19
19
 
@@ -24,6 +24,7 @@ AI-powered Vue 3 component for building **email templates** with drag-and-drop.
24
24
  ## Features
25
25
 
26
26
  - Drag-and-drop **email template builder** with 20+ content blocks
27
+ - **Fully AI-powered via MCP** — connect AI agents (Claude Code, OpenCode, Codex, Cursor) or your own AI backend to build designs live on the canvas. Structured tool calls mean guaranteed-valid output — no prompt engineering, no JSON hallucination
27
28
  - Responsive **HTML email** output compatible with all major email clients
28
29
  - **Newsletter editor** with merge tags, dynamic content, and display conditions
29
30
  - Visual **email designer** — no HTML/CSS knowledge required for end users
@@ -88,6 +89,301 @@ const onChange = async (data: { design: unknown; type: string }) => {
88
89
  </script>
89
90
  ```
90
91
 
92
+ ## Complete Example
93
+
94
+ ```vue
95
+ <template>
96
+ <div class="advanced-email-builder">
97
+ <div class="toolbar">
98
+ <button type="button" @click="editorRef?.undo()">Undo</button>
99
+ <button type="button" @click="editorRef?.redo()">Redo</button>
100
+ <button type="button" @click="editorRef?.showPreview('desktop')">
101
+ Preview
102
+ </button>
103
+ <button type="button" @click="handleExportHtml">Export HTML</button>
104
+ <button type="button" @click="handleExportImage">Export Image</button>
105
+ <span v-if="isDirty" class="dirty-indicator">Unsaved changes</span>
106
+ </div>
107
+
108
+ <DragbleEditor
109
+ ref="editorRef"
110
+ editor-key="your-editor-key"
111
+ editor-mode="email"
112
+ height="100%"
113
+ design-mode="live"
114
+ :options="editorOptions"
115
+ @ready="handleReady"
116
+ @change="handleChange"
117
+ @error="handleError"
118
+ />
119
+ </div>
120
+ </template>
121
+
122
+ <script setup lang="ts">
123
+ import { ref } from "vue";
124
+ import {
125
+ DragbleEditor,
126
+ type DesignJson,
127
+ type DragbleSDK,
128
+ type EditorOptions,
129
+ } from "dragble-vue-editor";
130
+
131
+ const editorRef = ref<InstanceType<typeof DragbleEditor> | null>(null);
132
+ const isDirty = ref(false);
133
+
134
+ const editorOptions: EditorOptions = {
135
+ appearance: { theme: "light" },
136
+ features: {
137
+ preview: true,
138
+ undoRedo: true,
139
+ imageEditor: true,
140
+ },
141
+ };
142
+
143
+ const handleReady = (editor: DragbleSDK) => {
144
+ // Set merge tags (must pass a MergeTagsConfig object)
145
+ editor.setMergeTags({
146
+ customMergeTags: [
147
+ { name: "First Name", value: "{{first_name}}" },
148
+ { name: "Last Name", value: "{{last_name}}" },
149
+ { name: "Company", value: "{{company}}" },
150
+ ],
151
+ excludeDefaults: false,
152
+ sort: true,
153
+ });
154
+
155
+ // Set custom fonts
156
+ editor.setFonts({
157
+ showDefaultFonts: true,
158
+ customFonts: [{ label: "Brand Font", value: "BrandFont, sans-serif" }],
159
+ });
160
+
161
+ // Load saved design if available
162
+ const savedDesign = localStorage.getItem("email-design");
163
+ if (savedDesign) {
164
+ editor.loadDesign(JSON.parse(savedDesign));
165
+ }
166
+ };
167
+
168
+ const handleChange = (data: { design: DesignJson; type: string }) => {
169
+ isDirty.value = true;
170
+ localStorage.setItem("email-design", JSON.stringify(data.design));
171
+ };
172
+
173
+ const handleExportHtml = async () => {
174
+ if (!editorRef.value) return;
175
+
176
+ const html = await editorRef.value.exportHtml();
177
+ const blob = new Blob([html], { type: "text/html" });
178
+ const url = URL.createObjectURL(blob);
179
+ const a = document.createElement("a");
180
+ a.href = url;
181
+ a.download = "email.html";
182
+ a.click();
183
+ URL.revokeObjectURL(url);
184
+ };
185
+
186
+ const handleExportImage = async () => {
187
+ if (!editorRef.value) return;
188
+
189
+ const data = await editorRef.value.exportImage();
190
+ window.open(data.url, "_blank");
191
+ };
192
+
193
+ const handleError = (error: Error) => {
194
+ console.error(error.message);
195
+ };
196
+ </script>
197
+
198
+ <style scoped>
199
+ .advanced-email-builder {
200
+ height: 100vh;
201
+ display: flex;
202
+ flex-direction: column;
203
+ }
204
+
205
+ .toolbar {
206
+ padding: 12px;
207
+ border-bottom: 1px solid #ddd;
208
+ display: flex;
209
+ gap: 8px;
210
+ align-items: center;
211
+ }
212
+
213
+ .dirty-indicator {
214
+ color: orange;
215
+ }
216
+ </style>
217
+ ```
218
+
219
+ ## MCP — AI Integration
220
+
221
+ Connect AI agents (Claude Code, OpenCode, Codex, Cursor, or your own AI backend) to the editor through the [Model Context Protocol](https://modelcontextprotocol.io). The AI calls structured tools — `add_row`, `add_heading`, `update_button`, `export_html` — that mutate design state live on the canvas. No prompt engineering, no JSON hallucination, no broken output.
222
+
223
+ ### Enabling MCP
224
+
225
+ MCP is off by default. Set `features: { mcp: true }` to opt in:
226
+
227
+ ```vue
228
+ <DragbleEditor
229
+ ref="editorRef"
230
+ editor-key="db_pxl81cxn92wignwx"
231
+ :options="{ features: { mcp: true } }"
232
+ />
233
+ ```
234
+
235
+ MCP also requires a **Starter plan or higher**. Both conditions must be true — plan allows it AND SDK enables it.
236
+
237
+ ### Quick example — your backend controls the AI
238
+
239
+ ```vue
240
+ <template>
241
+ <button @click="handleConnectAI">Connect AI</button>
242
+ <DragbleEditor
243
+ ref="editorRef"
244
+ editor-key="db_pxl81cxn92wignwx"
245
+ :options="{ features: { mcp: true } }"
246
+ />
247
+ </template>
248
+
249
+ <script setup lang="ts">
250
+ import { ref } from "vue";
251
+ import { DragbleEditor } from "dragble-vue-editor";
252
+
253
+ const editorRef = ref<InstanceType<typeof DragbleEditor> | null>(null);
254
+
255
+ const handleConnectAI = async () => {
256
+ // The id is YOUR identifier — derive it from your own database/session
257
+ // so the same user editing the same document always gets the same MCP
258
+ // session. Example: if your logged-in user is "alice123" and they're
259
+ // editing document "campaign-summer-2026", build an id like this:
260
+ //
261
+ // const id = "alice123-campaign-summer-2026";
262
+ //
263
+ // Format rules: 8-128 chars, only letters/digits/hyphens/underscores.
264
+ const userIdFromAuth = "alice123"; // from your auth/session
265
+ const docIdFromRoute = "campaign-summer"; // from your URL or DB row
266
+ const id = `${userIdFromAuth}-${docIdFromRoute}`;
267
+ const { sessionId } = await editorRef.value!.editor!.connectMCP({ id });
268
+ // Pass sessionId to your backend — it calls MCP tools with your mcp_key
269
+ };
270
+ </script>
271
+ ```
272
+
273
+ ### Quick example — end-user pairs their own AI client
274
+
275
+ ```ts
276
+ const handleLetUserPair = async () => {
277
+ const editor = editorRef.value!.editor!;
278
+ // Same id you'd use anywhere else for this user+document combination.
279
+ // 8-128 chars, only letters/digits/hyphens/underscores.
280
+ const id = "alice123-campaign-summer-2026";
281
+ await editor.connectMCP({ id });
282
+
283
+ // Explicitly generate a pairing code (not auto-generated)
284
+ const { code, expiresAt } = await editor.getPairingCode();
285
+ alert(`Paste this into Claude Code: ${code}`);
286
+ };
287
+ ```
288
+
289
+ ### One controller per session
290
+
291
+ Each session can be controlled by **either** your backend **or** an end-user's AI client (Claude Code, OpenCode), never both at the same time:
292
+
293
+ - If your backend makes the first tool call → session is locked to **backend**. Pairing codes are rejected.
294
+ - If a user pairs via pairing code first → session is locked to **paired client**. Backend tool calls are rejected.
295
+
296
+ This prevents two AI controllers from conflicting on the same design.
297
+
298
+ ### How it works
299
+
300
+ 1. **Enable MCP** in the SDK config: `features: { mcp: true }`.
301
+ 2. **Generate an MCP key** in the Dragble dashboard: Project → MCP Key → Generate. Store it in your backend env vars — never in browser code.
302
+ 3. **Call `editor.connectMCP({ id })`** where `id` is a stable identifier you control (see below).
303
+ 4. **Choose your AI path**: either your backend calls MCP tools directly (using the mcp_key), or you generate a pairing code for the end-user to connect their own AI client.
304
+ 5. **Mutations stream live** onto the editor canvas as the AI works.
305
+
306
+ ### The `id` parameter — why it matters
307
+
308
+ The `id` you pass to `connectMCP()` is a **Bring Your Own ID (BYOI)** that maps to your domain entities. It is NOT a random token — it is how Dragble identifies the session across browser refreshes, server restarts, and device switches.
309
+
310
+ **Rules:**
311
+
312
+ - 8–128 characters long
313
+ - Only letters, numbers, hyphens, and underscores (`a-z A-Z 0-9 - _`)
314
+ - Must be deterministic — the same user editing the same document should always produce the same `id`
315
+
316
+ **Why these rules?**
317
+
318
+ - The `id` is used in database lookups, URL paths, and storage keys — special characters or extreme lengths would break routing
319
+ - Same `id` = resume the same session. Random UUIDs mean every page refresh creates a new session and loses AI context
320
+ - Short IDs (< 8 chars) are too easy to guess, long IDs (> 128 chars) waste storage
321
+
322
+ ```ts
323
+ // Recommended: derive from your domain — concrete examples
324
+ editor.connectMCP({ id: "alice123-campaign-summer-2026" }); // user + doc
325
+ editor.connectMCP({ id: "workspace_acme_template_welcome" }); // workspace + template
326
+ editor.connectMCP({ id: "org-uber-eats-promo-q4-2026" }); // org + campaign
327
+ editor.connectMCP({ id: "tenant_42_invoice_template_v3" }); // tenant + entity
328
+
329
+ // Valid but NOT recommended — random IDs break session continuity
330
+ // (every page refresh creates a brand new session, AI loses context)
331
+ editor.connectMCP({ id: crypto.randomUUID() });
332
+ ```
333
+
334
+ ### Storage modes (compliance)
335
+
336
+ Choose how much of the session lives on Dragble's servers:
337
+
338
+ | Mode | Persistence | Use case |
339
+ | ---------------- | ------------------------- | ------------------------------------------------------- |
340
+ | `full` (default) | Metadata + design content | Standard SaaS; survives refresh, restart, device switch |
341
+ | `metadata-only` | Metadata only | Audit logs without storing customer content |
342
+ | `memory-only` | None — RAM only | HIPAA / SOC2 / strict data residency |
343
+
344
+ ```ts
345
+ editor.connectMCP({
346
+ id: "user-42-doc-99",
347
+ storage: "full", // default — best UX, refresh + cross-device resume
348
+ // "metadata-only" // audit metadata only, no design content persisted
349
+ // "memory-only" // nothing persisted (HIPAA / SOC2 / data residency)
350
+ });
351
+ ```
352
+
353
+ ### Disconnecting
354
+
355
+ `disconnectMCP()` permanently destroys the session — the database record is deleted and the session cannot be reopened:
356
+
357
+ ```ts
358
+ const { destroyed } = await editor.disconnectMCP();
359
+ ```
360
+
361
+ Your backend can also force-destroy a session server-side (e.g., when a user's subscription ends):
362
+
363
+ ```bash
364
+ curl -X DELETE https://mcp.dragble.com/sessions/user-42-doc-99 \
365
+ -H "X-API-Key: db_mcp_your_key_here"
366
+ ```
367
+
368
+ Idle sessions are reaped after 2 hours of inactivity. Active sessions never expire — each tool call resets the timer.
369
+
370
+ ### MCP method reference
371
+
372
+ | Method | Returns |
373
+ | -------------------------------------------------- | ----------------------------------------------------------------------- |
374
+ | `editor.connectMCP({ id, storage?, editorMode? })` | `{ sessionId, storageMode?, resumed? }` |
375
+ | `editor.disconnectMCP()` | `{ destroyed }` — permanently deletes session |
376
+ | `editor.getPairingCode()` | `{ code, expiresAt }` — generate a pairing code for end-user AI clients |
377
+ | `editor.endPairing()` | `{ revoked }` — invalidate the active pairing code |
378
+ | `editor.getMCPStatus()` | `{ paired: true, sessionId } \| { paired: false, reason? }` |
379
+ | `editor.onAIToolFired(cb)` | unsubscribe fn — fires when AI calls any tool |
380
+
381
+ ### Full documentation
382
+
383
+ - [MCP Overview](https://docs.dragble.com/mcp-server/overview)
384
+ - [Credentials & Security](https://docs.dragble.com/mcp-server/credentials)
385
+ - [AI Client Setup (OpenCode, Claude Code, Codex, etc.)](https://docs.dragble.com/mcp-server/ai-client-setup)
386
+
91
387
  ## Props
92
388
 
93
389
  | Prop | Type | Default | Description |
@@ -126,58 +422,6 @@ const onChange = async (data: { design: unknown; type: string }) => {
126
422
  | `user` | `UserInfo` | `undefined` | User info |
127
423
  | `designMode` | `"edit" \| "live"` | `undefined` | Template permissions |
128
424
 
129
- ## Events
130
-
131
- | Event | Description |
132
- | --------- | ------------------------------- |
133
- | `ready` | Editor is initialized and ready |
134
- | `load` | Design has been loaded |
135
- | `change` | Design has changed |
136
- | `error` | An error occurred |
137
- | `comment` | Comment event |
138
-
139
- ## Exposed Methods
140
-
141
- All SDK methods are accessible via a template ref. Key methods:
142
-
143
- | Method | Returns | Description |
144
- | ---------------------------------- | ---------------------- | ---------------------------------------- |
145
- | `loadDesign(design, options?)` | `void` | Load a design |
146
- | `loadBlank(options?)` | `void` | Load a blank design |
147
- | `getDesign()` | `Promise` | Get current design JSON |
148
- | `exportHtml(options?)` | `Promise<string>` | Export as HTML |
149
- | `exportJson()` | `Promise<DesignJson>` | Export design JSON |
150
- | `exportPlainText()` | `Promise<string>` | Export as plain text |
151
- | `exportImage(options?)` | `Promise` | Export as image |
152
- | `exportPdf(options?)` | `Promise` | Export as PDF |
153
- | `exportZip(options?)` | `Promise` | Export as ZIP |
154
- | `setMergeTags(config)` | `void` | Set merge tags config |
155
- | `getMergeTags()` | `Promise` | Get merge tags |
156
- | `setSpecialLinks(config)` | `void` | Set special links |
157
- | `setModules(modules)` | `void` | Set custom modules |
158
- | `setFonts(config)` | `void` | Set fonts config |
159
- | `setBodyValues(values)` | `void` | Set body values |
160
- | `setToolsConfig(config)` | `void` | Set tools config |
161
- | `setAppearance(config)` | `void` | Set appearance |
162
- | `setEditorMode(mode)` | `void` | Set editor mode |
163
- | `setLocale(locale, translations?)` | `void` | Set locale |
164
- | `setTextDirection(direction)` | `void` | Set text direction |
165
- | `setLanguage(language)` | `void` | Set language |
166
- | `setDisplayConditions(config)` | `void` | Set display conditions |
167
- | `setOptions(options)` | `void` | Set additional options |
168
- | `showPreview(device?)` | `void` | Show design preview |
169
- | `hidePreview()` | `void` | Hide preview |
170
- | `undo()` | `void` | Undo last action |
171
- | `redo()` | `void` | Redo last action |
172
- | `canUndo()` | `Promise<boolean>` | Check if undo available |
173
- | `canRedo()` | `Promise<boolean>` | Check if redo available |
174
- | `save()` | `void` | Trigger save |
175
- | `audit(options?)` | `Promise<AuditResult>` | Run design audit |
176
- | `registerTool(config)` | `Promise` | Register a custom tool |
177
- | `unregisterTool(id)` | `Promise` | Unregister a tool |
178
- | `addEventListener(event, cb)` | `() => void` | Subscribe to event (returns unsubscribe) |
179
- | `removeEventListener(event, cb)` | `void` | Unsubscribe from event |
180
-
181
425
  ## Composable
182
426
 
183
427
  For more control over the editor lifecycle, use the `useDragbleEditor` composable:
@@ -213,6 +457,246 @@ const exportHtml = async () => {
213
457
  | `isReady` | `Ref<boolean>` | Whether the editor is initialized |
214
458
  | `containerId` | `string` | DOM element ID to bind the editor to |
215
459
 
460
+ ## SDK Methods Reference
461
+
462
+ Access the SDK through the Vue component template ref (`editorRef.value`) or through the `editor` ref returned by `useDragbleEditor()`. The component exposes SDK methods directly, so calls such as `editorRef.value!.exportHtml()` work without reaching into framework internals. All export and getter methods return Promises.
463
+
464
+ ```ts
465
+ const html = await editorRef.value!.exportHtml();
466
+ const design = await editor.value!.exportJson();
467
+ ```
468
+
469
+ ### Design
470
+
471
+ ```ts
472
+ editorRef.value!.loadDesign(design, options?); // void
473
+ const result = await editorRef.value!.loadDesignAsync(design, options?);
474
+ // => { success, validRowsCount, invalidRowsCount, errors? }
475
+ editorRef.value!.loadBlank(options?); // void
476
+ const { html, json } = await editorRef.value!.getDesign(); // Promise
477
+ ```
478
+
479
+ ### Export
480
+
481
+ All export methods are **Promise-based**. There are no callback overloads.
482
+
483
+ ```ts
484
+ const html = await editorRef.value!.exportHtml(options?); // Promise<string>
485
+ const json = await editorRef.value!.exportJson(); // Promise<DesignJson>
486
+ const text = await editorRef.value!.exportPlainText(); // Promise<string>
487
+ const imageData = await editorRef.value!.exportImage(options?); // Promise<ExportImageData>
488
+ const pdfData = await editorRef.value!.exportPdf(options?); // Promise<ExportPdfData>
489
+ const zipData = await editorRef.value!.exportZip(options?); // Promise<ExportZipData>
490
+ const values = await editorRef.value!.getPopupValues(); // Promise<PopupValues | null>
491
+ ```
492
+
493
+ ### Merge Tags
494
+
495
+ `setMergeTags` accepts a `MergeTagsConfig` object, not a plain array.
496
+
497
+ ```ts
498
+ editorRef.value!.setMergeTags({
499
+ customMergeTags: [
500
+ { name: "First Name", value: "{{first_name}}" },
501
+ { name: "Company", value: "{{company}}" },
502
+ ],
503
+ excludeDefaults: false,
504
+ sort: true,
505
+ });
506
+ const tags = await editorRef.value!.getMergeTags(); // Promise<(MergeTag | MergeTagGroup)[]>
507
+ ```
508
+
509
+ ### Special Links
510
+
511
+ `setSpecialLinks` accepts a `SpecialLinksConfig` object.
512
+
513
+ ```ts
514
+ editorRef.value!.setSpecialLinks({
515
+ customSpecialLinks: [{ name: "Unsubscribe", href: "{{unsubscribe_url}}" }],
516
+ excludeDefaults: false,
517
+ });
518
+ const links = await editorRef.value!.getSpecialLinks(); // Promise<(SpecialLink | SpecialLinkGroup)[]>
519
+ ```
520
+
521
+ ### Modules
522
+
523
+ ```ts
524
+ editorRef.value!.setModules(modules); // void
525
+ editorRef.value!.setModulesLoading(loading); // void
526
+ const modules = await editorRef.value!.getModules(); // Promise<Module[]>
527
+ ```
528
+
529
+ ### Fonts
530
+
531
+ ```ts
532
+ editorRef.value!.setFonts(config); // void
533
+ const fonts = await editorRef.value!.getFonts(); // Promise<FontsConfig>
534
+ ```
535
+
536
+ ### Body Values
537
+
538
+ ```ts
539
+ editorRef.value!.setBodyValues({
540
+ backgroundColor: "#f5f5f5",
541
+ contentWidth: "600px",
542
+ });
543
+ const values = await editorRef.value!.getBodyValues(); // Promise<SetBodyValuesOptions>
544
+ ```
545
+
546
+ ### Editor Configuration
547
+
548
+ ```ts
549
+ editorRef.value!.setOptions(options); // void — Partial<EditorOptions>
550
+ editorRef.value!.setToolsConfig(toolsConfig); // void
551
+ editorRef.value!.setEditorMode(mode); // void
552
+ editorRef.value!.setEditorConfig(config); // void
553
+ const config = await editorRef.value!.getEditorConfig(); // Promise<EditorBehaviorConfig>
554
+ ```
555
+
556
+ ### Locale, Language & Text Direction
557
+
558
+ ```ts
559
+ editorRef.value!.setLocale(locale, translations?); // void
560
+ editorRef.value!.setLanguage(language); // void
561
+ const lang = await editorRef.value!.getLanguage(); // Promise<Language | null>
562
+ editorRef.value!.setTextDirection(direction); // void — 'ltr' | 'rtl'
563
+ const dir = await editorRef.value!.getTextDirection(); // Promise<TextDirection>
564
+ ```
565
+
566
+ ### Appearance
567
+
568
+ ```ts
569
+ editorRef.value!.setAppearance(appearance); // void
570
+ ```
571
+
572
+ ### Undo / Redo / Save
573
+
574
+ ```ts
575
+ editorRef.value!.undo(); // void
576
+ editorRef.value!.redo(); // void
577
+ const canUndo = await editorRef.value!.canUndo(); // Promise<boolean>
578
+ const canRedo = await editorRef.value!.canRedo(); // Promise<boolean>
579
+ editorRef.value!.save(); // void
580
+ ```
581
+
582
+ ### Preview
583
+
584
+ ```ts
585
+ editorRef.value!.showPreview(device?); // void — 'desktop' | 'tablet' | 'mobile'
586
+ editorRef.value!.hidePreview(); // void
587
+ ```
588
+
589
+ ### Custom Tools
590
+
591
+ ```ts
592
+ await editorRef.value!.registerTool(config); // Promise<void>
593
+ await editorRef.value!.unregisterTool(toolId); // Promise<void>
594
+ const tools = await editorRef.value!.getTools(); // Promise<Array<{ id, label, baseToolType }>>
595
+ ```
596
+
597
+ ### Custom Widgets
598
+
599
+ ```ts
600
+ await editorRef.value!.createWidget(config); // Promise<void>
601
+ await editorRef.value!.removeWidget(widgetName); // Promise<void>
602
+ ```
603
+
604
+ ### Collaboration & Comments
605
+
606
+ ```ts
607
+ editorRef.value!.showComment(commentId); // void
608
+ editorRef.value!.openCommentPanel(rowId); // void
609
+ ```
610
+
611
+ ### Tabs & Branding
612
+
613
+ ```ts
614
+ editorRef.value!.updateTabs(tabs); // void
615
+ editorRef.value!.setBrandingColors(config); // void
616
+ editorRef.value!.registerColumns(cells); // void
617
+ ```
618
+
619
+ ### Display Conditions
620
+
621
+ ```ts
622
+ editorRef.value!.setDisplayConditions(config); // void
623
+ ```
624
+
625
+ ### Audit
626
+
627
+ ```ts
628
+ const result = await editorRef.value!.audit(options?); // Promise<AuditResult>
629
+ ```
630
+
631
+ ### Asset Management
632
+
633
+ ```ts
634
+ const { success, url, error } = await editorRef.value!.uploadImage(file, options?);
635
+ const { assets, total } = await editorRef.value!.listAssets(options?);
636
+ const { success, error } = await editorRef.value!.deleteAsset(assetId);
637
+ const folders = await editorRef.value!.listAssetFolders(parentId?);
638
+ const folder = await editorRef.value!.createAssetFolder(name, parentId?);
639
+ const info = await editorRef.value!.getStorageInfo();
640
+ ```
641
+
642
+ ### Status & Lifecycle
643
+
644
+ ```ts
645
+ editorRef.value!.isReady(); // boolean
646
+ editorRef.value!.destroy(); // void
647
+ ```
648
+
649
+ ## Events
650
+
651
+ Component events (`@ready`, `@load`, `@change`, `@error`, `@comment`) cover the common Vue integration points. For lower-level SDK events, subscribe with `addEventListener` after the editor is ready:
652
+
653
+ ```ts
654
+ const unsubscribe = editorRef.value?.addEventListener(
655
+ "design:updated",
656
+ (data) => {
657
+ console.log("Design changed:", data);
658
+ },
659
+ );
660
+
661
+ // Or remove manually
662
+ editorRef.value?.removeEventListener("design:updated", callback);
663
+ ```
664
+
665
+ ### Available Events
666
+
667
+ | Event | Description |
668
+ | -------------------------- | --------------------------- |
669
+ | `editor:ready` | Editor initialized |
670
+ | `design:loaded` | Design loaded |
671
+ | `design:updated` | Design changed |
672
+ | `design:saved` | Design saved |
673
+ | `row:selected` | Row selected |
674
+ | `row:unselected` | Row unselected |
675
+ | `column:selected` | Column selected |
676
+ | `column:unselected` | Column unselected |
677
+ | `content:selected` | Content block selected |
678
+ | `content:unselected` | Content block unselected |
679
+ | `content:modified` | Content block modified |
680
+ | `content:added` | Content block added |
681
+ | `content:deleted` | Content block deleted |
682
+ | `preview:shown` | Preview opened |
683
+ | `preview:hidden` | Preview closed |
684
+ | `image:uploaded` | Image uploaded successfully |
685
+ | `image:error` | Image upload error |
686
+ | `export:html` | HTML exported |
687
+ | `export:plainText` | Plain text exported |
688
+ | `export:image` | Image exported |
689
+ | `save` | Save triggered |
690
+ | `save:success` | Save succeeded |
691
+ | `save:error` | Save failed |
692
+ | `template:requested` | Template requested |
693
+ | `element:selected` | Element selected |
694
+ | `element:deselected` | Element deselected |
695
+ | `export` | Export triggered |
696
+ | `displayCondition:applied` | Display condition applied |
697
+ | `displayCondition:removed` | Display condition removed |
698
+ | `displayCondition:updated` | Display condition updated |
699
+
216
700
  ## TypeScript
217
701
 
218
702
  Full type definitions are included. Import types directly from the package:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dragble-vue-editor",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "AI-powered Vue 3 email editor component. Drag-and-drop email builder for creating responsive email templates and newsletters. Build HTML emails visually with Dragble.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",