agentsclimarketplace

Dragble export

Skill Dragble/dragble-skills/dragble-export

Export content from the Dragble editor as HTML, JSON, plain text, image, PDF, or ZIP. Save and load designs, auto-save patterns, design JSON structure, and template validation.From its SKILL.md

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

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

15.3 KB, ~3.8k tokens by cl100k_base, as published. Nobody here has run it

Dragble Export & Design Management

Export content from the Dragble editor in 6 formats. Save, load, validate, and manipulate designs programmatically.

Overview

The Dragble editor supports 6 export formats split into two tiers:

TierFormatsRenderingPlan
Client-sideHTML, JSON, Plain TextBrowser-basedFree
Server-sideImage, PDF, ZIPChromium headlessPaid

Critical rule: Always save BOTH design JSON and HTML together. Design JSON is the editable source of truth; HTML is the rendered output. If you only save HTML, the design cannot be reloaded for editing.


Export Methods

All methods return Promises. There are no callback overloads.

exportHtml

Exports the current design as rendered HTML.

const html: string = await editor.exportHtml({
  title: "My Campaign",  // optional: sets <title> in the HTML
  minify: true            // optional: minifies the output HTML
});

Signature:

exportHtml(options?: { title?: string; minify?: boolean }): Promise<string>

exportJson

Exports the current design as a JSON object representing the full design tree.

const designJson: DesignJson = await editor.exportJson();

// Store the JSON for later reloading
await fetch("/api/designs/123", {
  method: "PUT",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(designJson),
});

Signature:

exportJson(): Promise<DesignJson>

exportPlainText

Exports the visible text content of the design, stripped of all HTML tags and formatting.

const text: string = await editor.exportPlainText();
console.log(text);
// "Hello World\nClick here to visit our site\n..."

Signature:

exportPlainText(): Promise<string>

exportImage

Renders the design as an image via server-side Chromium. Requires a paid plan. Does NOT work in popup mode.

const imageData: ExportImageData = await editor.exportImage({
  format: "png",            // "png" | "jpeg" | "webp"
  width: 600,               // pixel width of the output
  height: 0,                // 0 = auto-height based on content
  quality: 90,              // 1-100, applies to jpeg/webp
  deviceScaleFactor: 2,     // retina scaling (1 | 2 | 3)
  saveToStorage: true,      // upload result to project storage
  mergeTags: {              // resolve merge tags before rendering
    first_name: "Jane",
    company: "Acme Inc."
  }
});

// imageData.url  - public URL (if saveToStorage: true)
// imageData.data - base64-encoded image data

Signature:

exportImage(options?: {
  format?: "png" | "jpeg" | "webp";
  width?: number;
  height?: number;
  quality?: number;
  deviceScaleFactor?: number;
  saveToStorage?: boolean;
  mergeTags?: Record<string, string>;
}): Promise<ExportImageData>

Return type:

interface ExportImageData {
  url?: string;   // present when saveToStorage is true
  data: string;   // base64-encoded image
}

exportPdf

Renders the design as a PDF via server-side Chromium. Requires a paid plan. Does NOT work in popup mode.

const pdfData: ExportPdfData = await editor.exportPdf({
  pageSize: "A4",               // "A4" | "Letter" | "Legal" | custom
  orientation: "portrait",      // "portrait" | "landscape"
  printBackground: true,        // include background colors/images
  margin: {                     // page margins
    top: "10mm",
    right: "10mm",
    bottom: "10mm",
    left: "10mm"
  },
  width: 600,                   // content width in pixels
  saveToStorage: true,          // upload result to project storage
  mergeTags: {
    first_name: "Jane"
  }
});

// pdfData.url  - public URL (if saveToStorage: true)
// pdfData.data - base64-encoded PDF

Signature:

exportPdf(options?: {
  pageSize?: string;
  orientation?: "portrait" | "landscape";
  printBackground?: boolean;
  margin?: { top?: string; right?: string; bottom?: string; left?: string };
  width?: number;
  saveToStorage?: boolean;
  mergeTags?: Record<string, string>;
}): Promise<ExportPdfData>

Return type:

interface ExportPdfData {
  url?: string;   // present when saveToStorage is true
  data: string;   // base64-encoded PDF
}

exportZip

Bundles the design as a ZIP archive containing the HTML file and all referenced images. Requires a paid plan. Does NOT work in popup mode.

const zipData: ExportZipData = await editor.exportZip({
  imageFolder: "images"  // folder name for images inside the ZIP
});

// zipData.url  - public URL (if saved to storage)
// zipData.data - base64-encoded ZIP

Signature:

exportZip(options?: {
  imageFolder?: string;
}): Promise<ExportZipData>

Return type:

interface ExportZipData {
  url?: string;
  data: string;   // base64-encoded ZIP
}

Save & Load

Save Pattern

Always save both the design JSON (for re-editing) and the rendered HTML (for sending/displaying):

async function saveDesign(editor: DragbleEditor, designId: string) {
  // Export both in parallel
  const [html, designJson] = await Promise.all([
    editor.exportHtml({ minify: true }),
    editor.exportJson(),
  ]);

  // Persist to your backend
  await fetch(`/api/designs/${designId}`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      html,
      designJson,
      updatedAt: new Date().toISOString(),
    }),
  });
}

Load Pattern

Listen for the editor ready event, then load the design:

editor.addEventListener("editor:ready", () => {
  // Fetch the saved design JSON from your backend
  fetch(`/api/designs/${designId}`)
    .then((res) => res.json())
    .then((data) => {
      editor.loadDesign(data.designJson);
    });
});

loadDesign

Replaces the current design with the provided JSON. Does not validate the input.

editor.loadDesign(designJson);

Signature:

loadDesign(design: DesignJson): void

loadDesignAsync

Replaces the current design and resolves once the editor has finished rendering. Validates the design structure before loading.

try {
  await editor.loadDesignAsync(designJson);
  console.log("Design loaded and rendered");
} catch (err) {
  console.error("Invalid design JSON:", err);
}

Signature:

loadDesignAsync(design: DesignJson): Promise<void>

loadBlank

Resets the editor to an empty design.

editor.loadBlank();

Signature:

loadBlank(): void

getDesign

Returns the current design JSON synchronously from the editor's in-memory state.

const currentDesign: DesignJson = editor.getDesign();

Signature:

getDesign(): DesignJson

Auto-Save

Use a debounced listener on the design:updated event to auto-save without flooding your backend. A 2-second debounce is recommended.

let saveTimeout: ReturnType<typeof setTimeout> | null = null;

editor.addEventListener("design:updated", () => {
  // Clear any pending save
  if (saveTimeout) {
    clearTimeout(saveTimeout);
  }

  // Debounce: wait 2 seconds of inactivity before saving
  saveTimeout = setTimeout(async () => {
    try {
      const [html, designJson] = await Promise.all([
        editor.exportHtml({ minify: true }),
        editor.exportJson(),
      ]);

      await fetch(`/api/designs/${designId}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ html, designJson }),
      });

      console.log("Auto-saved successfully");
    } catch (err) {
      console.error("Auto-save failed:", err);
    }
  }, 2000);
});

Design JSON Structure

The design JSON follows a strict tree hierarchy:

DesignJson
├── body: BodyValues
│   ├── backgroundColor
│   ├── contentWidth
│   ├── fontFamily
│   ├── preheaderText
│   └── ...global styles
├── rows: RowValues[]
│   ├── columns: ColumnValues[]
│   │   ├── contents: ContentData[]
│   │   │   ├── type: string
│   │   │   └── values: { ...per-type properties }
│   │   └── ...column styles
│   └── ...row styles
└── counters: Record<string, number>

Content Types

TypeDescription
paragraphRich text block
headingHeading text (h1-h6)
buttonCall-to-action button
imageImage with optional link
dividerHorizontal rule / separator
menuNavigation link list
htmlRaw HTML block
socialSocial media icon links
videoEmbedded video player
tableData table
timerCountdown timer
formInput form
spacerVertical spacing block

Column Layouts

The columns array length and relative weight values define the layout:

columnsLayout
[1]100% (full width)
[1, 1]50% / 50%
[1, 2]33.3% / 66.7%
[2, 1]66.7% / 33.3%
[1, 1, 1]33.3% / 33.3% / 33.3%
[1, 2, 1]25% / 50% / 25%
[1, 1, 1, 1]25% / 25% / 25% / 25%

Minimal Valid Design JSON

{
  "body": {
    "rows": [
      {
        "columns": [
          {
            "contents": [
              {
                "type": "paragraph",
                "values": {
                  "text": "<p>Hello World</p>"
                }
              }
            ]
          }
        ],
        "values": {}
      }
    ],
    "values": {
      "backgroundColor": "#ffffff",
      "contentWidth": "600px",
      "fontFamily": {
        "label": "Arial",
        "value": "arial,helvetica,sans-serif"
      }
    }
  },
  "counters": {}
}

Validation & Audit

setValidator

Registers a global design validator that runs before export operations.

editor.setValidator((design: DesignJson) => {
  const errors: string[] = [];
  if (!design.body.values.preheaderText) {
    errors.push("Preheader text is required");
  }
  return { valid: errors.length === 0, errors };
});

Signature:

setValidator(
  validator: (design: DesignJson) => { valid: boolean; errors: string[] }
): void

setToolValidator

Registers a validator for a specific content type (tool). Runs when that tool's content is edited.

editor.setToolValidator("image", (values) => {
  const errors: string[] = [];
  if (!values.src || !values.alt) {
    errors.push("Images must have alt text");
  }
  return { valid: errors.length === 0, errors };
});

Signature:

setToolValidator(
  toolName: string,
  validator: (values: Record<string, unknown>) => { valid: boolean; errors: string[] }
): void

clearValidators

Removes all registered validators (both global and tool-specific).

editor.clearValidators();

Signature:

clearValidators(): void

audit

Runs all registered validators against the current design and returns the results.

const result: AuditResult = await editor.audit();

if (result.status === "fail") {
  console.error("Audit failed:", result.errors);
  // result.errors: Array<{ tool?: string; message: string }>
}

Signature:

audit(options?: { silent?: boolean }): Promise<AuditResult>

Return type:

interface AuditResult {
  status: "pass" | "fail";
  errors: Array<{ tool?: string; message: string }>;
}

validateTemplate

Validates a design JSON structure without loading it into the editor.

const result = await editor.validateTemplate(designJson);
if (!result.valid) {
  console.error("Template validation errors:", result.errors);
}

Signature:

validateTemplate(
  design: DesignJson
): Promise<{ valid: boolean; errors: string[] }>

validateTool

Validates values for a specific content type without loading them.

const result = await editor.validateTool("button", {
  text: "Click Me",
  href: ""
});
// result.valid === false, result.errors === ["Button must have a link URL"]

Signature:

validateTool(
  toolName: string,
  values: Record<string, unknown>
): Promise<{ valid: boolean; errors: string[] }>

Element Manipulation

selectElement

Selects an element in the editor by its unique ID, opening its property panel.

editor.selectElement("u_content_heading_1");

Signature:

selectElement(elementId: string): void

highlightElement

Highlights an element in the editor with a visual indicator. Pass null to clear the highlight.

// Highlight an element
editor.highlightElement("u_content_image_3");

// Clear highlight
editor.highlightElement(null);

Signature:

highlightElement(elementId: string | null): void

scrollToElement

Scrolls the editor viewport to bring the specified element into view.

editor.scrollToElement("u_row_5");

Signature:

scrollToElement(elementId: string): void

execCommand

Executes an editor command on the currently selected element.

editor.execCommand("bold");
editor.execCommand("fontSize", "18px");
editor.execCommand("foreColor", "#ff0000");

Signature:

execCommand(command: string, value?: string): void

Common Mistakes & Troubleshooting

Saving only HTML, not design JSON

Wrong:

const html = await editor.exportHtml();
await saveToBackend({ html });
// Design cannot be reloaded for editing!

Correct:

const [html, designJson] = await Promise.all([
  editor.exportHtml(),
  editor.exportJson(),
]);
await saveToBackend({ html, designJson });

Calling exportImage/exportPdf/exportZip on a free plan

These methods require a paid plan and server-side Chromium rendering. They will reject with an error on free plans:

try {
  const image = await editor.exportImage({ format: "png" });
} catch (err) {
  // "Export to image requires a paid plan"
}

Calling exportImage/exportPdf/exportZip in popup mode

Server-side export methods are not available when the editor is opened in popup mode. Use them only in the embedded (iframe) editor mode.

Loading a design before the editor is ready

Wrong:

const editor = new DragbleEditor("#container", config);
editor.loadDesign(designJson); // Editor not ready yet!

Correct:

const editor = new DragbleEditor("#container", config);
editor.addEventListener("editor:ready", () => {
  editor.loadDesign(designJson);
});

Auto-save firing too frequently

Without debouncing, design:updated fires on every keystroke and property change. Always debounce with at least a 2-second delay (see the Auto-Save section above).

Invalid design JSON structure

The design JSON must follow the exact tree hierarchy: body.rows[].columns[].contents[]. Missing any level will cause loadDesign to fail silently or produce a broken layout. Use loadDesignAsync to catch validation errors, or call validateTemplate before loading.

Forgetting to handle export errors

All export methods return Promises that can reject. Always wrap them in try/catch:

try {
  const html = await editor.exportHtml({ minify: true });
} catch (err) {
  console.error("Export failed:", err);
}

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.