dragble-angular-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 +531 -68
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -11,9 +11,9 @@
11
11
 
12
12
  # dragble-angular-editor
13
13
 
14
- AI-powered Angular component for building **email templates** with drag-and-drop. Embed a full-featured **AI-powered email editor** into your Angular app create responsive HTML emails, newsletters, transactional email templates, and email marketing campaigns visually without writing code.
14
+ The **fully AI-powered** Angular 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 Angular 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 Angular 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
@@ -151,6 +152,307 @@ export class EditorComponent {
151
152
  }
152
153
  ```
153
154
 
155
+ ## Complete Example
156
+
157
+ ```typescript
158
+ import { Component, ViewChild } from "@angular/core";
159
+ import {
160
+ DragbleEditorComponent,
161
+ DesignJson,
162
+ DragbleSDK,
163
+ EditorOptions,
164
+ } from "dragble-angular-editor";
165
+
166
+ @Component({
167
+ selector: "app-advanced-email-builder",
168
+ standalone: true,
169
+ imports: [DragbleEditorComponent],
170
+ styles: [
171
+ `
172
+ .advanced-email-builder {
173
+ height: 100vh;
174
+ display: flex;
175
+ flex-direction: column;
176
+ }
177
+
178
+ .toolbar {
179
+ padding: 12px;
180
+ border-bottom: 1px solid #ddd;
181
+ display: flex;
182
+ gap: 8px;
183
+ align-items: center;
184
+ }
185
+
186
+ .dirty-indicator {
187
+ color: orange;
188
+ }
189
+ `,
190
+ ],
191
+ template: `
192
+ <div class="advanced-email-builder">
193
+ <div class="toolbar">
194
+ <button type="button" (click)="editor.undo()">Undo</button>
195
+ <button type="button" (click)="editor.redo()">Redo</button>
196
+ <button type="button" (click)="editor.showPreview('desktop')">
197
+ Preview
198
+ </button>
199
+ <button type="button" (click)="handleExportHtml()">Export HTML</button>
200
+ <button type="button" (click)="handleExportImage()">
201
+ Export Image
202
+ </button>
203
+ <span *ngIf="isDirty" class="dirty-indicator">Unsaved changes</span>
204
+ </div>
205
+
206
+ <dragble-editor
207
+ #editor
208
+ [editorKey]="'your-editor-key'"
209
+ [editorMode]="'email'"
210
+ [height]="'100%'"
211
+ [designMode]="'live'"
212
+ [options]="editorOptions"
213
+ (ready)="handleReady($event)"
214
+ (change)="handleChange($event)"
215
+ (error)="handleError($event)"
216
+ ></dragble-editor>
217
+ </div>
218
+ `,
219
+ })
220
+ export class AdvancedEmailBuilderComponent {
221
+ @ViewChild("editor") editor!: DragbleEditorComponent;
222
+
223
+ isDirty = false;
224
+
225
+ editorOptions: EditorOptions = {
226
+ appearance: { theme: "light" },
227
+ features: {
228
+ preview: true,
229
+ undoRedo: true,
230
+ imageEditor: true,
231
+ },
232
+ };
233
+
234
+ handleReady(editor: DragbleSDK): void {
235
+ // Set merge tags (must pass a MergeTagsConfig object)
236
+ editor.setMergeTags({
237
+ customMergeTags: [
238
+ { name: "First Name", value: "{{first_name}}" },
239
+ { name: "Last Name", value: "{{last_name}}" },
240
+ { name: "Company", value: "{{company}}" },
241
+ ],
242
+ excludeDefaults: false,
243
+ sort: true,
244
+ });
245
+
246
+ // Set custom fonts
247
+ editor.setFonts({
248
+ showDefaultFonts: true,
249
+ customFonts: [{ label: "Brand Font", value: "BrandFont, sans-serif" }],
250
+ });
251
+
252
+ // Load saved design if available
253
+ const savedDesign = localStorage.getItem("email-design");
254
+ if (savedDesign) {
255
+ editor.loadDesign(JSON.parse(savedDesign));
256
+ }
257
+ }
258
+
259
+ handleChange(data: { design: DesignJson; type: string }): void {
260
+ this.isDirty = true;
261
+ localStorage.setItem("email-design", JSON.stringify(data.design));
262
+ }
263
+
264
+ async handleExportHtml(): Promise<void> {
265
+ const html = await this.editor.exportHtml();
266
+ const blob = new Blob([html], { type: "text/html" });
267
+ const url = URL.createObjectURL(blob);
268
+ const a = document.createElement("a");
269
+ a.href = url;
270
+ a.download = "email.html";
271
+ a.click();
272
+ URL.revokeObjectURL(url);
273
+ }
274
+
275
+ async handleExportImage(): Promise<void> {
276
+ const data = await this.editor.exportImage();
277
+ window.open(data.url, "_blank");
278
+ }
279
+
280
+ handleError(error: Error): void {
281
+ console.error(error.message);
282
+ }
283
+ }
284
+ ```
285
+
286
+ ## MCP — AI Integration
287
+
288
+ 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.
289
+
290
+ ### Enabling MCP
291
+
292
+ MCP is off by default. Set `features: { mcp: true }` to opt in:
293
+
294
+ ```html
295
+ <dragble-editor
296
+ #editor
297
+ [editorKey]="'db_pxl81cxn92wignwx'"
298
+ [options]="{ features: { mcp: true } }"
299
+ ></dragble-editor>
300
+ ```
301
+
302
+ MCP also requires a **Starter plan or higher**. Both conditions must be true — plan allows it AND SDK enables it.
303
+
304
+ ### Quick example — your backend controls the AI
305
+
306
+ ```typescript
307
+ import { Component, ViewChild } from "@angular/core";
308
+ import { DragbleEditorComponent } from "dragble-angular-editor";
309
+
310
+ @Component({
311
+ selector: "app-editor",
312
+ template: `
313
+ <button (click)="handleConnectAI()">Connect AI</button>
314
+ <dragble-editor
315
+ #editor
316
+ [editorKey]="'db_pxl81cxn92wignwx'"
317
+ [options]="{ features: { mcp: true } }"
318
+ ></dragble-editor>
319
+ `,
320
+ })
321
+ export class EditorComponent {
322
+ @ViewChild("editor") editor!: DragbleEditorComponent;
323
+
324
+ async handleConnectAI() {
325
+ // The id is YOUR identifier — derive it from your own database/session
326
+ // so the same user editing the same document always gets the same MCP
327
+ // session. Example: if your logged-in user is "alice123" and they're
328
+ // editing document "campaign-summer-2026", build an id like this:
329
+ //
330
+ // const id = "alice123-campaign-summer-2026";
331
+ //
332
+ // Format rules: 8-128 chars, only letters/digits/hyphens/underscores.
333
+ const userIdFromAuth = "alice123"; // from your auth/session
334
+ const docIdFromRoute = "campaign-summer"; // from your URL or DB row
335
+ const id = `${userIdFromAuth}-${docIdFromRoute}`;
336
+ const { sessionId } = await this.editor.editor!.connectMCP({ id });
337
+ // Pass sessionId to your backend — it calls MCP tools with your mcp_key
338
+ }
339
+ }
340
+ ```
341
+
342
+ ### Quick example — end-user pairs their own AI client
343
+
344
+ ```typescript
345
+ const handleLetUserPair = async () => {
346
+ const editor = this.editor.editor!;
347
+ // Same id you'd use anywhere else for this user+document combination.
348
+ // 8-128 chars, only letters/digits/hyphens/underscores.
349
+ const id = "alice123-campaign-summer-2026";
350
+ await editor.connectMCP({ id });
351
+
352
+ // Explicitly generate a pairing code (not auto-generated)
353
+ const { code, expiresAt } = await editor.getPairingCode();
354
+ alert(`Paste this into Claude Code: ${code}`);
355
+ };
356
+ ```
357
+
358
+ ### One controller per session
359
+
360
+ 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:
361
+
362
+ - If your backend makes the first tool call → session is locked to **backend**. Pairing codes are rejected.
363
+ - If a user pairs via pairing code first → session is locked to **paired client**. Backend tool calls are rejected.
364
+
365
+ This prevents two AI controllers from conflicting on the same design.
366
+
367
+ ### How it works
368
+
369
+ 1. **Enable MCP** in the SDK config: `features: { mcp: true }`.
370
+ 2. **Generate an MCP key** in the Dragble dashboard: Project → MCP Key → Generate. Store it in your backend env vars — never in browser code.
371
+ 3. **Call `editor.connectMCP({ id })`** where `id` is a stable identifier you control (see below).
372
+ 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.
373
+ 5. **Mutations stream live** onto the editor canvas as the AI works.
374
+
375
+ ### The `id` parameter — why it matters
376
+
377
+ 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.
378
+
379
+ **Rules:**
380
+
381
+ - 8–128 characters long
382
+ - Only letters, numbers, hyphens, and underscores (`a-z A-Z 0-9 - _`)
383
+ - Must be deterministic — the same user editing the same document should always produce the same `id`
384
+
385
+ **Why these rules?**
386
+
387
+ - The `id` is used in database lookups, URL paths, and storage keys — special characters or extreme lengths would break routing
388
+ - Same `id` = resume the same session. Random UUIDs mean every page refresh creates a new session and loses AI context
389
+ - Short IDs (< 8 chars) are too easy to guess, long IDs (> 128 chars) waste storage
390
+
391
+ ```typescript
392
+ // Recommended: derive from your domain — concrete examples
393
+ editor.connectMCP({ id: "alice123-campaign-summer-2026" }); // user + doc
394
+ editor.connectMCP({ id: "workspace_acme_template_welcome" }); // workspace + template
395
+ editor.connectMCP({ id: "org-uber-eats-promo-q4-2026" }); // org + campaign
396
+ editor.connectMCP({ id: "tenant_42_invoice_template_v3" }); // tenant + entity
397
+
398
+ // Valid but NOT recommended — random IDs break session continuity
399
+ // (every page refresh creates a brand new session, AI loses context)
400
+ editor.connectMCP({ id: crypto.randomUUID() });
401
+ ```
402
+
403
+ ### Storage modes (compliance)
404
+
405
+ Choose how much of the session lives on Dragble's servers:
406
+
407
+ | Mode | Persistence | Use case |
408
+ | ---------------- | ------------------------- | ------------------------------------------------------- |
409
+ | `full` (default) | Metadata + design content | Standard SaaS; survives refresh, restart, device switch |
410
+ | `metadata-only` | Metadata only | Audit logs without storing customer content |
411
+ | `memory-only` | None — RAM only | HIPAA / SOC2 / strict data residency |
412
+
413
+ ```typescript
414
+ editor.connectMCP({
415
+ id: "user-42-doc-99",
416
+ storage: "full", // default — best UX, refresh + cross-device resume
417
+ // "metadata-only" // audit metadata only, no design content persisted
418
+ // "memory-only" // nothing persisted (HIPAA / SOC2 / data residency)
419
+ });
420
+ ```
421
+
422
+ ### Disconnecting
423
+
424
+ `disconnectMCP()` permanently destroys the session — the database record is deleted and the session cannot be reopened:
425
+
426
+ ```typescript
427
+ const { destroyed } = await editor.disconnectMCP();
428
+ ```
429
+
430
+ Your backend can also force-destroy a session server-side (e.g., when a user's subscription ends):
431
+
432
+ ```bash
433
+ curl -X DELETE https://mcp.dragble.com/sessions/user-42-doc-99 \
434
+ -H "X-API-Key: db_mcp_your_key_here"
435
+ ```
436
+
437
+ Idle sessions are reaped after 2 hours of inactivity. Active sessions never expire — each tool call resets the timer.
438
+
439
+ ### MCP method reference
440
+
441
+ | Method | Returns |
442
+ | -------------------------------------------------- | ----------------------------------------------------------------------- |
443
+ | `editor.connectMCP({ id, storage?, editorMode? })` | `{ sessionId, storageMode?, resumed? }` |
444
+ | `editor.disconnectMCP()` | `{ destroyed }` — permanently deletes session |
445
+ | `editor.getPairingCode()` | `{ code, expiresAt }` — generate a pairing code for end-user AI clients |
446
+ | `editor.endPairing()` | `{ revoked }` — invalidate the active pairing code |
447
+ | `editor.getMCPStatus()` | `{ paired: true, sessionId } \| { paired: false, reason? }` |
448
+ | `editor.onAIToolFired(cb)` | unsubscribe fn — fires when AI calls any tool |
449
+
450
+ ### Full documentation
451
+
452
+ - [MCP Overview](https://docs.dragble.com/mcp-server/overview)
453
+ - [Credentials & Security](https://docs.dragble.com/mcp-server/credentials)
454
+ - [AI Client Setup (OpenCode, Claude Code, Codex, etc.)](https://docs.dragble.com/mcp-server/ai-client-setup)
455
+
154
456
  ## Inputs
155
457
 
156
458
  | Input | Type | Default | Description |
@@ -199,82 +501,243 @@ export class EditorComponent {
199
501
  | `error` | `Error` | Emitted when an error occurs |
200
502
  | `commentAction` | `CommentAction` | Emitted on comment events |
201
503
 
202
- ## Methods
504
+ ## SDK Methods Reference
203
505
 
204
- Access methods via `@ViewChild`:
506
+ Access methods through `@ViewChild` on the Angular component. The wrapper exposes SDK methods directly, so call `this.editor.exportHtml()` from your component class. All export and getter methods return Promises.
205
507
 
206
508
  ```typescript
207
- @ViewChild('editor') editor!: DragbleEditorComponent;
509
+ @ViewChild("editor") editor!: DragbleEditorComponent;
510
+
511
+ const html = await this.editor.exportHtml();
208
512
  ```
209
513
 
210
514
  ### Design
211
515
 
212
- | Method | Returns | Description |
213
- | ------------------------------ | --------- | ---------------------- |
214
- | `loadDesign(design, options?)` | `void` | Load a design |
215
- | `loadBlank(options?)` | `void` | Load a blank design |
216
- | `getDesign()` | `Promise` | Get the current design |
516
+ ```typescript
517
+ this.editor.loadDesign(design, options?); // void
518
+ const result = await this.editor.loadDesignAsync(design, options?);
519
+ // => { success, validRowsCount, invalidRowsCount, errors? }
520
+ this.editor.loadBlank(options?); // void
521
+ const { html, json } = await this.editor.getDesign(); // Promise
522
+ ```
217
523
 
218
524
  ### Export
219
525
 
220
- | Method | Returns | Description |
221
- | ----------------------- | -------------------------- | -------------------- |
222
- | `exportHtml(options?)` | `Promise<string>` | Export as HTML |
223
- | `exportJson()` | `Promise<DesignJson>` | Export as JSON |
224
- | `exportPlainText()` | `Promise<string>` | Export as plain text |
225
- | `exportImage(options?)` | `Promise<ExportImageData>` | Export as image |
226
- | `exportPdf(options?)` | `Promise<ExportPdfData>` | Export as PDF |
227
- | `exportZip(options?)` | `Promise<ExportZipData>` | Export as ZIP |
228
-
229
- ### Configuration
230
-
231
- | Method | Returns | Description |
232
- | ---------------------------------- | ------- | ------------------------- |
233
- | `setMergeTags(config)` | `void` | Update merge tags |
234
- | `setSpecialLinks(config)` | `void` | Update special links |
235
- | `setModules(modules)` | `void` | Update custom modules |
236
- | `setFonts(config)` | `void` | Update fonts |
237
- | `setBodyValues(values)` | `void` | Update body values |
238
- | `setToolsConfig(config)` | `void` | Update tools config |
239
- | `setAppearance(config)` | `void` | Update appearance |
240
- | `setEditorMode(mode)` | `void` | Change editor mode |
241
- | `setEditorConfig(config)` | `void` | Update editor config |
242
- | `setLocale(locale, translations?)` | `void` | Change locale |
243
- | `setTextDirection(direction)` | `void` | Set text direction |
244
- | `setLanguage(language)` | `void` | Set template language |
245
- | `setDisplayConditions(config)` | `void` | Update display conditions |
246
- | `setOptions(options)` | `void` | Update editor options |
247
- | `setBrandingColors(config)` | `void` | Update branding colors |
248
-
249
- ### Editor Actions
250
-
251
- | Method | Returns | Description |
252
- | ---------------------- | ---------------------- | -------------------------- |
253
- | `save()` | `void` | Trigger a save event |
254
- | `undo()` | `void` | Undo last action |
255
- | `redo()` | `void` | Redo last action |
256
- | `canUndo()` | `Promise<boolean>` | Check if undo is available |
257
- | `canRedo()` | `Promise<boolean>` | Check if redo is available |
258
- | `showPreview(device?)` | `void` | Show design preview |
259
- | `hidePreview()` | `void` | Hide preview |
260
- | `audit(options?)` | `Promise<AuditResult>` | Run design audit |
261
-
262
- ### Tools & Widgets
263
-
264
- | Method | Returns | Description |
265
- | ---------------------- | --------- | ---------------------- |
266
- | `registerTool(config)` | `Promise` | Register a custom tool |
267
- | `unregisterTool(id)` | `Promise` | Unregister a tool |
268
- | `getTools()` | `Promise` | Get registered tools |
269
- | `createWidget(config)` | `Promise` | Create a widget |
270
- | `removeWidget(name)` | `Promise` | Remove a widget |
271
-
272
- ### Events
273
-
274
- | Method | Returns | Description |
275
- | -------------------------------------- | ------------ | --------------------------------------------------- |
276
- | `addEventListener(event, callback)` | `() => void` | Subscribe to an event; returns unsubscribe function |
277
- | `removeEventListener(event, callback)` | `void` | Remove an event listener |
526
+ All export methods are **Promise-based**. There are no callback overloads.
527
+
528
+ ```typescript
529
+ const html = await this.editor.exportHtml(options?); // Promise<string>
530
+ const json = await this.editor.exportJson(); // Promise<DesignJson>
531
+ const text = await this.editor.exportPlainText(); // Promise<string>
532
+ const imageData = await this.editor.exportImage(options?); // Promise<ExportImageData>
533
+ const pdfData = await this.editor.exportPdf(options?); // Promise<ExportPdfData>
534
+ const zipData = await this.editor.exportZip(options?); // Promise<ExportZipData>
535
+ const values = await this.editor.getPopupValues(); // Promise<PopupValues | null>
536
+ ```
537
+
538
+ ### Merge Tags
539
+
540
+ `setMergeTags` accepts a `MergeTagsConfig` object, not a plain array.
541
+
542
+ ```typescript
543
+ this.editor.setMergeTags({
544
+ customMergeTags: [
545
+ { name: "First Name", value: "{{first_name}}" },
546
+ { name: "Company", value: "{{company}}" },
547
+ ],
548
+ excludeDefaults: false,
549
+ sort: true,
550
+ });
551
+ const tags = await this.editor.getMergeTags(); // Promise<(MergeTag | MergeTagGroup)[]>
552
+ ```
553
+
554
+ ### Special Links
555
+
556
+ `setSpecialLinks` accepts a `SpecialLinksConfig` object.
557
+
558
+ ```typescript
559
+ this.editor.setSpecialLinks({
560
+ customSpecialLinks: [{ name: "Unsubscribe", href: "{{unsubscribe_url}}" }],
561
+ excludeDefaults: false,
562
+ });
563
+ const links = await this.editor.getSpecialLinks(); // Promise<(SpecialLink | SpecialLinkGroup)[]>
564
+ ```
565
+
566
+ ### Modules
567
+
568
+ ```typescript
569
+ this.editor.setModules(modules); // void
570
+ this.editor.setModulesLoading(loading); // void
571
+ const modules = await this.editor.getModules(); // Promise<Module[]>
572
+ ```
573
+
574
+ ### Fonts
575
+
576
+ ```typescript
577
+ this.editor.setFonts(config); // void
578
+ const fonts = await this.editor.getFonts(); // Promise<FontsConfig>
579
+ ```
580
+
581
+ ### Body Values
582
+
583
+ ```typescript
584
+ this.editor.setBodyValues({
585
+ backgroundColor: "#f5f5f5",
586
+ contentWidth: "600px",
587
+ });
588
+ const values = await this.editor.getBodyValues(); // Promise<SetBodyValuesOptions>
589
+ ```
590
+
591
+ ### Editor Configuration
592
+
593
+ ```typescript
594
+ this.editor.setOptions(options); // void — Partial<EditorOptions>
595
+ this.editor.setToolsConfig(toolsConfig); // void
596
+ this.editor.setEditorMode(mode); // void
597
+ this.editor.setEditorConfig(config); // void
598
+ const config = await this.editor.getEditorConfig(); // Promise<EditorBehaviorConfig>
599
+ ```
600
+
601
+ ### Locale, Language & Text Direction
602
+
603
+ ```typescript
604
+ this.editor.setLocale(locale, translations?); // void
605
+ this.editor.setLanguage(language); // void
606
+ const lang = await this.editor.getLanguage(); // Promise<Language | null>
607
+ this.editor.setTextDirection(direction); // void — 'ltr' | 'rtl'
608
+ const dir = await this.editor.getTextDirection(); // Promise<TextDirection>
609
+ ```
610
+
611
+ ### Appearance
612
+
613
+ ```typescript
614
+ this.editor.setAppearance(appearance); // void
615
+ ```
616
+
617
+ ### Undo / Redo / Save
618
+
619
+ ```typescript
620
+ this.editor.undo(); // void
621
+ this.editor.redo(); // void
622
+ const canUndo = await this.editor.canUndo(); // Promise<boolean>
623
+ const canRedo = await this.editor.canRedo(); // Promise<boolean>
624
+ this.editor.save(); // void
625
+ ```
626
+
627
+ ### Preview
628
+
629
+ ```typescript
630
+ this.editor.showPreview(device?); // void — 'desktop' | 'tablet' | 'mobile'
631
+ this.editor.hidePreview(); // void
632
+ ```
633
+
634
+ ### Custom Tools
635
+
636
+ ```typescript
637
+ await this.editor.registerTool(config); // Promise<void>
638
+ await this.editor.unregisterTool(toolId); // Promise<void>
639
+ const tools = await this.editor.getTools(); // Promise<Array<{ id, label, baseToolType }>>
640
+ ```
641
+
642
+ ### Custom Widgets
643
+
644
+ ```typescript
645
+ await this.editor.createWidget(config); // Promise<void>
646
+ await this.editor.removeWidget(widgetName); // Promise<void>
647
+ ```
648
+
649
+ ### Collaboration & Comments
650
+
651
+ ```typescript
652
+ this.editor.showComment(commentId); // void
653
+ this.editor.openCommentPanel(rowId); // void
654
+ ```
655
+
656
+ ### Tabs & Branding
657
+
658
+ ```typescript
659
+ this.editor.updateTabs(tabs); // void
660
+ this.editor.setBrandingColors(config); // void
661
+ this.editor.registerColumns(cells); // void
662
+ ```
663
+
664
+ ### Display Conditions
665
+
666
+ ```typescript
667
+ this.editor.setDisplayConditions(config); // void
668
+ ```
669
+
670
+ ### Audit
671
+
672
+ ```typescript
673
+ const result = await this.editor.audit(options?); // Promise<AuditResult>
674
+ ```
675
+
676
+ ### Asset Management
677
+
678
+ ```typescript
679
+ const { success, url, error } = await this.editor.uploadImage(file, options?);
680
+ const { assets, total } = await this.editor.listAssets(options?);
681
+ const { success, error } = await this.editor.deleteAsset(assetId);
682
+ const folders = await this.editor.listAssetFolders(parentId?);
683
+ const folder = await this.editor.createAssetFolder(name, parentId?);
684
+ const info = await this.editor.getStorageInfo();
685
+ ```
686
+
687
+ ### Status & Lifecycle
688
+
689
+ ```typescript
690
+ this.editor.isReady(); // boolean
691
+ this.editor.destroy(); // void
692
+ ```
693
+
694
+ ## Events
695
+
696
+ Angular outputs (`(ready)`, `(load)`, `(change)`, `(error)`, `(commentAction)`) cover the common component integration points. For lower-level SDK events, subscribe with `addEventListener` after the editor is ready:
697
+
698
+ ```typescript
699
+ const unsubscribe = this.editor.addEventListener("design:updated", (data) => {
700
+ console.log("Design changed:", data);
701
+ });
702
+
703
+ // Or remove manually
704
+ this.editor.removeEventListener("design:updated", callback);
705
+ ```
706
+
707
+ ### Available Events
708
+
709
+ | Event | Description |
710
+ | -------------------------- | --------------------------- |
711
+ | `editor:ready` | Editor initialized |
712
+ | `design:loaded` | Design loaded |
713
+ | `design:updated` | Design changed |
714
+ | `design:saved` | Design saved |
715
+ | `row:selected` | Row selected |
716
+ | `row:unselected` | Row unselected |
717
+ | `column:selected` | Column selected |
718
+ | `column:unselected` | Column unselected |
719
+ | `content:selected` | Content block selected |
720
+ | `content:unselected` | Content block unselected |
721
+ | `content:modified` | Content block modified |
722
+ | `content:added` | Content block added |
723
+ | `content:deleted` | Content block deleted |
724
+ | `preview:shown` | Preview opened |
725
+ | `preview:hidden` | Preview closed |
726
+ | `image:uploaded` | Image uploaded successfully |
727
+ | `image:error` | Image upload error |
728
+ | `export:html` | HTML exported |
729
+ | `export:plainText` | Plain text exported |
730
+ | `export:image` | Image exported |
731
+ | `save` | Save triggered |
732
+ | `save:success` | Save succeeded |
733
+ | `save:error` | Save failed |
734
+ | `template:requested` | Template requested |
735
+ | `element:selected` | Element selected |
736
+ | `element:deselected` | Element deselected |
737
+ | `export` | Export triggered |
738
+ | `displayCondition:applied` | Display condition applied |
739
+ | `displayCondition:removed` | Display condition removed |
740
+ | `displayCondition:updated` | Display condition updated |
278
741
 
279
742
  ## TypeScript
280
743
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dragble-angular-editor",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "AI-powered Angular 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",