agentsclimarketplace

Dragble config

Skill Dragble/dragble-skills/dragble-config

Configure the Dragble editor appearance, features, merge tags, special links, fonts, localization, display conditions, collaboration, branding colors, and editor behavior.From its SKILL.md

Install
npx -y skills add Dragble/dragble-skills --skill dragble-config

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

29.5 KB, ~7.9k tokens by cl100k_base, as published. Nobody here has run it

Dragble Editor Configuration

Overview

All editor configuration is passed through the options object in dragble.init(). The top-level DragbleConfig controls boot-time settings (container, auth, mode), while options holds all runtime-configurable settings (appearance, tools, features, localization, merge tags, etc.).

dragble.init({
  containerId: 'editor-container',
  editorKey: 'ek_xxx',
  editorMode: 'email',
  options: {
    appearance: { theme: 'light', accentColor: 'indigo' },
    features: { preview: true, undoRedo: true },
    tools: { video: { enabled: false } },
    locale: 'en-US',
    // ... all other config
  },
});

DragbleConfig (Top Level)

PropertyTypeRequiredDescription
containerIdstringYesDOM element ID where the editor iframe is mounted
editorKeystringYesProject editor key for authentication (ek_...)
editorMode'email' | 'web' | 'popup'NoBuilder type. Default: 'email'
designMode'edit' | 'live'No'edit' = admin mode (shows Row Actions), 'live' = end-user mode (enforces row permissions). Default: 'live'
designDesignJson | nullNoInitial design JSON. null = blank design, undefined = no design loaded
popupPopupConfigNoPopup builder config (only relevant when editorMode is 'popup')
callbacksDragbleCallbacksNoEvent callbacks (onReady, onChange, onLoad, onError, onModuleSave, onPreview, onContentDialog, etc.)
optionsEditorOptionsNoAll editor configuration (appearance, tools, features, locale, merge tags, etc.)
editorUrlstringNoCustom editor source URL for enterprise self-hosted editors
editorVersion'stable' | 'latest' | stringNoEditor version to load. Default: 'latest'. Ignored when editorUrl is set
environment'production' | 'development' | 'staging'NoEnvironment mode
dragble.init({
  containerId: 'editor',
  editorKey: 'ek_live_abc123',
  editorMode: 'email',
  designMode: 'live',
  design: savedDesignJson,
  callbacks: {
    onReady: () => console.log('Editor ready'),
    onChange: (data) => console.log('Design changed', data),
  },
  options: { /* ... */ },
});

Appearance

Theme

The editor supports 4 themes. Branded themes ignore accentColor.

ThemeaccentColorDescription
'light'CustomizableGeneric light theme, accent tints the UI
'dark'CustomizableGeneric dark theme, accent tints the UI
'dragble-light'IGNORED (fixed indigo)Dragble branded light theme
'dragble-dark'IGNORED (fixed white-on-black)Dragble branded dark theme

CORRECT:

options: {
  appearance: {
    theme: 'light',
    accentColor: 'teal', // Works — generic theme respects accentColor
  }
}

WRONG:

options: {
  appearance: {
    theme: 'dragble-light',
    accentColor: 'teal', // IGNORED — branded themes use fixed colors
  }
}

Accent Colors

24 Radix UI color scale names:

gray, gold, bronze, brown, yellow, amber, orange, tomato, red, ruby, crimson, pink, plum, purple, violet, iris, indigo, blue, cyan, teal, jade, green, grass, mint, sky

Side Panel

options: {
  appearance: {
    sidePanel: {
      tabs: {
        content: { visible: true },   // CONTENT tab (tools)
        modules: { visible: true },    // MODULES tab (rows + custom tabs)
        styles: { visible: true },     // STYLES tab (body settings)
      },
      modulesTab: {
        rows: { visible: true, defaultExpanded: true },
        modulesLibrary: {
          visible: true,
          defaultExpanded: false,
          title: 'My Modules',
          categories: ['Banner', 'footer', 'header'], // Filter/order displayed categories (optional)
          defaultCategory: 'Banner',                    // Pre-selected category (optional)
        },
        customTabs: [
          { id: 'snippets', label: 'Snippets', content: '...', icon: 'star', order: 1, visible: true }
        ],
      },
      stylesTab: {
        general: { visible: true, defaultExpanded: true },
        fonts: { visible: true, defaultExpanded: false },
        contentAlignment: { visible: true },
        backgroundImage: { visible: true },
        linkStyles: { visible: true },
        preheader: { visible: true },
      },
      dock: 'right',           // 'left' | 'right' (default: 'right')
      width: 380,              // Panel width in pixels (default: 380)
      collapsible: false,      // Whether panel can be collapsed (default: false)
      accordionsCollapsed: false, // Start all accordions collapsed (default: false)
    },
  }
}

Action Bar

options: {
  appearance: {
    actionBar: {
      placement: 'top_left',  // 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'
      compact: false,          // Smaller buttons when true
    },
  }
}

Shortcut Bar

options: {
  appearance: {
    shortcutBar: {
      placement: 'top_left',  // 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'
    },
  }
}

Custom Loader

Priority: svg > url > html + css > default spinner.

options: {
  appearance: {
    loader: {
      svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><circle cx="25" cy="25" r="20" fill="none" stroke="#6366f1" stroke-width="4"><animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="1s" repeatCount="indefinite"/></circle></svg>',
      // OR
      url: 'https://example.com/loader.gif',
      // OR
      html: '<div class="my-loader">Loading...</div>',
      css: '.my-loader { color: #6366f1; font-size: 18px; }',
    },
  }
}

Branding Colors

Set brand colors for all color pickers in the editor at runtime:

// Simple flat array
dragble.setBrandingColors({
  colors: ['#FF0000', '#00FF00', '#0000FF', '#FFAA00'],
  defaultColors: true, // show default palette below brand colors (default: true)
});

// Named palettes
dragble.setBrandingColors({
  colors: [
    { id: 'primary', label: 'Primary', colors: ['#6366f1', '#4f46e5', '#4338ca'], default: true },
    { id: 'secondary', label: 'Secondary', colors: ['#f59e0b', '#d97706', '#b45309'] },
  ],
  defaultColors: false, // hide default palette
});

Runtime

dragble.setAppearance({ theme: 'dark', accentColor: 'violet' });

Feature Flags

Feature flags live in options.features. All are optional.

FeatureTypeDefaultDescription
previewbooleantrueShow preview button
undoRedobooleantrueShow undo/redo buttons
responsiveDesignbooleantrueShow responsive design view toggles (desktop/mobile)
imageEditorbooleanundefinedEnable inline image editor. false = force disable even if plan allows
auditbooleanfalseEnable template audit trail
stockImagesboolean | StockImagesConfigtrueStock images in file manager. false = hide even if plan allows
userUploadsbooleantrueEnable user image uploads
fileManagerbooleantrueShow File Manager modal. false hides "Choose Image" button but keeps direct upload
smartMergeTagsbooleanfalseEnable smart merge tag suggestions
specialLinksbooleantrueShow special links in text toolbar
languageSelectorbooleantrueShow language dropdown in side panel. false = language controlled via SDK only
preheaderTextbooleantrueShow preheader text input in Styles panel (email mode only)
ampbooleanfalseEnable AMP for Email (email mode only). Unlocks interactive tools
modulesboolean | ModuleFeaturesConfigfalseEnable Modules Library (save row as module, synced modules). Not available in contentType: 'module'
headerbooleanfalseEnable locked header row from SDK. Requires plan permission
footerbooleanfalseEnable locked footer row from SDK. Requires plan permission
dynamicImagebooleanundefinedEnable dynamic image merge tag on image tool (Business+ plans)
collaborationboolean | CollaborationFeaturesConfigfalseTeam collaboration (commenting, reviewer role). See Collaboration
exportExportFeaturesConfigall trueGranular export toggles: { html, json, image, pdf, zip }
options: {
  features: {
    preview: true,
    undoRedo: true,
    responsiveDesign: true,
    imageEditor: true,
    audit: true,
    stockImages: { enabled: true, safeSearch: true, defaultSearchTerm: 'business' },
    userUploads: true,
    fileManager: true,
    smartMergeTags: false,
    specialLinks: true,
    languageSelector: true,
    preheaderText: true,
    amp: false,
    modules: { saveRowAsModule: true },
    header: false,
    footer: false,
    dynamicImage: true,
    collaboration: true,
    export: { html: true, json: true, image: true, pdf: true, zip: true },
  }
}

Merge Tags

Merge tags are dynamic content placeholders (e.g., {{first_name}}) that users can insert into text.

MergeTagsConfig

interface MergeTagsConfig {
  excludeDefaults?: boolean;     // Hide built-in tags, show only custom (default: false)
  sort?: boolean;                // Sort alphabetically
  customMergeTags?: (MergeTag | MergeTagGroup)[];
}

interface MergeTag {
  label: string;                 // Display label in dropdown
  value: string;                 // Value inserted (e.g., '{{first_name}}')
  category?: string;             // Optional grouping category
  sample?: string;               // Preview sample value
}

interface MergeTagGroup {
  name: string;                  // Group name
  mergeTags: (MergeTag | MergeTagGroup)[];  // Nested tags
}

Flat Example

options: {
  mergeTags: {
    customMergeTags: [
      { label: 'First Name', value: '{{first_name}}', sample: 'Jane' },
      { label: 'Last Name', value: '{{last_name}}', sample: 'Doe' },
      { label: 'Email', value: '{{email}}', sample: '[email protected]' },
    ],
  }
}

Grouped Example

options: {
  mergeTags: {
    excludeDefaults: true,
    customMergeTags: [
      {
        name: 'Contact Info',
        mergeTags: [
          { label: 'First Name', value: '{{contact.first_name}}', sample: 'Jane' },
          { label: 'Last Name', value: '{{contact.last_name}}', sample: 'Doe' },
        ],
      },
      {
        name: 'Company',
        mergeTags: [
          { label: 'Company Name', value: '{{company.name}}', sample: 'Acme Inc' },
          { label: 'Website', value: '{{company.website}}', sample: 'https://acme.com' },
        ],
      },
    ],
  }
}

Runtime

dragble.setMergeTags({
  customMergeTags: [
    { label: 'Coupon Code', value: '{{coupon_code}}', sample: 'SAVE20' },
  ],
});

const tags = await dragble.getMergeTags();

Special Links

Special links add custom link categories to the link picker in the text editor toolbar.

SpecialLinksConfig

interface SpecialLinksConfig {
  excludeDefaults?: boolean;
  customSpecialLinks?: (SpecialLink | SpecialLinkGroup)[];
}

interface SpecialLink {
  name: string;                  // Display name
  href: string;                  // Link href value
  target?: '_blank' | '_self' | '_parent' | '_top';
}

interface SpecialLinkGroup {
  name: string;                  // Group name
  specialLinks: SpecialLink[];
}

Flat Example

options: {
  specialLinks: {
    customSpecialLinks: [
      { name: 'Unsubscribe', href: '{{unsubscribe_url}}', target: '_blank' },
      { name: 'View in Browser', href: '{{webview_url}}', target: '_blank' },
      { name: 'Manage Preferences', href: '{{preferences_url}}', target: '_blank' },
    ],
  }
}

Grouped Example

options: {
  specialLinks: {
    excludeDefaults: true,
    customSpecialLinks: [
      {
        name: 'Email Actions',
        specialLinks: [
          { name: 'Unsubscribe', href: '{{unsubscribe_url}}' },
          { name: 'View in Browser', href: '{{webview_url}}' },
        ],
      },
      {
        name: 'Social',
        specialLinks: [
          { name: 'Share on Twitter', href: '{{share_twitter_url}}' },
          { name: 'Share on Facebook', href: '{{share_facebook_url}}' },
        ],
      },
    ],
  }
}

Runtime

dragble.setSpecialLinks({
  customSpecialLinks: [
    { name: 'Survey', href: 'https://example.com/survey' },
  ],
});

const links = await dragble.getSpecialLinks();

Fonts

FontsConfig

interface FontsConfig {
  excludeDefaults?: boolean;   // Hide built-in fonts, show only custom (default: false)
  customFonts?: FontDefinition[];
}

interface FontDefinition {
  label: string;               // Display label in dropdown
  value: string;               // CSS font-family value
  url?: string;                // URL to load font (Google Fonts, etc.)
  weights?: number[];          // Available font weights (e.g., [400, 700])
  defaultFont?: boolean;       // Whether this is a default system font
}

Example

options: {
  fonts: {
    excludeDefaults: false,
    customFonts: [
      {
        label: 'Inter',
        value: "'Inter', sans-serif",
        url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap',
        weights: [400, 500, 600, 700],
      },
      {
        label: 'Playfair Display',
        value: "'Playfair Display', serif",
        url: 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&display=swap',
        weights: [400, 700],
      },
    ],
  }
}

Runtime

dragble.setFonts({
  customFonts: [
    { label: 'Roboto', value: "'Roboto', sans-serif", url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap' },
  ],
});

const fonts = await dragble.getFonts();

Display Conditions

Display conditions enable conditional rendering of rows based on user data or context. Conditions wrap row HTML with before/after syntax (e.g., Liquid, Jinja, Handlebars) that the sending platform evaluates at render time.

DisplayConditionsConfig

interface DisplayConditionsConfig {
  enabled?: boolean;                          // Enable feature (default: false)
  conditions?: DisplayConditionDefinition[];  // Pre-defined conditions for picker
  permissions?: DisplayConditionsPermissions;
}

interface DisplayConditionDefinition {
  type: string;        // Category (e.g., 'Customer Segment')
  label: string;       // Display label in picker
  description?: string;
  before: string;      // Syntax before the row (e.g., '{% if customer.vip %}')
  after: string;       // Syntax after the row (e.g., '{% endif %}')
}

interface DisplayConditionsPermissions {
  canSelect?: boolean;  // Select from pre-defined conditions (default: true)
  canAdd?: boolean;     // Add custom conditions with own syntax (default: true)
  canEdit?: boolean;    // Edit existing conditions (default: true)
  canRemove?: boolean;  // Remove conditions from rows (default: true)
}

Template Engine Syntax

The before and after fields wrap the row's HTML at export time:

<!-- before -->
{% if customer.tier == "VIP" %}
  <tr><!-- row HTML --></tr>
{% endif %}
<!-- after -->

Any template engine syntax works (Liquid, Jinja2, Handlebars, Mustache, etc.) since the editor passes these strings through verbatim.

Example

options: {
  displayConditions: {
    enabled: true,
    conditions: [
      {
        type: 'Customer Segment',
        label: 'VIP Members',
        description: 'Only show to VIP tier customers',
        before: '{% if customer.tier == "VIP" %}',
        after: '{% endif %}',
      },
      {
        type: 'Cart Status',
        label: 'Has Items in Cart',
        before: '{% if cart.items_count > 0 %}',
        after: '{% endif %}',
      },
      {
        type: 'Location',
        label: 'US Customers Only',
        before: '{{#if (eq country "US")}}',
        after: '{{/if}}',
      },
    ],
    permissions: {
      canSelect: true,
      canAdd: false,   // Users can only pick from pre-defined, not create custom
      canEdit: false,
      canRemove: true,
    },
  }
}

Runtime

dragble.setDisplayConditions({
  enabled: true,
  conditions: [
    { type: 'Segment', label: 'New Users', before: '{% if user.new %}', after: '{% endif %}' },
  ],
  permissions: { canSelect: true, canAdd: true, canRemove: true },
});

// Disable display conditions
dragble.setDisplayConditions({ enabled: false });

Localization

Supported Locales

The editor ships with full translations for 29 locales (BCP 47 format):

CodeLanguage
en-USEnglish (US)
en-CAEnglish (Canada)
ar-AEArabic (UAE) — RTL
zh-CNChinese (Simplified)
zh-TWChinese (Traditional)
cs-CZCzech
da-DKDanish
nl-NLDutch
et-EEEstonian
fa-IRFarsi / Persian — RTL
fi-FIFinnish
fr-FRFrench (France)
fr-CAFrench (Canada)
de-DEGerman
hu-HUHungarian
id-IDIndonesian
it-ITItalian
ja-JPJapanese
ko-KRKorean
no-NONorwegian
pl-PLPolish
pt-BRPortuguese (Brazil)
pt-PTPortuguese (Portugal)
ru-RURussian
es-ESSpanish
sv-SESwedish
tr-TRTurkish
uk-UAUkrainian
vi-VNVietnamese

Base codes (e.g., 'en', 'fr', 'de') are also accepted and auto-mapped to their default regional variant.

Configuration

options: {
  locale: 'fr-FR',

  // Override or extend built-in translations
  translations: {
    'fr-FR': {
      'content_tools.paragraph': 'Mon Paragraphe',
      'buttons.save': 'Sauvegarder',
    },
    // Add a completely new language
    'th': {
      'content_tools.paragraph': 'ย่อหน้า',
      'buttons.save': 'บันทึก',
      // Missing keys fall back to English
    },
  },

  textDirection: 'ltr',  // 'ltr' | 'rtl'. Auto-set when language has rtl flag.

  // Template language (distinct from UI locale)
  language: {
    label: 'العربية',
    value: 'ar-SA',
    rtl: true,       // Auto-detects RTL from language code (ar, he, fa, ur) when omitted
    default: false,
  },
}

Priority: translations[locale] > built-in locale > English US defaults.

Runtime

// Switch UI locale
dragble.setLocale('fr-FR');

// Switch with custom overrides
dragble.setLocale('fr-CA', { 'buttons.save': 'Sauvegarder' });

// Set template language (auto-switches text direction for RTL)
dragble.setLanguage({ label: 'العربية', value: 'ar-SA', rtl: true });

// Get current template language
const lang = await dragble.getLanguage();

// Manual text direction control
dragble.setTextDirection('rtl');
const dir = await dragble.getTextDirection();

Collaboration

Team collaboration enables commenting, reviewer roles, and @mentions on the editor canvas.

CollaborationFeaturesConfig

interface CollaborationFeaturesConfig {
  enabled?: boolean;                   // Enable collaboration (default: false)
  role?: 'editor' | 'reviewer';       // 'editor' = full edit + comment, 'reviewer' = read-only + comment
  commenting?: boolean | CollaborationCommentingConfig;
  onComment?: (action: CommentAction) => void;
}

interface CollaborationCommentingConfig {
  enabled?: boolean;                   // Enable commenting (default: true when collaboration enabled)
  mentions?: boolean;                  // Enable @mentions (default: true)
  getMentions?: (search: string) => Promise<MentionUser[]>;
}

interface MentionUser {
  userHandle: string;                  // e.g., '@jane'
  username: string;                    // 'Jane Smith'
  userColor?: string;                  // '#3498DB'
}

CommentAction Types

TypeDescription
NEW_COMMENTA new comment or reply was created
COMMENT_EDITEDAn existing comment's content was modified
COMMENT_DELETEDA comment was removed
COMMENT_THREAD_RESOLVEDA root comment thread was marked as resolved
COMMENT_THREAD_REOPENEDA previously resolved thread was reopened

Requirements

Collaboration requires user.id to be set in options so comments can be attributed:

options: {
  user: { id: 'user-123', name: 'Jane Doe', email: '[email protected]', color: '#3b82f6' },
}

Simple Example

options: {
  user: { id: 'user-123', name: 'Jane Doe' },
  features: {
    collaboration: true, // Enable commenting for current user as 'editor' role
  },
}

Reviewer Role

options: {
  user: { id: 'user-456', name: 'Bob Reviewer' },
  features: {
    collaboration: {
      enabled: true,
      role: 'reviewer', // Read-only editing + commenting
      commenting: true,
    },
  },
}

Advanced with @Mentions and Notifications

options: {
  user: { id: 'user-123', name: 'Jane Doe', color: '#E74C3C' },
  features: {
    collaboration: {
      enabled: true,
      role: 'editor',
      commenting: {
        enabled: true,
        mentions: true,
        getMentions: async (search) => {
          const res = await fetch(`/api/team?q=${encodeURIComponent(search)}`);
          return await res.json();
          // Returns: [{ userHandle: '@bob', username: 'Bob Smith', userColor: '#3498DB' }]
        },
      },
      onComment: (action) => {
        console.log(`${action.type}: ${action.comment.content}`);
        if (action.type === 'NEW_COMMENT' && action.mentions.length > 0) {
          notifyMentionedUsers(action.mentions);
        }
        // Sync all comments to backend
        saveCommentsToBackend(action.comments);
      },
    },
  },
}

Editor Behavior

EditorBehaviorConfig

interface EditorBehaviorConfig {
  minRows?: number;                   // Minimum rows in body (default: 0)
  maxRows?: number | null;            // Maximum rows (default: unlimited)
  contentType?: 'module';             // 'module' = single-row module editor (locked to 1 row)
  autoSelectOnDrop?: boolean;         // Auto-select element when dropped (default: false)
  confirmOnDelete?: boolean;          // Show confirm dialog before deleting (default: false)
  rows?: boolean;                     // Show Rows accordion in Modules tab (default: true)
  title?: string;                     // Editor title (shown in header)
}

Example

options: {
  editor: {
    minRows: 1,
    maxRows: 10,
    autoSelectOnDrop: true,
    confirmOnDelete: true,
    rows: true,
    title: 'Newsletter Editor',
  },
}

Module Editor (Single-Row)

options: {
  editor: {
    contentType: 'module', // Locks editor to 1 row for reusable content editing
  },
}

Runtime

dragble.setEditorConfig({
  maxRows: 5,
  autoSelectOnDrop: true,
  confirmOnDelete: false,
});

const config = await dragble.getEditorConfig();

Body/Canvas Values

Control the email/page body defaults (background, content width, fonts, links, preheader).

setBodyValues / getBodyValues

interface SetBodyValuesOptions {
  backgroundColor?: string;
  contentWidth?: string;             // e.g., '600px'
  contentAlign?: 'left' | 'center';
  backgroundImage?: Partial<BackgroundImage>;
  fontFamily?: FontFamily;           // { label: string, value: string, url?: string }
  textColor?: string;
  preheaderText?: string;            // Email only
  linkStyle?: Partial<LinkStyle>;
}

Example

// Set at init
options: {
  bodyValues: {
    backgroundColor: '#f5f5f5',
    contentWidth: '600px',
    contentAlign: 'center',
    textColor: '#333333',
    fontFamily: { label: 'Inter', value: "'Inter', sans-serif" },
    preheaderText: 'Check out our latest deals...',
    linkStyle: {
      linkColor: '#6366f1',
      linkHoverColor: '#4f46e5',
      linkUnderline: true,
      linkHoverUnderline: true,
    },
  },
}

// Set at runtime
dragble.setBodyValues({
  backgroundColor: '#ffffff',
  contentWidth: '700px',
});

// Get current values
const bodyValues = await dragble.getBodyValues();

Tools Configuration

Built-in Tools

14 built-in content tools:

Tool KeyDescription
textText / paragraph (alias: paragraph)
headingHeading
buttonButton / CTA
imageImage
dividerHorizontal divider
menuNavigation menu
htmlRaw HTML
socialSocial media icons
videoVideo embed
tableTable
timerCountdown timer
formForm (AMP only in email mode)
spacerVertical spacer
paragraphAlias for text

ToolConfig

interface ToolConfig {
  enabled?: boolean;
  position?: number;                     // Sort order in tools panel
  properties?: Record<string, ToolPropertyConfig>;
}

interface ToolPropertyConfig {
  value?: unknown;                       // Default value override
  editable?: boolean;                    // Whether user can change this property
}

Example

options: {
  tools: {
    video: { enabled: false },
    form: { enabled: false },
    heading: {
      position: 1,
      properties: {
        text: { value: 'Default Heading Text' },
        fontSize: { value: '28px' },
        color: { value: '#111827' },
      },
    },
    button: {
      properties: {
        backgroundColor: { value: '#6366f1' },
        borderRadius: { value: '8px', editable: true },
      },
    },
    image: {
      properties: {
        alt: { value: '', editable: true },
      },
    },
  }
}

Runtime

dragble.setToolsConfig({
  video: { enabled: true },
  social: { enabled: false },
});

Popup Config

Popup configuration is only relevant when editorMode is 'popup'.

PopupConfig

interface PopupConfig {
  defaultWidth?: string;       // Default popup width
  defaultHeight?: string;      // Default popup height
  exportMode?: 'full' | 'partial';  // 'full' = complete HTML document, 'partial' = embeddable fragment
  popupId?: string;            // Custom popup ID for HTML element IDs
}

Example

dragble.init({
  containerId: 'editor',
  editorKey: 'ek_xxx',
  editorMode: 'popup',
  popup: {
    defaultWidth: '500px',
    defaultHeight: 'auto',
    exportMode: 'full',
    popupId: 'my-popup',
  },
});

getPopupValues

Retrieve the current popup settings from the design:

const popupValues = await dragble.getPopupValues();
// Returns: {
//   campaignType: 'lightbox',
//   position: { horizontal: 'center', vertical: 'center' },
//   width: '500px',
//   height: 'auto',
//   overlay: { enabled: true, color: 'rgba(0,0,0,0.5)', closeOnClick: true },
//   closeButton: { enabled: true, position: 'top-right', size: '24px', color: '#fff', ... },
//   animation: 'fade',
//   animationDuration: '300ms',
//   borderRadius: '12px',
//   boxShadow: '0 25px 50px rgba(0,0,0,0.25)',
//   padding: '20px',
//   border: { width: '0', style: 'none', color: '#000' },
//   displayDelay: 0,
//   contentAlign: 'center',
//   contentVerticalAlign: 'middle',
// }

Note: backgroundColor and backgroundImage are NOT part of popup values. They are managed through bodyValues, same as email/web mode.


Common Mistakes & Troubleshooting

1. Using accentColor with branded themes

// WRONG: accentColor is ignored with dragble-light / dragble-dark
appearance: { theme: 'dragble-light', accentColor: 'teal' }

// CORRECT: Use generic themes for custom accent
appearance: { theme: 'light', accentColor: 'teal' }

2. Missing user.id for collaboration

// WRONG: Collaboration won't attribute comments
features: { collaboration: true }

// CORRECT: Always set user.id
options: {
  user: { id: 'user-123', name: 'Jane' },
  features: { collaboration: true },
}

3. Calling methods before editor is ready

// WRONG: Editor may not be ready yet
dragble.init({ ... });
dragble.setMergeTags({ ... }); // May throw

// CORRECT: Use onReady callback
dragble.init({
  containerId: 'editor',
  editorKey: 'ek_xxx',
  callbacks: {
    onReady: () => {
      dragble.setMergeTags({ customMergeTags: [...] });
    },
  },
});

4. Confusing locale (UI) with language (template content)

  • locale controls the editor interface language (button labels, panel text, tooltips)
  • language controls the template content direction and language metadata
options: {
  locale: 'en-US',  // Editor UI in English
  language: { label: 'العربية', value: 'ar-SA', rtl: true }, // Template content in Arabic (RTL)
}

5. Using panels instead of sidePanel

panels is deprecated. Use sidePanel for all panel configuration:

// WRONG (deprecated)
appearance: { panels: { tools: { dock: 'left' } } }

// CORRECT
appearance: { sidePanel: { dock: 'left', width: 380 } }

6. Expecting backgroundColor in popup values

Popup background color and image are managed through bodyValues, not popupValues:

// Access popup background via design, not popup values
const design = await dragble.getDesign();
const bgColor = design.body.values.backgroundColor;

7. Feature flags vs plan permissions

Feature flags can only disable features, not grant them. If a feature requires a plan upgrade (e.g., image editor, dynamic image), setting features.imageEditor: true won't work unless the plan allows it. Setting features.imageEditor: false always disables it regardless of plan.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,629. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.