@emailmaker/emailmaker
v1.1.103
Published
<p align="left"> <a href="https://emailmaker.ru/" target="blank"><img src="https://emcdn.ru/389992/240402_17_0uOwHpL.png" width="500" alt="EmailMaker" /></a> </p>
Readme
Установка
Установите пакет из npm:
npm install @emailmaker/emailmakerПолезные ссылки
- Каталог пакета на npm: @emailmaker/emailmaker
- Справка на сайте EmailMaker: Подключение редактора
Добавьте плагин сборки EmailMaker в конфигурацию приложения (он копирует статику пакета и задаёт __emailmaker__.publicPath для загрузки чанков). Без Webpack/Vite-плагина вручную нужно обеспечить корректный базовый путь к бандлу.
Плагины к редактору
Редактор расширяется плагинами: они добавляют панели, блоки и свои настройки. Плагин — отдельный
проект; заготовку делает CLI (npx @emailmaker/cli --kind plugin), а его сборка ведётся
через PluginDev из этого же пакета.
Главное, что стоит знать заранее: плагин и редактор обязаны работать на одном экземпляре React,
antd и ui-kit — иначе интерфейс падает при отрисовке панели плагина. PluginDev обеспечивает это
сам, а плагин сборки хоста предупредит, если в сборку попала вторая копия.
Плагины на глобалах при ESM-входе: compatGlobals
Плагин, собранный под глобалы (UMD-скрипт по адресу, дескриптор с type: 'umd' или
resolve: 'registry'), берёт antd, иконки и ui-kit из window.__emailmaker__.modules. В ESM-входе этот
слой совместимости по умолчанию выключен: он целиком везёт antd и каталог иконок, и платить за него
должен только тот, у кого такие плагины есть. Без слоя плагин падает с ошибкой
`antd` is not available: the compatibility layer for global plugins is not enabled; в её конце —
Requested by: <адрес> скрипта, который обратился к имени. modules.UiKit и modules.UiKitIcons без
слоя не бросают, а отдают undefined и пишут ту же причину в консоль один раз: прежние плагины
проверяли их наличием.
Включите слой одним из способов:
- опцией плагина сборщика —
compatGlobals: true(см. «Для сборщика Webpack» / «Для сборщика Vite»); - или явным импортом до создания редактора:
import '@emailmaker/emailmaker/compat-globals';.
Слой попадает в стартовый бандл. В Webpack опция подключает его к каждой точке входа, даже если редактор у вас грузится лениво; в Vite — вместе с модулем редактора. В UMD-сборке слой не нужен: глобалы там есть всегда.
Чеклист для агентов и автоматизации
Используйте таблицу при генерации кода интеграции или ревью. Источник типов — emailmaker.d.ts в пакете (ExportedApi); не подставляйте вымышленные поля API.
Подробнее о том, как в .d.ts сливаются ExportedApi.Options / Instance, что такое AppEvent и SaveEventPayload, и как читать исключения (handleSubjectChanged и др.), — в типах самого пакета: они лежат рядом с кодом и открываются подсказками IDE.
| Правило | Детали |
| --- | --- |
| init | Обязательно content. Сигнатура: init(options) → Promise<Instance>. |
| Namespace vs instance | У модуля emailmaker — в основном init (+ именованные ESM-экспорты). setConfig, show, save, destroy — у экземпляра после init. |
| Повтор на том же DOM | Повторный init на тот же узел отдаёт тот же объект и применяет опции, как setConfig. Если прежний ещё стартует — его промис отклоняется AbortError, успешен второй. destroy() через любую ссылку уничтожает редактор. Свой объект на вызов — опция ownership: 'counted'. |
| События | instance.handle*(…) обычно возвращает отписку. handleSubjectChanged / handlePreheaderChanged — другая сигнатура (см. типы). |
| Async | Колбэки и getAuthToken могут быть async; ошибки — через handleError или свою логику. |
| Сборка | Без штатного плагина Webpack/Vite задайте publicPath (__emailmaker__). Бандл крупный (React, Monaco и др.). |
| Авторизация | getAuthToken; client_secret только на сервере. |
| Файловый менеджер | storageProvider, config.imageCenterFileManagerView: true, при необходимости секция config.fileManager (ExportedApi.FileManagerOptions). Контракт StorageProvider объёмен — сверяйтесь с типами. |
| baseUrl | Origin API без суффикса /wp-json (напр. https://api.emailmaker.ru или https://app.emailmaker.ru/api); SDK добавляет /wp-json сам. |
| standaloneKey | Ключ замкнутого контура: файлы и внешние адреса идут через ваши обработчики, а не через наш бэкенд. Описан в документации по прокси ресурсов. |
| commonStyles | Опционально на init / reset: объект стилей контента (частичный merge с COMMON_STYLES). |
Готовый пример страницы-хоста создаёт CLI: npx @emailmaker/cli --kind demo-stand.
Для сборщика Webpack
Импортируйте класс и передайте его в plugins под тем же именем:
const EmailmakerWebpackPlugin = require('@emailmaker/emailmaker/webpack');
module.exports = {
plugins: [
new EmailmakerWebpackPlugin({
// По желанию: outputFolder, modulesPath, publicPath — см. пакет.
// compatGlobals: true — только если подключаете плагины на глобалах (см. выше).
}),
],
};Для сборщика Vite
Импорт только из scoped-пакета:
import { defineConfig } from 'vite';
import emailmakerVite from '@emailmaker/emailmaker/vite';
export default defineConfig({
plugins: [
emailmakerVite({
// По желанию: outputFolder, modulesPath, publicPath — см. пакет.
// compatGlobals: true — только если подключаете плагины на глобалах (см. выше).
}),
],
});__emailmaker__
Плагины Webpack/Vite добавляют в бандл объект __emailmaker__ = { publicPath: '...' } с базовым URL к чанкам пакета. Вручную менять его обычно не нужно; без штатного плагина задайте publicPath самостоятельно так, чтобы загрузчик нашёл собранные emailmaker.js / emailmaker-esm.js.
Имя класса Webpack-плагина строится из имени приложения в package.json (для @emailmaker/emailmaker это EmailmakerWebpackPlugin).
Инициализация
Импортируйте плагин в коде приложения:
import * as emailmaker from '@emailmaker/emailmaker';У init обязательное поле — content (HTML-строка или объект письма / шаблона). После успешной инициализации методы setConfig, updateConfig, show, save и др. доступны у экземпляра, возвращённого из init (не у объекта emailmaker).
Частые поля верхнего уровня — в таблице; детали конфигурации и содержимого см. ниже в разделе «Конфигурация».
| Поле | Назначение |
| --- | --- |
| getAuthToken | OAuth-колбэк: возвращает access token. |
| element | Контейнер монтирования (селектор, узел или фрагмент). |
| hidden | Работать в этом контейнере, но не показывать интерфейс (см. «Скрытый старт»). |
| content | Обязательно: HTML-строка или объект письма/шаблона. |
| config | Настройки UI редактора (ExportedApi.Config). |
| project | Идентификатор проекта в вашей системе / в EmailMaker. |
| baseUrl | Origin API без /wp-json (см. AppInitMixin.Options). |
| storageProvider | Реализация StorageProvider для файлового менеджера. |
| standaloneKey | Замкнутый контур: прокси файлов и адресов через instance.handleFileProxy / handleUrlProxy. Подробности — в документации по прокси ресурсов. |
| commonStyles | Объект стилей контента (частичный merge с COMMON_STYLES). |
getAuthToken— функция(user?, project?, prevToken?) => string | Promise<string>, возвращающая OAuth access token.client_secretполучайте только на сервере.
Именованные экспорты в ESM-сборке (emailmaker-esm.js):
import { init, parseAMP, COMMON_STYLES, version, helpers, errors } from '@emailmaker/emailmaker';| Экспорт | Назначение |
| --- | --- |
| init | То же, что emailmaker.init, с ленивой подгрузкой ядра (Promise<Instance>). |
| parseAMP | Разбор AMP: вход — строка или { code }, результат ParsedAMP (html / ampHtml и т.д.). |
| COMMON_STYLES | Дефолтный пресет стилей контента текущей сборки EmailMaker. Для init({ commonStyles: { ...COMMON_STYLES, … } }). Значения и их описания видны в типах пакета. |
| version | Строка версии сборки. |
| helpers | Вспомогательные утилиты (например, соответствие HTTP-статусов кодам ошибок). |
| errors | Ошибки API, в т.ч. errors.FileManagerApiError для контракта с файловым менеджером. |
Получение OAuth токена на бэкенде
curl --location 'https://api.emailmaker.ru/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=*******' \
--data-urlencode 'client_secret=*******' \
--data-urlencode 'grant_type=client_credentials'Пример клиентской функции: токен отдаёт ваш бэкенд (секрет не уходит в браузер):
async function getAuthToken(user, project, prevToken) {
const res = await fetch('/api/your-backend/emailmaker-token', { method: 'POST' });
const data = await res.json();
return data.access_token;
}Создание сущности EmailMaker plugin
Простейший вариант — HTML письма и тема:
emailmaker
.init({
getAuthToken,
element: '#test',
content: {
subject: 'Тема письма',
html: `КОД_ПИСЬМА`,
},
config: {},
})
.then((instance) => {});Вариант с шаблоном и внешним id письма:
emailmaker
.init({
getAuthToken,
element: '#editor',
project: 'ключ-проекта-в-вашей-системе',
content: {
templateId: 22304,
email: {
id: 'crm-email-id-123',
},
blocks: [],
},
config: {
theme: 'light',
// advancedConfigs: { editor: { … }, pro: { … } },
},
})
.then((instance) => {});КОД_ПИСЬМА можно взять из нашего образца или вставить HTML-код своего письма.
Объект редактора сразу: create()
init() возвращает редактор, только когда он готов и письмо из content уже показано. Обработчики
при этом срабатывают раньше — внутри init(), — и сослаться в них на редактор нельзя: переменная
ещё не присвоена. create() отдаёт объект редактора сразу, синхронно, а готовность — отдельным
промисом:
const editor = emailmaker.create({
getAuthToken,
element: '#editor',
content: { email },
handleEmailInit: async () => {
// Объект уже есть — к моменту события он присвоен.
const compiled = await editor.compileEmail(undefined, { amp: true });
await saveOnYourSide(compiled);
},
});
await editor.ready;Это обычный объект, как и результат init(): все методы — его собственные поля, их видят
Object.keys, spread и Object.assign, любой метод можно подменить своим. Объект можно сразу
положить в своё хранилище, подписаться на события или вызвать destroy().
Объект привязывается к редактору, как только тот собран, — ещё до показа письма из content: так
вызовы из обработчиков (как compileEmail() выше) доходят до редактора. После привязки все вызовы
идут прямо в редактор. До привязки объект ведёт себя так:
| что | до привязки |
| --- | --- |
| подписки handle* | запоминаются и цепляются до первого события — ничего не теряется |
| асинхронные методы (show, compileEmail, getEmailSnapshot, …) | ждут готовности и выполняются |
| setEmailMeta, setBlockMeta, notify | применяются при готовности, по порядку |
| getElement() | узел из опций, если он передан элементом; для селектора — undefined |
| getCodeErrors() | пустой список |
| getConfig(), use() | бросают понятную ошибку — дождитесь ready или используйте useAsync() |
| поля container, undoRedoService | undefined; чтение не бросает |
| destroy() | отменяет инициализацию, ready отвергается с AbortError |
Между привязкой и ready письмо может быть ещё не показано. Если нужен именно показанный документ —
например, снимок getEmailSnapshot(), — дождитесь ready.
Ошибка инициализации приходит отказом ready. Если объект создали, а ready не ждут, такая ошибка
всплывёт в консоли как необработанная — это намеренно: иначе о том, что редактор не поднялся, не
узнать. Исключение — destroy() до готовности: этот отказ ожидаемый и не шумит.
Повторный create() или init() в тот же узел описан ниже, в разделе
«Повторный init() в тот же узел».
await emailmaker.create(options).ready — это ровно то, что делает init(options): переходить с
одного на другое не обязательно.
React (обёртка)
В пакете есть готовый компонент: он сам создаёт контейнер, вызывает init при монтировании
и destroy при размонтировании.
import Emailmaker from '@emailmaker/emailmaker/react';
const content = { email: { id: 'new' }, templateId: 1 };
const editorConfig = { theme: 'light' };
const editorStyle = { height: '100vh' };
export function EmailmakerEditor({ project, getAuthToken }) {
return (
<Emailmaker
getAuthToken={getAuthToken}
project={project}
content={content}
config={editorConfig}
style={editorStyle}
/>
);
}Компонент принимает те же опции, что и init, плюс id, className, style и container
(остальные атрибуты контейнера). element передавать не нужно — компонент подставляет свой
div.
Два ограничения, о которых лучше знать заранее:
- Любое обновление props приводит к
resetредактора. Держите объекты и колбэки стабильными (useMemo/useCallback), иначе редактор будет пересоздаваться на каждый рендер родителя. - Ошибки инициализации компонент не пробрасывает и никак не показывает. Если
initпадает, вы увидите пустой контейнер — для диагностики вызовитеinitнапрямую.
Когда нужен полный контроль над жизненным циклом, обёртку пишут руками.
Минимальный каркас: монтирование, destroy при размонтировании.
import { useEffect, useRef } from 'react';
import * as emailmaker from '@emailmaker/emailmaker';
export function EmailmakerEditor({
project,
templateId,
getAuthToken,
}: {
project: string | number;
templateId: number;
getAuthToken: NonNullable<Parameters<typeof emailmaker.init>[0]['getAuthToken']>;
}) {
const rootRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let instance: Awaited<ReturnType<typeof emailmaker.init>> | undefined;
(async () => {
instance = await emailmaker.init({
getAuthToken,
element: rootRef.current ?? '#emailmaker-root',
project,
content: { email: { id: 'new' }, templateId },
config: { theme: 'light' },
});
})();
return () => {
instance?.destroy();
};
}, [project, templateId, getAuthToken]);
return <div id="emailmaker-root" ref={rootRef} style={{ height: '100vh' \}} />;
}Скрытый старт и разворот
Редактор можно поднять заранее и показать позже — без второго запуска.
// Заранее: письмо загружено, его можно читать, собирать и сохранять. Интерфейса нет.
await emailmaker.init({ element: '#editor', hidden: true, content, getAuthToken });
// Когда понадобилось показать — тот же вызов без флага.
await emailmaker.init({ element: '#editor', content, getAuthToken });Второй вызов не поднимает второй редактор: работающий экземпляр находится по контейнеру и разворачивается. Холст при этом не пересоздаётся, а состояние документа переживает переход.
Чем это отличается от показа новым init() в другой контейнер: там поднимается второй редактор со
своим холстом, а всё, что накопил первый и что вы не передали заново в content (например
метаданные письма), теряется вместе с ним.
Единственное требование к контейнеру: пока он скрыт, он должен оставаться в потоке рисования.
display: none останавливает раскладку всего, что внутри, и письмо будет не с чего измерить —
верстка уедет в мобильную. Нулевая высота, visibility: hidden или увод за экран подходят. Прятать
сам холст — наша забота, не ваша.
Что заменяется, а что сохраняется при развороте:
| | переживает разворот |
| --- | --- |
| экземпляр и его службы | да |
| холст (не мигает, не перезагружается) | да |
| метаданные и состояние документа | да |
| вёрстка письма | заменяется той, что передали в content |
Повторный init() в тот же узел
Как ведёт себя повторный init() или create() в тот же контейнер, задаёт опция ownership.
По умолчанию (ownership: 'single') — как в 1.1.89. У контейнера один объект редактора:
const first = await emailmaker.init({ element: '#editor', content });
const second = await emailmaker.init({ element: '#editor', content, config });
first === second; // true
second.destroy(); // уничтожает редактор — через любую ссылку- Повторный вызов применяет опции, как
setConfig. Обработчики из опций подменяются сразу: события, которые придут дальше, уходят в обработчики нового вызова. Подписки, сделанные через объект (editor.handleSaveEmail(cb)), живут — объект ведь тот же. - Письмо показывается заново, только если оно отличается от последнего, которое вы задали сами —
через
init,showилиreset.init(A)→show(B)→init(A)вернёт письмо A.init(A)→init(A)письмо не перерисует, и правки пользователя останутся. - Если вызов передаёт те же данные, что и прежний, — то же письмо, конфигурацию, пользователя и тот же
набор обработчиков, пусть даже новыми функциями, как при каждой перерисовке компонента, — опции
заново не применяются:
handleSetOptionsиhandleOptionsChangedне приходят, токен не перезапрашивается. - Вызов, сделанный, пока прежний ещё стартует, вытесняет его: промис прежнего отклоняется
AbortError(emailmaker.isSupersededInitError(error)), успешен только новый. Так ведёт себя и React StrictMode без отдельной уборки. destroy()через любую ссылку уничтожает редактор, аhandleDestroy, подписанный через объект, срабатывает.
ownership: 'counted' — свой объект на каждый вызов. Нужен, когда редактор в одном контейнере
делят несколько частей приложения: страница подняла его, следующая вошла в тот же контейнер, а уборка
прежней приходит позже — React размонтирует прежнюю страницу после того, как смонтировал новую, а
в StrictMode вызывает эффект дважды.
const first = emailmaker.create({ element: '#editor', content, ownership: 'counted' });
const second = emailmaker.create({ element: '#editor', content, ownership: 'counted' });
await Promise.all([first.ready, second.ready]); // оба успешны, редактор один
first.destroy(); // снимает только долю first и его подписки
second.destroy(); // последняя доля: редактор уничтожается чуть позже- Каждый вызов получает свой объект со всеми методами общего редактора и держит долю во владении.
Подписки объекта живут до его
destroy(). destroy()снимает долю этого объекта. Редактор уничтожается, когда долей не осталось, — с короткой отложкой, чтобы уборка и новый вызов подряд (StrictMode) его не пересоздавали.- Действуют опции и обработчики последнего вызова. Уходит его доля — обработчики из опций переходят к последнему оставшемуся владельцу; письмо и настройки остаются как есть.
emailmaker.getInstance(element)отдаёт объект последнего вызова.
Смешивать режимы можно. Вызов без флага держит редактор, пока не вызовут destroy() его объекта,
и получает общий объект контейнера. Доли counted живут поверх. destroy() объекта без флага
уничтожает редактор со всеми долями — их объекты дальше ведут себя как после destroy(). В режиме
разработки смешивание отмечается предупреждением в консоли.
Если повторный вызов не стартовал (упал токен или app_init, отменили через signal), отклоняется
только он: редактор остаётся с прежними опциями и прежним письмом, восстановленным из снимка (текст и
шапка; история отмен сбрасывается). Если вернуть не удалось, редактор уничтожается, а методы прежних
объектов бросают EditorStartFailedError с причиной в cause — поднимите редактор новым init().
Отмена signal прежнего вызова после того, как пришёл новый, редактор не трогает.
init() в другой контейнер поднимает новый редактор.
Конфигурация
Конфигурация задаётся объектом с несколькими уровнями вложенности: внешний вид редактора, стартовое содержимое и обработчики событий.
Повторный вызов init на тот же DOM-элемент отдаёт тот же объект и применяет новые опции — см. «Повторный init() в тот же узел».
element
Строка-селектор, Element или DocumentFragment: контейнер, куда монтируется редактор. Если элемент не задан, приложение может работать в фоновом режиме без видимого контейнера.
content
Секция content задаёт данные для редактора. Варианты:
{string}
HTML-разметка письма.
{object} параметры письма
{
id?: string; // Уникальный ID письма в вашей системе.
html?: string; // HTML письма.
subject?: string; // Тема письма.
preheader?: string; // Прехедер.
}{object} шаблон, блоки, комментарии
{
templateId?: string | number; // ID шаблона в EmailMaker.
email?: object; // параметры письма (см. выше).
blocks?: []; // блоки.
comments?: []; // комментарии к письму.
}Сменить контент после инициализации: instance.show(content).
Стили контента (commonStyles)
Не путать с config: объект commonStyles передаётся на верхнем уровне init / reset (тип ExportedApi.AppConfigMixin.Options).
Дефолт из сборки: именованный экспорт
COMMON_STYLES— состав полей и их описания приходят с типами пакета, IDE показывает их подсказкой.Частичное переопределение: можно передать только нужные ветки (например
{ block: { backgroundColor: '#000' } }) — приложение сливает их сCOMMON_STYLES, чтобы панель «Стили контента», iframe и список общих блоков не ломались на «дырах» в объекте.Приоритет: сохранённое
email.common_stylesважнее, чемinit.commonStyles.Сброс сохранённого в сторе плагина override:
commonStyles: undefinedвresetилиupdateConfig(с ключом).Вкладка в UI: флаг
config.commonStylesEnabled(частоtrueпо умолчанию).Структура: ветки по видам содержимого —
block,text,headings,buttons,linksи прочие; у каждой свои поля вроде цвета, шрифта и отступов. Точный состав виден в типеCommonStyles, он приезжает с пакетом.Пресет зависит от сборки: у EmailMaker — системные шрифты и акцент
#1C52DC, у UseBlocks — Google Fonts и свой акцент. Тот же объект служит базой, когда у письма нет сохранённых стилей.Блоки, вставленные в письмо, наследуют эти стили: превью блока в песочнице рисуется теми же значениями, что и письмо.
config
Настройки UI редактора. После init используйте instance.setConfig, instance.updateConfig и instance.getConfig — не методы объекта emailmaker.
Список параметров секции config
| Параметр | Описание | Значение по умолчанию |
| -------------------------------------- | --------------------------------------------------------------------------- | --------------------- |
| theme | Тема плагина: light, dark. | light |
| autosaveTimeout | Задержка перед автосохранением, мс. | 10000 |
| mergeTags | JSON со списком динамических переменных. | undefined |
| styles | Строка со стилями в формате CSS. | '' |
| projectStyles | Свои шрифты, цвета бренда и соцсети проекта, см. ниже. | undefined |
| | | |
| headerShow | Показ шапки. | true |
| headerArrowBackVisible | Показ стрелки "Назад". | true |
| headerTitleVisible | Показ поля "Тема". | true |
| headerPreheaderVisible | Показ поля "Прехедер". | true |
| historyEnabled | Показ кнопки "История версий". | true |
| codeEnabled | Показ кнопки "Code-mode". | true |
| codeModeDirection | Режим показа редактора кода по умолчанию | none|left|right|bottom|
| codeSideEnabled | Показ кнопки "Code-mode" в режиме, когда есть переключатель десктоп/мобайл. | true |
| previewIconEnabled | Показ кнопки "Предпросмотр". | true |
| previewModeEnabled | Ссылка на Предпросмотр. | true |
| livePreviewEnabled | Показ кнопки "Тестировние в почтовиках". | true |
| sendTestEnabled | Показ кнопки "Отправить тест". | true |
| sharePreviewEnabled | Показ кнопки "Поделиться". | true |
| webversionEnabled | Показ ссылки на веб-версию. | true |
| commonStylesEnabled | Показ стилей контента. | true |
| emailResponsiveControlEnabled | Показ переключателя адаптивности письма. | true |
| templateNotEmAlertShow | Показ уведомления о том, что шаблон не из EM. | true |
| | | |
| emailDropdownMenuEnabled | Показ меню письма. | true |
| emailDropdownMenuClone | Показ пункта "Клонировать". | true |
| emailDropdownMenuImport | Показ пункта "Импортировать". | true |
| emailDropdownMenuImportButton | Показ пункта "Импортировать" кнопкой | true |
| emailDropdownMenuLock | Показ пункта "Заблокировать". | true |
| emailDropdownMenuRemove | Показ пункта "Удалить". | true |
| | | |
| emailExportEnabled | Показ кнопки "Экспорт". | true |
| exportHtmlEnabled | Экспорт в HTML. | true |
| exportZipEnabled | Экспорт в ZIP. | true |
| exportClipboardEnabled | Экспорт в буфер. | true |
| exportWebHookEnabled | Экспорт в вебхук. | true |
| exportOutlookEnabled | Экспорт в Outlook. | true |
| exportPdfEnabled | Экспорт в PDF. | true |
| exportPngEnabled | Экспорт в PNG. | true |
| | | |
| nextButtonEnabled | Показ второстепенной кнопки. | false |
| nextButtonText | Текст на второстепенной кнопке. | '' |
| saveButtonEnabled | Показ основной кнопки. | true |
| saveButtonText | Текст основной кнопки. | '' |
| commandButtonsEnabled | Показ основной и второстепенной кнопки. | true |
| | | |
| sidePanelFloating | Плавающая боковая панель. | true |
| desktopMobileSwitchRule | Полоса переключения режимов. | true |
| desktopMobileSwitchPanel | Панель переключения режимов. | false |
| onlyCodeModeInMobile | вывод в мобиле только кода. | false |
| | | |
| pathEnabled | Показ хлебных крошек. | true |
| copyPasteEnabled | Показ копирования-вставки блока в iframe. | true |
| saveBlockEnabled | Показ кнопки сохранения блока. | true |
| editImageControlEnabled | Показ кнопки редактирования изображения. | true |
| | | |
| menuDirection | Расположение боковой панели. | 'right' |
| defaultBuildPanel | Настройка открывающейся вкладки по умолчанию. | ''/'empty'/'project'/'common'/'template' |
| commentsEnabled | Показ кнопки "Комментарии". | true |
| aiAssistentEnabled | Показ "AI ассистента". | true |
| optimizeEnabled | Показ кнопки "Оптимизация". | true |
| optimizeLinksEnabled | Показ проверки ссылок. | true |
| optimizeImagesEnabled | Показ проверки картинок. | true |
| optimizeSpamAssassinEnabled | Показ проверки SpamAssassin. | true |
| optimizeCheckCodeEnabled | Показ проверки кода. | true |
| settingsEnabled | Показ кнопки "Настройки". | true |
| settingsTabEnabled | Показ таба "Настройки". Табы UTM-метки и Аннотации от него не зависят. | true |
| UTMEnabled | Показ таба UTM-метки. | true |
| annotationsEnabled | Показ таба Аннотации Gmail. Включён только в режиме pro. | false |
| descriptionEnabled | Показ поля "Описание" в настройках. | true |
| tagsEnabled | Показ поля Теги в настройках. | true |
| switchDesktopMobilePropsEnabled | Показ переключателя мобильных свойств. | true |
| stepToOtherElementEnabled | Показ кнопок перехода к другим элементам. | true |
| deleteButtonEnabled | Показ кнопки удаления элемента. | true |
| toggleSidePanel | Возможность схлопывать боковую панель. | true |
| blocksSectionsShow | Показ меню блоков. | true |
| blocksEmptyEnabled | Показ пустых блоков. | true |
| blocksSavedEnabled | Показ сохранённых блоков. | true |
| blocksCommonEnabled | Показ общих блоков. | true |
| blocksTemplateEnabled | Показ блоков шаблона. | true |
| blocksSearchEnabled | Показ поиска по блокам. | true |
| blocksAddPanelEnabled | Показ панели добавления блока в визуальном редакторе. | true |
| imagePathDropdownEnabled | Показ выпадашки с путём к изображению. | true |
| | | |
| formFieldImageTitleEnabled | Показ поля title в форме картинки. | true |
| formFieldBorderEnabled | Показ полей изменения рамки. | true |
| formFieldBorderRadiusEnabled | Показ полей изменения скругления. | true |
| formFieldBoxShadowEnabled | Показ полей изменения тени. | true |
| formFieldVisibilityEnabled | Показ кнопок изменения видимости элемента. | true |
| formFieldExportAsPictureEnabled | Показ переключателя экспорт в картинку. | true |
| | | |
| formButtonFieldInsertImageEnabled | Показ кнопки вставки изображения в текст у кнопки. | true |
| formButtonFieldVerticalAlignEnabled | Показ кнопок вертикального выравнивания у кнопки. | true |
| formButtonFieldTextAlignEnabled | Показ кнопок выравнивания текста у кнопки. | true |
| formButtonFieldBackgroundImageEnabled | Показ полей фонового изображения у кнопки. | true |
| formButtonFieldPaddingsEnabled | Показ полей внутренних отступов у кнопки. | true |
| formButtonFieldReplacerEnabled | Показ реплейсера у кнопки. | true |
| | | |
| formCellFieldVerticalAlignEnabled | Показ кнопок вертикального выравнивания у ячейки. | true |
| formCellFieldBackgroundImageEnabled | Показ полей фонового изображения у ячейки. | true |
| formCellFieldPaddingsEnabled | Показ полей внутренних отступов у ячейки. | true |
| formCellFieldSizeEnabled | Показ полей ширины и высоты. | true |
| | | |
| formImageFieldVerticalAlignEnabled | Показ кнопок вертикального выравнивания у картинки. | true |
| formImageFieldReplacerEnabled | Показ кнопок вертикального выравнивания у картинки. | true |
| formImageFieldHeightEnabled | Показ поля высоты у картинки. | true |
| | | |
| formTableFieldBackgroundImageEnabled | Показ полей фонового изображения у таблицы. | true |
| formDivFieldBackgroundImageEnabled | Показ полей фонового изображения у контейнера. | true |
| formEmailFieldBackgroundImageEnabled | Показ полей фонового изображения у письма. | true |
| | | |
| formTextFieldTypografEnabled | Показ кнопки типографа. | true |
| formTextFieldReplacerEnabled | Показ кнопки реплейсера. | true |
| | | |
| elementImageEnabled | Элемент Картинка. | true |
| elementTextEnabled | Элемент Текст. | true |
| elementHeaderEnabled | Элемент Заголовок. | true |
| elementButtonEnabled | Элемент Кнопка. | true |
| elementListULEnabled | Элемент Список UL. | true |
| elementListOLEnabled | Элемент Список OL. | true |
| elementDividerEnabled | Элемент Разделитель. | true |
| elementSpacerEnabled | Элемент Отступ. | true |
| elementSocialEnabled | Элемент Соцсети | true |
| elementTableEnabled | Элемент Таблица. | true |
| elementCountdownEnabled | Элемент Счётчик. | true |
| elementVideoEnabled | Элемент Видео. | true |
| elementAIimageEnabled | Элемент AI картинка. | true |
| elementAItextEnabled | Элемент AI текст. | true |
| elementCarouselEnabled | Элемент AMP карусель. | true |
| elementAccordionEnabled | Элемент AMP аккордион. | true |
| elementFormEnabled | Элемент AMP форма. | true |
| | | |
| imageCenterLocalUploadEnabled | Поле загрузки файла с компьютера в Image Center. | true |
| deferEditorChrome | Строить кнопки шапки и правую панель сразу после первого кадра страницы: письмо появляется раньше, шапка и панель — мгновением позже. false — выключить, { header: false } или { panel: false } — выключить часть. | true |
| imageCenterStockEnabled | Таб Стока фотографий. | true |
| imageCenterGifEnabled | Таб Стока гифок. | true |
| imageCenterEditorEnabled | Таб редактора изображений. | true |
| compressMaxSize | Целевой вес картинки после клиентского сжатия, мегабайты (не байты). 0 выключает сжатие. По умолчанию 0.48. | см. типы |
| compressMaxWidthOrHeight | Макс. ширина/высота после ресайза, px. 0 выключает сжатие. По умолчанию 2000. | см. типы |
Свои шрифты (projectStyles.fonts)
Шрифты передаёт ваше приложение в конфиге, списком config.projectStyles.fonts. Загрузить файл шрифта из интерфейса плагина нельзя. Переданные шрифты появляются в выборе шрифта группой «Custom fonts», применяются на холсте и попадают в HTML письма.
Одна запись — один файл или одно начертание. Записи с одинаковым family собираются в одно семейство с несколькими начертаниями.
const instance = await emailmaker.init({
getAuthToken,
element: '#editor',
config: {
projectStyles: {
fonts: [
// Семейство тремя файлами: в списке одна строка, три начертания.
{ family: 'Brand', source_url: 'https://cdn.example.com/brand-400.woff2', category: 'serif' },
{ family: 'Brand', source_url: 'https://cdn.example.com/brand-700.woff2', weight: '700' },
{ family: 'Brand', source_url: 'https://cdn.example.com/brand-400i.woff2', style: 'italic' },
// Google Fonts: начертания берутся из адреса.
{ family: 'Inter', source_url: 'https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400;0,700;1,400' },
// Своя таблица стилей: одна запись — одно начертание.
{ family: 'Accent', source_url: 'https://cdn.example.com/accent/fonts.css' },
],
},
},
});| Поле | Обязательное | Описание |
| ------------ | ------------ | -------- |
| family | да | Имя семейства для CSS font-family. По нему же подписана строка в выборе шрифта. |
| source_url | да | Адрес файла шрифта (.woff2, .woff, .ttf, .otf), таблицы стилей Google Fonts или своей таблицы стилей (.css). |
| weight | нет | CSS font-weight файла. По умолчанию 400. У Google Fonts веса из адреса важнее этого поля. |
| style | нет | normal, italic или oblique. По умолчанию normal. |
| category | нет | sans-serif или serif. По умолчанию sans-serif. Тип семейства задаёт его первая запись. |
| name | нет | Ваше название записи. В выборе шрифта не показывается. |
| id | нет | Ваш идентификатор записи. Редактор его только хранит. |
Как шрифт попадает в письмо.
- Файл шрифта подключается правилом
@font-face, по одному на каждое начертание. - Таблица стилей, Google Fonts или своя, подключается тегом
<link>. - Тексту редактор ставит инлайном безопасный шрифт:
Helvetica, Arial, sans-serif, а дляcategory: 'serif'—"Times New Roman", Times, serif. Сам шрифт задаёт классem-font-*. Почтовый клиент без веб-шрифтов покажет безопасный шрифт. - Если шрифтом больше не размечен ни один элемент, его
@font-faceубирается из письма.
Требования к адресу.
- Нужен
https://. Адресаhttp:,data:и//hostотклоняются. - Относительный путь (
/fonts/brand.woff2) редактор достраивает до абсолютного от адреса вашей страницы. Так шрифт откроется и у получателя письма. - Если файл лежит на другом домене, сервер должен отдавать заголовок
Access-Control-Allow-Origin. Без него браузер не применит шрифт, и на холсте будет запасной. - Файл должен открываться без авторизации: его скачивает почтовый клиент получателя.
Что ещё нужно знать.
- Число шрифтов не ограничено.
- Если две записи совпадают по
family,weightиstyle, используется первая. - Запись с ошибкой пропускается, остальные применяются. Причина пишется в консоль с префиксом
[emailmaker] projectStyles:. - В плагине шрифты доступны только для чтения: изменить их из интерфейса нельзя.
- Не все почтовые клиенты показывают веб-шрифты. Gmail и Outlook для Windows покажут безопасный шрифт, поэтому проверяйте вёрстку и с ним.
Сменить список на лету: instance.updateConfig({ projectStyles }). Раздел передаётся целиком: он заменяет прежний, а не дополняет его.
В том же разделе передаются цвета бренда (colors, до 32) и соцсети проекта (socials, до 20). Полное описание полей — в типах ExportedApi.ProjectStyles.
Обработка событий
Обработчики передаются в init({ ... }) или подписываются на экземпляре: instance.handle*(listener, priority?). Подписка обычно возвращает функцию отписки. Обработчики могут быть async.
События handleSaveEmail, handleSaveBlock, handleSaveComment получают не сырой объект, а SaveEventPayload: { patch, value, prev?, source? } — полезно сравнивать patch и итоговое value.
handleSubjectChanged и handlePreheaderChanged оформлены иначе: через них подписываются на строковые значения темы и прехедера — см. emailmaker.d.ts.
Пример
emailmaker.init({
element: '#test',
content: { email: { id: '1' } },
getAuthToken,
handleReadEmail: async (id) => fetchEmail(id),
handleSaveEmail: async (payload) => saveEmail(payload.value),
}).then((instance) => {
const unsubscribe = instance.handleEmailChanged(async () => {
/* ... */
});
/* unsubscribe(); */
});События
| Обработчик | Описание |
| ---------- | -------- |
| handleReadEmail | Загрузка письма по id с вашего API (аргумент — строка id). |
| handleSaveEmail | Сохранение письма; полезная нагрузка — SaveEventPayload с value и patch. |
| handleRemoveEmail | Удаление письма. |
| handleReadEmailRevisions | Список автосохранений / ревизий для письма. Автосохранение в нём отмечено autosave: true. |
| handleReadEmailAutosave | Необязательно: последнее автосохранение письма (EmailRevision) или null, если его нет. Заведён — редактор восстанавливает несохранённую работу из него; нет — ищет в handleReadEmailRevisions свежайшую запись с autosave: true. Восстанавливается только снимок новее modified письма. |
| handleEmailAutosave | Создано автосохранение (EmailRevision с autosave: true). Храните его как есть. |
| handleRemoveEmailAutoSave | Удалено автосохранение. Сейчас редактор это событие не вызывает. |
| handleReadBlocks | Список блоков проекта. |
| handleReadCommonBlocks | Общие блоки. |
| handleReadTemplateBlocks | Блоки шаблона в контексте письма. |
| handleSaveBlock | Сохранение блока (SaveEventPayload). |
| handleRemoveBlock | Удаление блока. |
| handleReadComments | Комментарии к письму. |
| handleSaveComment | Сохранение комментария. |
| handleRemoveComment | Удаление комментария. |
| handleSaveButtonClick | Клик по основной кнопке (например «Сохранить»). |
| handleNextButtonClick | Клик по «Далее». |
| handlePreviousButtonClick | Клик «Назад» в шапке. |
| handleEmailInit | Письмо показано на холсте. Приходит при старте и на каждый show(), который меняет показанное: другое письмо; письмо без id (каждый такой показ — новый черновик); то же письмо, у которого отличается хотя бы одно из переданных полей — код, тема, прехедер, общие стили, — даже если холст при этом не перезаливается; show({ reload: true }). Не приходит, если show() с тем же id передаёт ровно то, что уже показано (непереданные поля не сравниваются), и на внутреннюю перезаливку холста. Если письмо правили в редакторе, повтор show() с прежним кодом снова нальёт его и пришлёт событие. Однократные действия («сохранить при первом открытии») на нём не стройте. |
| handleEmailChanged | Изменился параметр письма. |
| handleHtmlChanged | Изменился HTML-код. |
| handleSubjectChanged | Изменилась тема (особая подпись — см. типы). |
| handlePreheaderChanged | Изменился прехедер (особая подпись — см. типы). |
| handleCloneEmail | Клонирование письма. |
| handleShareEmail | Поделиться. |
| handleLivePreviewEmail | Живой предпросмотр. |
| handlePreviewEmail | Предпросмотр. |
| handleValidate | Результат проверки кода (CodeError[]). |
| handleTestEmailSend | Отправка тестового письма. |
| handleTestEmailModal | Открыто окно тестовой отправки. |
| handleDestroy | Перед уничтожением экземпляра. |
| handleLoad | Завершена загрузка экземпляра; можно безопасно вызывать getEmail и др. |
| handleLoadImage | Загрузка изображения пользователем; handleFileProxy, handleUrlProxy — прокси файлов/URL при необходимости. Вернуть URL; исключение из обработчика = отказ (редактор не грузит картинку на свой бэкенд), undefined — редактор дозагружает сам, кроме замкнутого контура (standaloneKey). |
| handleNotify | Произвольное уведомление. Среди предупреждений (type: 'warning') — TEMPLATE_READ_ERROR (data: { templateId }): шаблон по templateId не прочитался, письмо открыто без него. Приходит один раз на шаблон за жизнь редактора, в handleError не попадает; заголовка у события нет и всплывающего сообщения редактор не показывает — покажите своё, если пользователю нужно знать. |
| handleError | Ошибка (можно переопределить UI). |
| handleSuccess | Успешная операция. |
| handleReadLocale | Язык интерфейса (AppConfigMixin). Каталог возвращается текстом .po, JSON-строкой или готовым объектом LocaleData. |
| handleTranslate | Пользовательский перевод строк. |
| handleLimitUsage | Отображение квот / лимитов. |
Полный перечень и сигнатуры — в emailmaker.d.ts (ExportedApi.DataMixin.Events и смежные миксины).
Восстановление автосохранения
- Восстановленное содержимое редактор считает текущим: событий правки (
handleEmailChanged,handleHtmlChanged) не будет, пока пользователь не изменит письмо. Автосохранение при этом остаётся у вас, поэтому при следующем открытии письмо восстановится снова. - Удалять автосохранение после обычного сохранения редактор пока не просит:
handleRemoveEmailAutoSaveне вызывается.
Методы
Модуль экспортирует init (и именованные символы в ESM). Остальное — методы экземпляра после await emailmaker.init(...).
| Метод | Описание |
| ----- | -------- |
| init | Асинхронная инициализация: Promise<Instance>. |
| show | Подмена контента (строка или объект Content). |
| save | Сохранить текущее письмо по цепочке приложения. |
| getEmailSnapshot | Поля открытого письма без сборки (Promise<Email>): те же значения, что у getEmail, минус compiled. |
| getEmail | Текущее письмо вместе со сборкой HTML/AMP/текста. Нужны только поля — зовите getEmailSnapshot. |
| compileEmail | Явная компиляция HTML/AMP/текста; опционально email и флаги форматов. |
| setEmailMeta | Обновить meta текущего письма без полной замены. |
| setBlockMeta | Обновить meta блока в кэше blocks DataSource по blockId. |
| getBlocks | Блоки в текущем контексте (Block.meta round-trip при own-property). |
| getComments | Комментарии. |
| getEmailRevisions | Ревизии / автосохранения. |
| getCodeErrors | Устарел. Ошибки линтера кода; отвечает только при открытом редакторе. |
| validateCode | Проверить код открытого письма по требованию (Promise<CodeError[]>), без редактора. |
| getConfig | Текущий config. |
| setConfig | Полная замена настроек UI. |
| updateConfig | Глубокое слияние с текущим config. |
| notify | Показать уведомление (строка или объект). |
| getElement | DOM-узел или фрагмент монтирования. |
| destroy | Уничтожить экземпляр (вызывайте при размонтировании в SPA). |
| reset | Переинициализация с новыми Options (Promise<Instance>). |
| undoRedoService | Сервис отмены/повтора (undo, redo, снимки истории и др.). |
📂 Файловый менеджер
Для включения файлового менеджера необходимо в конфигурации инициализации указать параметры:
emailmaker.init({
storageProvider: ..., // реализация интерфейса StorageProvider
config: {
imageCenterFileManagerView: true
}
});Секция fileManager управляет возможностями файлового менеджера. Все поля необязательные; незаданное поле берёт своё умолчание (у флагов enable* это true), отключает только явный false.
В секции можно задать и compressMaxSize с compressMaxWidthOrHeight — те же поля, что в корне конфига. Заданные здесь, они перекрывают корневые, но только внутри файлового менеджера: картинка, брошенная прямо на холст, загруженная через модалку или пришедшая с импортом HTML, по-прежнему сжимается по корневому значению.
Basic Operations
- enableTrash — включает функциональность корзины для удаленных файлов. При включении файлы перемещаются в корзину вместо полного удаления. По умолчанию true
- enableRenameFolder — разрешает переименование папок. По умолчанию true
- enableRenameFile — разрешает переименование файлов. По умолчанию true
- enableUpdateFile — разрешает обновление содержимого или метаданных файлов. По умолчанию true
- enableEdit — разрешает редактирование файлов (изменение содержимого или свойств). По умолчанию true
- enableMoveFile — разрешает перемещение файлов между папками. По умолчанию true
- enableMoveFolder — разрешает перемещение папок. По умолчанию true
- enableCopyFile — разрешает копирование файлов. По умолчанию true
- enableCopyFolder — разрешает копирование папок. По умолчанию true
- enableDelete — разрешает удаление файлов и папок. При включенной корзине файлы перемещаются в корзину. По умолчанию true
- multiSelect — разрешает множественный выбор файлов и папок для групповых операций. По умолчанию true
- defaultFolderName — Имя папки по умолчанию
- availableImageExtensions — расширения в подсказке «Supported files» под областью загрузки пустой папки; выводятся с точкой (
['jpg', 'png']→.jpg, .png). Только подпись: что можно загрузить, поле не ограничивает. По умолчанию['jpeg', 'svg', 'png'] - dedupeUploadNamesOnClient — клиент сам подбирает свободное имя перед загрузкой: занятое
photo.pngуйдёт какphoto_1.png, потомphoto_2.png. Сюда же попадают копии из редактора изображений, загрузка по URL, копирование и перемещение; создать папку с занятым именем не даст — покажет ошибку. Стоит лишнего запроса списка папки перед каждой загрузкой, поэтому включать имеет смысл там, где коллизии имён не разруливает бэкенд. По умолчанию false - saveCopyToCurrentFolder — «Сохранить копию» в редакторе изображений кладёт файл в текущую папку. По умолчанию (false) копия уходит в папку по умолчанию, независимо от того, где открыт редактор
- showNotifications — всплывающие уведомления файлового менеджера. По умолчанию true
- sortOptions — белый список значений сортировки в дропдауне списка файлов (
'date-desc' \| 'date-asc' \| 'size-desc' \| 'size-asc' \| 'name-asc' \| 'name-desc' \| 'type-asc'). Если не задан или массив пустой — доступны все варианты, которые отображает file-manager. Пример:['date-desc', 'name-asc']— скрыть сортировки по размеру и типу, если платформа их не реализует.
Pagination & Display
- enablePagination — разрешает постраничную навигацию. Если отключено, все элементы загружаются сразу. По умолчанию true
- unifiedCatalogFetch — работает только вместе с
enablePagination: false. Запросы на список папок и список файлов сливаются в один запрос всего каталога, дальше нужный тип отбирается на клиенте: один вызовgetItemsвместо двух. При включённой серверной пагинации поле игнорируется. Ставьтеfalse, если бэкенду важно получать раздельные запросы на папки и файлы. По умолчанию true - expandRootFolderOnInit — раскрывает корневой узел дерева папок при открытии; вложенные папки остаются свёрнутыми. По умолчанию true
- previewFallbackToFileUrl — если у файла нет
thumbnailиthumbnailUrl, для превью в таблице и сетке берётсяfile.url(только изображения). По умолчанию true
Client-Side Processing
clientSideSorting — клиентская сортировка файлов.
- Если
true, применяется стандартная сортировка (по имени по возрастанию). - Если указана функция, она принимает текущий массив файлов, необязательные
sortByиsortOrder, и возвращает отсортированный массив.
- Если
clientSidePaging — клиентская пагинация файлов.
- Если
true, применяется стандартная нарезка массива поpageиlimit. - Если указана функция, она принимает массив файлов,
pageиlimit, и возвращает массив только для нужной страницы.
- Если
clientSideFilter — клиентская фильтрация файлов.
- Если
true, применяется стандартная фильтрация по текстуsearch. - Если указана функция, она принимает массив файлов и необязательный
search, и возвращает отфильтрованный массив.
- Если
Cache Strategy
- cacheStrategy — стратегия кэширования для оптимизации производительности.
- ttl — время жизни кэша в миллисекундах. По умолчанию 1800000 (30 минут)
- errorTtl — время жизни кэша для ошибок в миллисекундах. Если не задан, равен
ttl - allowSortedInsert — разрешает вставку элемента в отсортированный массив без полной инвалидации кэша. По умолчанию true
- allowSearchInsert — разрешает вставку элемента в массив с фильтрами без полной инвалидации кэша. По умолчанию true
- invalidateOnPaginationFull — если страница полная, кэш помечается как устаревший при вставке нового элемента. По умолчанию true
- comparator — функция сравнения двух файлов для сортировки. По умолчанию сравнение по имени
- filterMatch — функция проверки, проходит ли файл текущий фильтр. По умолчанию поиск по имени
Интерфейс StorageProvider
Свойства
| Свойство | Тип | Описание | По умолчанию |
| --------------------------- | --------- | ----------------------------------------------------------- | ------------ |
| pathMode | boolean | Использовать путь как идентификатор для папок | - |
| rootId | string | Пользовательский идентификатор корневой папки | '' |
| trashPath | string | Путь к корзине/папке для удаленных файлов | - |
| tempPath | string | Путь к временной папке для загрузок | - |
| useSoftDelete | boolean | Включить программное мягкое удаление | false |
| ensureFolderTrailingSlash | boolean | Добавлять к путям папок завершающий слэш | true |
| validateDuplicates | boolean | Проверка на существование при операциях с файлами и папками | - |
Методы
Основные методы
| Метод | Параметры | Возвращает | Описание |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| getItems | options:- folderId?: string- search?: string- sortBy?: string- sortOrder?: 'asc'\|'desc'- page?: number- limit?: number- itemType: 'all'\|'file'\|'folder' | { items: FileSystemItem[], pagination?: Partial<ApiPaginationResponse> } | Получить список файлов и папок с фильтрацией, сортировкой, пагинацией |
| createFolder | name: string, parentid?: string | FileSystemItem | Создать новую папку |
| uploadFile |
