mcpField
U
unlayer-config

unlayer-config

github.com/unlayer开发14125 次安装4 周前更新
代码仓库

关于 unlayer-config

面向集成 Unlayer 编辑器的开发者配置技能,介绍编辑器选项、运行时方法及相关实现细节。内容涵盖外观、动态内容、文件上传、安全、语言本地化和验证。

主要功能

  1. 01配置编辑器功能与标签页
  2. 02设置主题和自定义字体
  3. 03添加合并标签与设计标签
  4. 04接入自定义图片上传
  5. 05生成服务端 HMAC 签名
  6. 06配置本地化与内容验证

适用场景

  1. 01定制嵌入式邮件编辑器
  2. 02展示个性化模板占位符
  3. 03将用户图片上传保存到后端
  4. 04验证并清理编辑器内容

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/unlayer/unlayer-skills --skill unlayer-config

安装后在 Claude Code 中输入/unlayer-config确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
feature-flags.md
file-storage.md
security.md

详细介绍

配置编辑器

概述

Unlayer 的行为由 unlayer.init() 选项和运行时方法控制。本技能涵盖功能、外观、动态内容、安全性和文件存储。

这也包括在构建器中的图像上打开的图像编辑器。对于独立的 Image Editor 和 @unlayer/react-image-editor,请使用 unlayer-image-editor。

密钥位置:

  • Project ID — Dashboard > Project > Settings
  • Project Secret(用于 HMAC)— Dashboard > Project > Settings > API Keys
  • Cloud API Key(用于图像/PDF 导出)— Dashboard > Project > Settings > API Keys

Dashboard:console.unlayer.com


功能标志

控制编辑器中可用的功能:

unlayer.init({
  features: {
    audit: true,             // Content validation
    preview: true,           // Preview button
    undoRedo: true,          // Undo/redo
    stockImages: true,       // Stock photo library
    userUploads: true,       // User upload tab
    preheaderText: true,     // Email preheader field
    textEditor: {
      spellChecker: true,
      tables: false,         // Tables inside text blocks
      cleanPaste: 'confirm', // true | false | 'basic' | 'confirm'
      emojis: true,
    },

    // Paid features:
    ai: true,                // AI text generation
    collaboration: false,    // Real-time collaboration
    sendTestEmail: false,    // Test email button
  },
});

有关所有标志(AI 子选项、图像编辑器、颜色选择器等),请参阅 references/feature-flags.md。


外观与主题

unlayer.init({
  appearance: {
    theme: 'modern_dark',    // 'modern_light' | 'modern_dark' | 'classic_light' | 'classic_dark'
    panels: {
      tools: {
        dock: 'right',       // 'left' | 'right' (default: 'right')
        collapsible: true,
      },
    },
    actionBar: {
      placement: 'top',     // 'top' | 'bottom' | 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'
    },
  },
});

// Change at runtime:
unlayer.setAppearance({ theme: 'modern_dark' });
// Or just the theme:
unlayer.setTheme('modern_dark');
自定义字体
unlayer.init({
  fonts: {
    showDefaultFonts: true,
    customFonts: [{
      label: 'Poppins',
      value: "'Poppins', sans-serif",
      url: 'https://fonts.googleapis.com/css?family=Poppins:400,700',
      weights: [400, 700],  // or [{ label: 'Regular', value: 400 }, { label: 'Bold', value: 700 }]
    }],
  },
});
选项卡配置
unlayer.init({
  tabs: {
    content: { enabled: true, position: 1 },
    blocks: { enabled: true, position: 2 },
    body: { enabled: true, position: 3 },
    images: { enabled: true, position: 4 },
    uploads: { enabled: true, position: 5 },
  },
});

合并标签

合并标签是发送时会被替换的占位符(例如,{{first_name}})。它们使用模板引擎的语法(Handlebars、Liquid、Jinja 等):

unlayer.setMergeTags({
  first_name: {
    name: 'First Name',
    value: '{{first_name}}',        // Your template syntax
    sample: 'John',                  // Shown in editor preview
  },
  last_name: {
    name: 'Last Name',
    value: '{{last_name}}',
    sample: 'Doe',
  },
  company: {
    name: 'Company',                 // Nested group
    mergeTags: {
      name: { name: 'Company Name', value: '{{company.name}}', sample: 'Acme Inc' },
      logo: { name: 'Logo URL', value: '{{company.logo}}' },
    },
  },
  products: {
    name: 'Products',
    rules: {
      repeat: {
        name: 'Repeat for Each Product',
        before: '{{#each products}}', // Loop start — syntax depends on your template engine
        after: '{{/each}}',           // Loop end
        sample: true,                  // Show sample data in editor
      },
    },
    mergeTags: {
      name: { name: 'Product Name', value: '{{this.name}}' },
      price: { name: 'Price', value: '{{this.price}}' },
      image: { name: 'Image URL', value: '{{this.image}}' },
    },
  },
});

// Autocomplete trigger (optional)
unlayer.setMergeTagsConfig({ autocompleteTriggerChar: '{{', sort: true });
设计标签(仅限编辑器的占位符)

设计标签会在编辑器 UI 中替换,但不会在导出内容中替换 — 适用于向模板作者展示个性化内容:

unlayer.setDesignTags({
  business_name: 'Acme Corp',
  current_user_name: 'Jane Smith',
});
unlayer.setDesignTagsConfig({ delimiter: ['{{', '}}'] });
显示条件(付费)

使用模板引擎的条件块包装内容:

unlayer.setDisplayConditions([
  {
    type: 'segment',
    label: 'VIP Customers',
    description: 'Only shown to VIP segment',
    before: '{% if customer.vip %}',   // Your template engine syntax
    after: '{% endif %}',
  },
  {
    type: 'segment',
    label: 'New Subscribers',
    description: 'First 30 days only',
    before: '{% if subscriber.age_days < 30 %}',
    after: '{% endif %}',
  },
]);
特殊链接

用户可以插入的预定义链接(取消订阅、偏好设置等):

unlayer.setSpecialLinks({
  unsubscribe: {
    name: 'Unsubscribe',
    href: '{{unsubscribe_url}}',
    target: '_blank',
  },
  preferences: {
    name: 'Preferences',
    specialLinks: {
      email_prefs: { name: 'Email Preferences', href: '{{preferences_url}}' },
      profile: { name: 'Profile Settings', href: '{{profile_url}}' },
    },
  },
});

HMAC 安全性

防止用户相互冒充。使用 Project Secret(Dashboard > Project > Settings > API Keys)在服务端生成 HMAC 签名:

Node.js:

const crypto = require('crypto');
const signature = crypto
  .createHmac('sha256', 'YOUR_PROJECT_SECRET')  // From Dashboard
  .update(String(userId))
  .digest('hex');

Python/Django:

import hmac, hashlib

signature = hmac.new(
    b'YOUR_PROJECT_SECRET',
    bytes(str(request.user.id), encoding='utf-8'),
    digestmod=hashlib.sha256
).hexdigest()

客户端 — 传入服务端生成的签名:

unlayer.init({
  user: {
    id: userId,                       // Must match what you signed
    signature: signatureFromServer,    // HMAC from your backend
    name: 'John Doe',                 // Optional
    email: '[email protected]',          // Optional
  },
});

Ruby 和 PHP 示例请参阅 references/security.md。


文件存储与图像上传

自定义上传(你的服务器)
unlayer.registerCallback('image', (file, done) => {
  const data = new FormData();
  data.append('file', file.attachments[0]);

  fetch('/api/uploads', { method: 'POST', body: data })
    .then((r) => {
      if (!r.ok) throw new Error('Upload failed');
      return r.json();
    })
    .then((result) => done({ progress: 100, url: result.url }))
    .catch((err) => console.error('Upload error:', err));
});

你的后端应返回:

{ "url": "https://your-cdn.com/images/uploaded-file.png" }
文件管理器(浏览已上传的图像)

初始化时需要设置 user.id — 图像按用户隔离:

unlayer.init({
  user: { id: 123 },                  // Required for file manager
  features: { userUploads: { enabled: true, search: true } },
});

unlayer.registerProvider('userUploads', (params, done) => {
  // params: { page, perPage, searchText }
  fetch(`/api/images?userId=123&page=${params.page}&perPage=${params.perPage}`)
    .then((r) => r.json())
    .then((data) => {
      done(
        data.items.map((img) => ({
          id: img.id,                  // Required
          location: img.url,           // Required — the image URL
          width: img.width,            // Optional but recommended
          height: img.height,          // Optional but recommended
          contentType: img.contentType, // Optional: 'image/png'
          source: 'user',              // Required: must be 'user'
        })),
        { hasMore: data.hasMore, page: params.page, total: data.total }
      );
    });
});

你的后端应返回:

{
  "items": [
    { "id": "img_1", "url": "https://...", "width": 800, "height": 600, "contentType": "image/png" }
  ],
  "hasMore": true,
  "total": 42
}

有关使用 XHR 显示上传进度、删除图像和设置 Amazon S3 的信息,请参阅 references/file-storage.md。


本地化

unlayer.init({
  locale: 'es-ES',
  textDirection: 'rtl',        // 'ltr' | 'rtl' | null
  translations: {
    es: { Save: 'Guardar', Cancel: 'Cancelar' },
  },
});

验证

// Global validator — runs on all content
unlayer.setValidator(async ({ html, design, defaultErrors }) => {
  return [...defaultErrors]; // Return modified error list
});

// Per-tool validator
unlayer.setToolValidator('text', async ({ html, defaultErrors }) => {
  return defaultErrors;
});

// Run audit on demand
unlayer.audit((result) => {
  // result: { status: 'FAIL' | 'PASS', errors: [{ id, icon, severity, title, description }] }
  if (result.status === 'FAIL') {
    console.log('Issues found:', result.errors);
  }
});

safeHtml(XSS 防护)

unlayer.init({
  safeHtml: true,   // Sanitize HTML via DOMPurify

  // Or with custom options:
  safeHtml: {
    domPurifyOptions: {
      FORCE_BODY: true,
    },
  },

  // WRONG: safeHTML (capital HTML) is DEPRECATED — use safeHtml
});

常见错误

错误修复方法
safeHTML(大写)使用 safeHtml(驼峰命名)— 旧写法已弃用
features.blocks = false 隐藏选项卡它会禁用 blocks,但不会隐藏选项卡 — 使用 tabs 配置
已弃用的 colorPicker.presets改用 colorPicker.colors(string[] 或 ColorGroup[])
文件管理器缺少 user.id文件管理器初始化时需要设置 user.id
Project Secret 暴露在前端绝不要将密钥放入客户端代码 — 在服务端生成 HMAC
合并标签语法不匹配与模板引擎保持一致:{{var}}(Handlebars)、${var}(JS)、{% %}(Jinja)

故障排查

问题修复方法
合并标签未显示检查是否在 editor:ready 后调用了 setMergeTags(),或是否在 init() 中传入
HMAC 签名被拒绝确保 user.id 与签名时完全一致,且密钥正确
文件管理器显示为空检查是否设置了 user.id、userUploads.enabled = true,以及 provider 是否返回正确格式
主题未生效初始化后使用 unlayer.setAppearance({ theme: 'modern_dark' }) 或 unlayer.setTheme('modern_dark')

付费功能

功能启用方式
自定义 CSS/JS在 init 中设置 customCSS、customJS
显示条件setDisplayConditions()
样式指南setStyleGuide()
导出图像/PDF/ZIP需要 Cloud API key
AI 功能features.ai
协作features.collaboration

Resources

评论

支持 Markdown