@getdraft/plugin 1.13.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "@letty/eslint-config-ts"
3
+ }
package/README.md ADDED
@@ -0,0 +1,312 @@
1
+ # Draft Plugin
2
+
3
+ Документация по установке плагина в ваше приложение
4
+
5
+ ## Установка
6
+
7
+ ### NPM-пакет
8
+
9
+ Установить пакет для развертывания плагина в своем приложении можно используя npm-пакет
10
+
11
+ #### npm
12
+
13
+ ```
14
+ npm i @letty-email/plugin -E
15
+ ```
16
+
17
+ #### yarn
18
+
19
+ ```
20
+ yarn add @letty-email/plugin --exact
21
+ ```
22
+
23
+ ### HTML-script
24
+
25
+ _**Coming soon**_
26
+
27
+ ## Инициализация
28
+
29
+ ### **Шаг 1. Импортируйте плагин**
30
+
31
+ Если используете npm-пакет
32
+
33
+ ```javascript
34
+ import DraftPlugin from '@letty-email/plugin';
35
+ ```
36
+
37
+ в случае встраивания плагина напрямую в html используйте объект `window`
38
+
39
+ ```javascript
40
+ const DraftPlugin = window.DraftPlugin;
41
+ ```
42
+
43
+ ### **Шаг 2. Создание контейнера**
44
+
45
+ Чтобы редактор отобразился на вашей странице необходимо создать div-элемент с id `letty-plugin-container`
46
+
47
+ ```html
48
+ <div id="letty-plugin-container"></div>
49
+ ```
50
+
51
+ ### **Шаг 3. Аутентификация**
52
+
53
+ Endpoint URL: `https://api.kvilla.ru`
54
+
55
+ - **Основной пользователь**
56
+
57
+ - **Первый вход**
58
+
59
+ 1. Зарегистрируйте пользователя через `POST` `v1/register`
60
+
61
+ ```JSON
62
+ {
63
+ "email": "example@example.ru",
64
+ "password": "string",
65
+ }
66
+ ```
67
+
68
+ 2. Авторизуйтесь через `POST` `v1/login`
69
+
70
+ ```JSON
71
+ {
72
+ "email": "example@example.ru",
73
+ "password": "string",
74
+ }
75
+ ```
76
+
77
+ и получите сессионную куку.
78
+
79
+ 3. Создайте организацию через `POST` `v1/organizations` с параметрами:
80
+
81
+ ```JSON
82
+ {
83
+ "name": "string",
84
+ "plan_id": 1,
85
+ "with_default_project": true
86
+ }
87
+ ```
88
+
89
+ в Headers нужно выставить Cookie = полученная сессия из прошлого шага.
90
+
91
+ 4. Создайте постоянный токен (API key) для организации: `POST` `/v1/organizations/{organizationID}/tokens`.
92
+
93
+ ```JSON
94
+ {
95
+ "name": "string",
96
+ "expires_at": "2025-08-26T00:16:54.409Z"
97
+ }
98
+ ```
99
+
100
+ 5. Сохраните API key у себя на бэкенде.
101
+ 6. Передайте ключ в редактор.
102
+
103
+ - **Саблогин**
104
+
105
+ - **Первый вход**
106
+ 1. Если основной логин ещё не создан или не заходил — выполните шаги 1–5 для основного пользователя.
107
+ 2. Зарегистрируйте саблогин проделав шаги 1-2 для саблогина.
108
+ 3. Под авторизацией основного пользователя добавьте саблогин в организацию основного пользователя аккаунта: `POST` `/v1/organizations/{organizationID}/users`.
109
+ ```JSON
110
+ {
111
+ "user_id": "ID саблогина",
112
+ "role_id": 1
113
+ }
114
+ ```
115
+ 4. Создайте токен для пользователя: `POST` `/v1/organizations/{organizationID}/users/{userID}/tokens`.
116
+ ```JSON
117
+ {
118
+ "name": "string",
119
+ "expires_at": "2025-08-26T00:16:54.409Z"
120
+ }
121
+ ```
122
+ 5. Сохраните токен у себя бэкенде и передавайте его в редактор.
123
+
124
+ - **Повторный вход**
125
+
126
+ 1. Извлеките сохранённый API key.
127
+ 2. Передайте ключ в редактор.
128
+
129
+ ### **Шаг 4. Создание плагина**
130
+
131
+ Инициализируйте редактор в JavaScript-коде:
132
+
133
+ ```javascript
134
+ import DraftPlugin from '@letty-email/plugin'
135
+
136
+ const letty = DraftPlugin.create({
137
+ token: "полученный токен авторизации",
138
+ pluginId: "ID созданного плагина из ЛК",
139
+ locale: "en" либо "ru",
140
+ });
141
+ ```
142
+
143
+ ### **Шаг 5. Запуск редактора**
144
+
145
+ ```javascript
146
+ letty.start({
147
+ template: {}, // JSON-письма
148
+ uid: 'Уникальный ID письма в вашей системе',
149
+ templateName?: 'Название письма(если используете встроенную шапку редактора)'
150
+ });
151
+ ```
152
+
153
+ ## Параметры плагина
154
+
155
+ ### **token**
156
+
157
+ Токен авторизации текущего клиента, который будет использоваться для получения и сохранения блоков, писем и всего, что с ними связано
158
+
159
+ ### **pluginId**
160
+
161
+ Идентификатор текущей инсталляции плагина, которую вы создали и настроили в ЛК
162
+
163
+ ### **on**
164
+
165
+ Объект содержащий обработчики событий редактора.
166
+
167
+ | Событие | Описание | Возвращаемые параметры |
168
+ | --------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
169
+ | `ready` | Редактор загрузился и готов к работе | <code>{<br>&nbsp;json: TemplateJSON;<br>&nbsp;html: string;<br>&nbsp;lastSavedTemplate: TemplateJSON;<br>&nbsp;}</code> |
170
+ | `error` | Произошла ошибка | <code>{<br>&nbsp;message: string;<br>&nbsp;details: object\|string;<br>&nbsp;}</code> |
171
+ | `notification` | Уведомление от редактора | <code>{<br>&nbsp;intent: 'error' \| 'info' \| 'success' \| 'warning';<br>&nbsp;errorDetails?: object;<br>&nbsp;message: string;<br>&nbsp;}</code> |
172
+ | `autosave` | Произошло автосохранение | <code>{<br>&nbsp;json: TemplateJSON;<br>&nbsp;}</code> |
173
+ | `save` | Произошло сохранение<br>_(Вызывается, в случае если используется внутренняя шапка редактора)_ | <code>{<br>&nbsp;json: TemplateJSON;<br>&nbsp;}</code> |
174
+ | `exit` | Попытка выхода из редактора<br>_(Вызывается, в случае если используется внутренняя шапка редактора)_ | <code>{<br>&nbsp;json: TemplateJSON;<br>&nbsp;}</code> |
175
+ | `toggleGrid` | Включена подсветка сетки | <code>{<br>&nbsp;state: boolean;<br>&nbsp;}</code> |
176
+ | `togglePreview` | Включен предпросмотр | <code>{<br>&nbsp;state: boolean;<br>&nbsp;json: TemplateJSON;<br>&nbsp;html: string;<br>&nbsp;darkHTML: string;<br>&nbsp;}</code> |
177
+ | `toggleMenu` | Включено меню | <code>{<br>&nbsp;menu: [EditorMenu](#EditorMenu) \| null;<br>&nbsp;}</code> |
178
+ | `history` | Добавлено изменение/Произошло перемещение по истории | <code>{<br>&nbsp;hasUndos: boolean;<br>&nbsp;hasRedos: boolean;<br>&nbsp;type: 'change'\|'undo'\|'redo'<br>&nbsp;}</code> |
179
+ | `uploadImage` | В редактор пришло изображение для загрузки | |
180
+ | `openGallery` | Открыта галерея | |
181
+
182
+ ### **container - опциональный**
183
+
184
+ Id элемента в котором будет развернут iframe с редактором
185
+ По умолчанию `letty-plugin-container`
186
+
187
+ ### **locale - опциональный**
188
+
189
+ Язык локализации редактора.
190
+ Доступные опции: `ru` и `en`
191
+ По умолчанию `ru`
192
+
193
+ ### **autosave - опциональный**
194
+
195
+ Параметры автосохранения
196
+ Варианты значения: `true`, `false`, либо `{ interval: number }`
197
+ По умолчанию `true`
198
+
199
+ ## Методы редактора
200
+
201
+ ### `start` - Запуск редактора
202
+
203
+ ```typescript
204
+ ({
205
+ template: TemplateJSON; // Шаблон письма
206
+ uid: string; // Уникальный идентификатор письма
207
+ templateName?: string; // Название письма для вывода во встроенной шапке редактора
208
+ }) => void
209
+ ```
210
+
211
+ ### `changeTemplate` - Сменить используемый шаблон
212
+
213
+ ```typescript
214
+ (
215
+ template: TemplateJSON, // Шаблон письма
216
+ options?: {
217
+ templateName?: string; // Название письма для вывода во встроенной шапке редактора
218
+ uid?: string; // Уникальный идентификатор письма, на случай, если планируете сменить редактируемое письмо, а не просто контент
219
+ },
220
+ ) => void
221
+ ```
222
+
223
+ ### `toggleGrid` - переключить отображение сетки
224
+
225
+ Вызывает ивент `toggleGrid`
226
+
227
+ ```typescript
228
+ (
229
+ state?: boolean, // Состояние сетки. Если значение отсутствует, то выставляется противоположное текущему
230
+ ) => void
231
+ ```
232
+
233
+ ### `togglePreview` - Переключить отображение предпросмотра
234
+
235
+ Вызывает ивент `togglePreview`
236
+
237
+ ```typescript
238
+ (
239
+ state?: boolean, // Состояние сетки. Если значение отсутствует, то выставляется противоположное текущему
240
+ ) => void
241
+ ```
242
+
243
+ ### `toggleMenu` - Переключить меню/Выключить меню
244
+
245
+ Вызывает ивент `toggleMenu`
246
+
247
+ ```typescript
248
+ (
249
+ menu: EditorMenu | null, // Идентификатор вызываемого меню, либо null, для закрытия открытого меню
250
+ ) => void
251
+ ```
252
+
253
+ <a name="EditorMenu"></a>
254
+
255
+ ```
256
+ EditorMenu: 'audit', 'guide', 'style', 'blocks'
257
+ ```
258
+
259
+ ### `toggleViewMode` - Переключить режим отображения редактора
260
+
261
+ Вызывает ивент `toggleMenu`
262
+
263
+ ```typescript
264
+ (
265
+ mode: 'desktop' | 'mobile', // Идентификатор вызываемого меню, либо null, для закрытия открытого меню
266
+ ) => void
267
+ ```
268
+
269
+ ### `undo` - Назад по истории изменений
270
+
271
+ Вызывает ивент `history`
272
+
273
+ ```typescript
274
+ () => void
275
+ ```
276
+
277
+ ### `redo` - Вперед по истории изменений
278
+
279
+ Вызывает ивент `history`
280
+
281
+ ```typescript
282
+ () => void
283
+ ```
284
+
285
+ ### `save` - Асинхронный метод для сохранения текущего состояния
286
+
287
+ Возвращаемые параметры:
288
+
289
+ ```typescript
290
+ {
291
+ json: TemplateJSON,
292
+ html: string,
293
+ }
294
+ ```
295
+
296
+ ### `exit` - Асинхронный метод для выхода из редактора
297
+
298
+ ```typescript
299
+ {
300
+ json: TemplateJSON,
301
+ html: string,
302
+ }
303
+ ```
304
+
305
+ ### `exportContent` - Асинхронный метод для получения текущего контента письма
306
+
307
+ ```typescript
308
+ {
309
+ json: TemplateJSON,
310
+ html: string,
311
+ }
312
+ ```
@@ -0,0 +1,2 @@
1
+ "use strict";var P=Object.defineProperty;var L=(n,e,t)=>e in n?P(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var a=(n,e,t)=>L(n,typeof e!="symbol"?e+"":e,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const I="http://localhost:5174",h="https://seplugin.sendsay.ru",x=async n=>{try{const e=await fetch(n,{method:"HEAD"});if(!e.ok)return null;const t=e.headers.get("Content-Length");return t?parseInt(t,10):null}catch{return null}},g=n=>new Promise((e,t)=>{const o=new Image;o.onload=async()=>{const i=await x(n)??0;e({originalWidth:o.width,originalHeight:o.height,ratio:o.width/o.height,fileSize:i})},o.onerror=i=>{t(i)},o.src=n}),A="@getdraft/plugin",D="1.13.1-beta.0",C="dist/index.cjs.js",O="dist/index.es.js",H="src",N={prebuild:"rm -rf dist",build:"vite build","dev-tsc":"npx tsc --noEmit",eslint:"npx eslint src","eslint:fix":"pnpm eslint --fix",prettify:"npx prettier -w ./",prepublish:"pnpm build",watch:"NODE_ENV=development vite build --watch",start:"NODE_ENV=development npx vite"},M={},T={"@types/letty":"workspace:^","@vitejs/plugin-legacy":"5.4.1",rollup:"4.22.4",terser:"5.31.3","rollup-plugin-dts":"6.1.1","@letty/eslint-config-ts":"workspace:^","@letty/prettier":"workspace:^"},z="MIT",K={name:A,version:D,main:C,module:O,types:H,scripts:N,dependencies:M,devDependencies:T,license:z},u=(n,e)=>{if(n.length!==1)return n;if(n.charCodeAt(0)>=880){if(e.indexOf("Key")===0&&e.length===4)return e.charAt(3);if(e.indexOf("Digit")===0&&e.length===6)return e.charAt(5)}return n},S=n=>{let e=`${u(n.key,n.code)}`.toLowerCase()||n.key;const t=navigator.userAgent.includes("Mac OS");e==="backspace"&&t&&(e="delete");const o=[];return(t&&n.metaKey||!t&&n.ctrlKey)&&o.push("mod"),n.shiftKey&&o.push("shift"),o.length>0&&(e=`${o.join("-")}-${u(e,n.code)}`),e},U=new Set(["escape","mod-c","mod-d","mod-z","mod-y","mod-shift-z","mod-v","delete","mod-g","mod-p","mod-s"]),_=()=>{var s,r;const n=new RegExp("(^| )seplugin-dev=([^;]+)"),e=document.cookie.match(n),t=(r=e==null?void 0:(s=e[2]).toLowerCase)==null?void 0:r.call(s);if(t==="true")return I;if(t==="stage"||t!=null&&t.includes("https://")||window.location.pathname.includes("netlify"))return t!=null&&t.includes("https://")?t:`${h}/stage`;const[o,i]=K.version.split(".");return`${h}/v${o}.${i}`},j=({token:n,pluginId:e,apiUrl:t})=>{if(!n)throw new Error("No [token] was provided");if(!e)throw new Error("No [pluginId] was provided");if(!t)throw new Error("No [apiUrl] was provided")},G=()=>{const n=document.createElement("iframe");return n.src=_(),n.setAttribute("style","height:100%;width:100%;min-width:960px;border:0px"),n.setAttribute("allow","clipboard-read; clipboard-write"),n.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups"),n};class ${constructor(e){a(this,"iframe",null);a(this,"hotkeysAttached",!1);a(this,"onHostHotkey",e=>{var c;if(!this.captureHotkeysEnabled()||!((c=this.iframe)!=null&&c.contentWindow))return;const t=S(e);if(!U.has(t))return;const o=e.target;if(o&&(o.isContentEditable||["INPUT","TEXTAREA","SELECT"].includes(o.tagName)))return;e.preventDefault();const{key:i,ctrlKey:s,metaKey:r,shiftKey:l,altKey:d}=e;this.postEvent("hostHotkey",{key:i,code:e.code,metaKey:r,ctrlKey:s,shiftKey:l,altKey:d,keyCode:t})});a(this,"onImageUpload",async({id:e,...t})=>{var o,i;try{const s=await((i=(o=this.config.on).uploadImage)==null?void 0:i.call(o,t));let r={};s&&(r=await g(s)),this.postEvent("imageUploaded",{id:e,img:{...r,url:s}})}catch{this.postEvent("imageUploaded",{id:e})}});a(this,"onLoadPersonalization",async()=>{try{if(!this.config.on.loadPersonalization)throw new Error("No personalization loader provided");const e=await this.config.on.loadPersonalization();this.postEvent("loadPersonalization",{items:e})}catch{this.postEvent("loadPersonalization",{})}});a(this,"onGalleryOpen",async({id:e})=>{var o,i;const t=async s=>{const r={url:s};if(s){const l=await g(s);Object.assign(r,l)}this.postEvent("imageUploaded",{id:e,img:r})};(i=(o=this.config.on).openGallery)==null||i.call(o,t)});a(this,"onMessage",e=>{const{data:{source:t,event:o,...i}}=e;t==="DraftPlugin"&&(o==="openGallery"?this.onGalleryOpen(i):o==="uploadImage"?this.onImageUpload(i):o==="loadPersonalization"?this.onLoadPersonalization():this.triggerEvent(this.config.on[o],i))});a(this,"destroy",()=>{var e;(e=this.iframe)==null||e.remove(),this.iframe=null,window.removeEventListener("message",this.onMessage),this.hotkeysAttached&&(window.removeEventListener("keydown",this.onHostHotkey),this.hotkeysAttached=!1)});this.config=e}captureHotkeysEnabled(){return this.config.disableHotkeysPassing!==!0}promisedIframeMethod(e){let t=null;const o=new Promise((i,s)=>{const r=l=>{const{data:{source:d,event:c,...p}}=l;d==="DraftPlugin"&&c===e&&(l.stopPropagation(),t&&(clearTimeout(t),t=null),i(p),window.removeEventListener("message",r,!0))};window.addEventListener("message",r,!0),t=setTimeout(()=>{window.removeEventListener("message",r,!0),s("Request timed out"),t=null},2e3)});return this.postEvent(e),o}postEvent(e,t={}){this.iframe&&this.iframe.contentWindow&&this.iframe.contentWindow.postMessage({...t,event:e,source:"DraftPlugin"},"*")}triggerEvent(e,t){if(e)return e(t)}start({template:e,templateName:t,uid:o}){const i=document.querySelector(this.config.container);if(i){const s=({data:{source:r,event:l}})=>{if(r==="DraftPlugin"&&l==="loaded"){const{on:d,container:c,locale:p,autosave:y,token:v,apiUrl:E,pluginId:k,__config:b}=this.config;j(this.config),this.iframe&&this.iframe.contentWindow&&this.postEvent("init",{container:c,locale:p,uid:o,autosave:y,token:v,pluginId:k,apiUrl:E,__config:b,enabledListeners:Object.keys(d),template:e,templateName:t}),window.addEventListener("message",this.onMessage),window.removeEventListener("message",s)}};this.iframe=G(),window.addEventListener("message",s),i.appendChild(this.iframe),this.captureHotkeysEnabled()&&!this.hotkeysAttached&&(window.addEventListener("keydown",this.onHostHotkey),this.hotkeysAttached=!0)}else throw new Error("Specified container not found")}changeTemplate(e,t={}){this.postEvent("changeTemplate",{...t,template:e})}toggleGrid(e){this.postEvent("toggleGrid",{state:e})}togglePreview(e){this.postEvent("togglePreview",{state:e})}toggleMenu(e=null){this.postEvent("toggleMenu",{menu:e})}toggleViewMode(e){this.postEvent("toggleViewMode",{viewMode:e})}undo(){this.postEvent("undo")}redo(){this.postEvent("redo")}save(){return this.promisedIframeMethod("saveAction")}exit(){return this.promisedIframeMethod("exitAction")}exportContent(){return this.promisedIframeMethod("exportContent")}}var m=(n=>(n.Audit="audit",n.Guide="guide",n.Style="style",n.Blocks="blocks",n))(m||{}),f=(n=>(n.Desktop="desktop",n.Mobile="mobile",n))(f||{});const W={container:"#draft-plugin-container",disableHotkeysPassing:!1,locale:"ru",on:{}};class F{create(e){return new $({...W,...e})}}const w=new F;window.DraftPlugin=w;exports.EditorMenu=m;exports.ViewMode=f;exports.default=w;
2
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/routes.ts","../src/utils.ts","../src/plugin.ts","../src/types.ts","../src/index.ts"],"sourcesContent":["export const LOCAL_ENDPOINT = 'http://localhost:5174';\nexport const PROD_ENDPOINT = 'https://seplugin.sendsay.ru';\n","import { ImageParams } from './types';\n\nconst getImageFileSize = async (imageUrl: string) => {\n try {\n const response = await fetch(imageUrl, { method: 'HEAD' });\n\n if (!response.ok) {\n return null;\n }\n\n const contentLength = response.headers.get('Content-Length');\n\n if (contentLength) {\n return parseInt(contentLength, 10);\n } else {\n return null;\n }\n } catch (error) {\n return null;\n }\n};\n\nexport const getImageParamsFromFileUrl = (imgSrc: string) =>\n new Promise<Omit<ImageParams, 'url'>>((resolve, reject) => {\n const img = new Image();\n\n img.onload = async () => {\n const fileSize = (await getImageFileSize(imgSrc)) ?? 0;\n\n resolve({\n originalWidth: img.width,\n originalHeight: img.height,\n ratio: img.width / img.height,\n fileSize,\n });\n };\n\n img.onerror = (error) => {\n reject(error);\n };\n\n img.src = imgSrc;\n });\n","import { LOCAL_ENDPOINT, PROD_ENDPOINT } from './routes';\nimport {\n EditorMenu,\n ExitParams,\n OnInitParams,\n OpenGalleryParams,\n Handlers,\n PluginConfig,\n SaveParams,\n TemplateJSON,\n UploadImageParams,\n ViewMode,\n HandlerParams,\n} from './types';\nimport { getImageParamsFromFileUrl } from './utils';\nimport pkg from '../package.json';\n\nconst getLatinKey = (key: string, code: string) => {\n if (key.length !== 1) {\n return key;\n }\n\n const capitalHetaCode = 880;\n const isNonLatin = key.charCodeAt(0) >= capitalHetaCode;\n\n if (isNonLatin) {\n if (code.indexOf('Key') === 0 && code.length === 4) {\n return code.charAt(3);\n }\n\n if (code.indexOf('Digit') === 0 && code.length === 6) {\n return code.charAt(5);\n }\n }\n\n return key;\n};\n\nconst constructKeyCode = (event: KeyboardEvent) => {\n let keyCode =\n `${getLatinKey(event.key, event.code)}`.toLowerCase() || event.key;\n const macOs = navigator.userAgent.includes('Mac OS');\n\n if (keyCode === 'backspace' && macOs) {\n keyCode = 'delete';\n }\n\n const prefix = [];\n\n if ((macOs && event.metaKey) || (!macOs && event.ctrlKey)) {\n prefix.push('mod');\n }\n\n if (event.shiftKey) {\n prefix.push('shift');\n }\n\n if (prefix.length > 0) {\n keyCode = `${prefix.join('-')}-${getLatinKey(keyCode, event.code)}`;\n }\n\n return keyCode;\n};\n\nconst CORE_HOTKEYS = new Set([\n 'escape',\n 'mod-c',\n 'mod-d',\n 'mod-z',\n 'mod-y',\n 'mod-shift-z',\n 'mod-v',\n 'delete',\n 'mod-g',\n 'mod-p',\n 'mod-s',\n]);\n\nconst getDeployLink = () => {\n const regex = new RegExp(`(^| )seplugin-dev=([^;]+)`);\n const match = document.cookie.match(regex);\n const value = match?.[2].toLowerCase?.();\n\n if (value === 'true') {\n return LOCAL_ENDPOINT;\n }\n\n if (\n value === 'stage' ||\n value?.includes('https://') ||\n window.location.pathname.includes('netlify')\n ) {\n return value?.includes('https://') ? value : `${PROD_ENDPOINT}/stage`;\n }\n\n const [major, mid] = pkg.version.split('.');\n\n return `${PROD_ENDPOINT}/v${major}.${mid}`;\n};\n\nconst verifyConfig = ({ token, pluginId, apiUrl }: PluginConfig) => {\n if (!token) {\n throw new Error('No [token] was provided');\n }\n\n if (!pluginId) {\n throw new Error('No [pluginId] was provided');\n }\n\n if (!apiUrl) {\n throw new Error('No [apiUrl] was provided');\n }\n};\n\nconst createIframe = () => {\n const iframe = document.createElement('iframe');\n iframe.src = getDeployLink();\n iframe.setAttribute(\n 'style',\n 'height:100%;width:100%;min-width:960px;border:0px',\n );\n iframe.setAttribute('allow', 'clipboard-read; clipboard-write');\n iframe.setAttribute(\n 'sandbox',\n 'allow-scripts allow-same-origin allow-forms allow-popups',\n );\n\n return iframe;\n};\n\nexport class PluginInstance {\n public iframe: HTMLIFrameElement | null = null;\n private hotkeysAttached = false;\n constructor(public config: PluginConfig) {}\n\n private captureHotkeysEnabled() {\n return this.config.disableHotkeysPassing !== true;\n }\n\n private onHostHotkey = (event: KeyboardEvent) => {\n if (!this.captureHotkeysEnabled() || !this.iframe?.contentWindow) {\n return;\n }\n\n const keyCode = constructKeyCode(event);\n\n if (!CORE_HOTKEYS.has(keyCode)) {\n return;\n }\n\n const target = event.target as HTMLElement | null;\n\n if (\n target &&\n (target.isContentEditable ||\n ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName))\n ) {\n return;\n }\n\n event.preventDefault();\n\n const { key, ctrlKey, metaKey, shiftKey, altKey } = event;\n\n this.postEvent('hostHotkey', {\n key,\n code: event.code,\n metaKey,\n ctrlKey,\n shiftKey,\n altKey,\n keyCode,\n });\n };\n\n private promisedIframeMethod<T>(eventName: string) {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const res = new Promise<T>((resolve, reject) => {\n const onEvent = (\n e: MessageEvent<{ source: string; event: keyof PluginConfig['on'] }>,\n ) => {\n const {\n data: { source, event, ...data },\n } = e;\n if (source === 'DraftPlugin' && event === eventName) {\n e.stopPropagation();\n\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n resolve(data as T);\n window.removeEventListener('message', onEvent, true);\n }\n };\n\n window.addEventListener('message', onEvent, true);\n\n timer = setTimeout(() => {\n window.removeEventListener('message', onEvent, true);\n reject('Request timed out');\n timer = null;\n }, 2000);\n });\n\n this.postEvent(eventName);\n\n return res;\n }\n\n private postEvent(event: string, data: object = {}) {\n if (this.iframe && this.iframe.contentWindow) {\n this.iframe.contentWindow.postMessage(\n {\n ...data,\n event,\n source: 'DraftPlugin',\n },\n '*',\n );\n }\n }\n\n private onImageUpload = async ({\n id,\n ...data\n }: UploadImageParams & { id: string }) => {\n try {\n const url = await this.config.on.uploadImage?.(data);\n let params = {};\n\n if (url) {\n params = await getImageParamsFromFileUrl(url);\n }\n\n this.postEvent('imageUploaded', { id, img: { ...params, url } });\n } catch (e) {\n this.postEvent('imageUploaded', { id });\n }\n };\n\n private onLoadPersonalization = async () => {\n try {\n if (!this.config.on.loadPersonalization) {\n throw new Error('No personalization loader provided');\n }\n\n const personalizationDictionary =\n await this.config.on.loadPersonalization();\n\n this.postEvent('loadPersonalization', {\n items: personalizationDictionary,\n });\n } catch (e) {\n this.postEvent('loadPersonalization', {});\n }\n };\n\n private onGalleryOpen = async ({ id }: { id: string }) => {\n const applyImage = async (url?: string) => {\n const imgParams = { url };\n\n if (url) {\n const params = await getImageParamsFromFileUrl(url);\n Object.assign(imgParams, params);\n }\n\n this.postEvent('imageUploaded', {\n id,\n img: imgParams,\n });\n };\n\n this.config.on.openGallery?.(applyImage);\n };\n\n private triggerEvent<H extends CallableFunction | undefined>(\n handler: H,\n params: HandlerParams<H>,\n ) {\n if (!handler) {\n return;\n }\n\n return handler(params);\n }\n\n private onMessage = (\n ev: MessageEvent<{ source: string; event: keyof Handlers }>,\n ) => {\n const {\n data: { source, event, ...data },\n } = ev;\n\n if (source === 'DraftPlugin') {\n if (event === 'openGallery') {\n this.onGalleryOpen(data as OpenGalleryParams);\n } else if (event === 'uploadImage') {\n this.onImageUpload(data as UploadImageParams & { id: string });\n } else if (event === 'loadPersonalization') {\n this.onLoadPersonalization();\n } else {\n this.triggerEvent(\n this.config.on[event],\n data as HandlerParams<Handlers[typeof event]>,\n );\n }\n }\n };\n\n start({\n template,\n templateName,\n uid,\n }: {\n template: string | TemplateJSON;\n uid: string;\n templateName?: string;\n }) {\n const containerEl = document.querySelector(this.config.container);\n\n if (!containerEl) {\n throw new Error('Specified container not found');\n } else {\n const onInit = ({ data: { source, event } }: OnInitParams) => {\n if (source === 'DraftPlugin' && event === 'loaded') {\n const {\n on,\n container,\n locale,\n autosave,\n token,\n apiUrl,\n pluginId,\n __config,\n } = this.config;\n\n verifyConfig(this.config);\n\n if (this.iframe && this.iframe.contentWindow) {\n this.postEvent('init', {\n container,\n locale,\n uid,\n autosave,\n token,\n pluginId,\n apiUrl,\n __config,\n enabledListeners: Object.keys(on),\n template,\n templateName,\n });\n }\n\n window.addEventListener('message', this.onMessage);\n window.removeEventListener('message', onInit);\n }\n };\n\n this.iframe = createIframe();\n window.addEventListener('message', onInit);\n containerEl.appendChild(this.iframe);\n\n if (this.captureHotkeysEnabled() && !this.hotkeysAttached) {\n window.addEventListener('keydown', this.onHostHotkey);\n this.hotkeysAttached = true;\n }\n }\n }\n\n destroy = () => {\n this.iframe?.remove();\n this.iframe = null;\n window.removeEventListener('message', this.onMessage);\n\n if (this.hotkeysAttached) {\n window.removeEventListener('keydown', this.onHostHotkey);\n this.hotkeysAttached = false;\n }\n };\n\n changeTemplate(\n template: string | TemplateJSON,\n options: {\n templateName?: string;\n uid?: string;\n } = {},\n ) {\n this.postEvent('changeTemplate', { ...options, template });\n }\n\n toggleGrid(state?: boolean) {\n this.postEvent('toggleGrid', { state });\n }\n\n togglePreview(state?: boolean) {\n this.postEvent('togglePreview', { state });\n }\n\n toggleMenu(menu: EditorMenu | null = null) {\n this.postEvent('toggleMenu', { menu });\n }\n\n toggleViewMode(viewMode: ViewMode) {\n this.postEvent('toggleViewMode', { viewMode });\n }\n\n undo() {\n this.postEvent('undo');\n }\n\n redo() {\n this.postEvent('redo');\n }\n\n save() {\n return this.promisedIframeMethod<SaveParams>('saveAction');\n }\n\n exit() {\n return this.promisedIframeMethod<ExitParams>('exitAction');\n }\n\n exportContent() {\n return this.promisedIframeMethod<SaveParams>('exportContent');\n }\n}\n","export type EventHandler<T> = (data: T) => void;\n\nexport type AsyncEventHandler<P, R> = (params: P) => Promise<R>;\n\nexport interface SaveParams {\n json: TemplateJSON;\n html: string;\n}\n\nexport type OnInitParams = MessageEvent<{\n source: string;\n event: 'loaded' | unknown;\n}>;\n\nexport enum EditorMenu {\n Audit = 'audit',\n Guide = 'guide',\n Style = 'style',\n Blocks = 'blocks',\n}\n\nexport enum ViewMode {\n Desktop = 'desktop',\n Mobile = 'mobile',\n}\n\nexport interface ReadyParams extends SaveParams {\n lastSavedTemplate?: {\n template: TemplateJSON;\n date: Date;\n };\n}\n\nexport interface ErrorParams {\n message: string;\n code?: string;\n}\n\nexport interface ExitParams extends SaveParams {}\n\nexport interface NotificationParams {\n intent: 'error' | 'info' | 'success' | 'warning';\n errorDetails?: object;\n message?: string;\n title: string;\n}\n\nexport interface OpenGalleryParams {\n id: string;\n}\n\nexport interface UploadImageParams {\n file: string;\n name: string;\n}\n\nexport interface ImageParams {\n url: string;\n originalWidth: number;\n originalHeight: number;\n ratio: number;\n fileSize: number;\n}\n\nexport interface AutosaveParams {\n json: TemplateJSON;\n}\n\ninterface ToggleParams {\n state: boolean;\n}\n\nexport interface ToggleGridParams extends ToggleParams {}\n\nexport interface TogglePreviewParams extends ToggleParams {\n json: TemplateJSON;\n html: string;\n darkHTML: string;\n}\n\nexport interface ToggleViewModeParams {\n mode: ViewMode;\n}\n\nexport interface ToggleMenuParams {\n menu: EditorMenu | null;\n}\n\nexport interface HistoryParams {\n hasUndos: boolean;\n hasRedos: boolean;\n currentStep: number;\n}\n\nexport type TemplateJSON = Record<string, any>;\n\nexport type PersonalizationDictionary = Array<\n | { name: string; value: string }\n | { groupName: string; items: Array<{ name: string; value: string }> }\n>;\n\nexport type ToolbarActionName =\n | 'duplicate'\n | 'destroy'\n | 'edit'\n | 'up'\n | 'down';\nexport type HotkeyContext = 'hotkey';\nexport type OverlayContext = 'overlay-click' | 'overlay-double-click';\nexport type ToolbarContext = 'toolbar';\nexport type UnitSettingsContext = 'unit-settings';\nexport type BlockType = 'system' | 'user';\nexport type DndDragContext = 'canvas' | 'list' | 'palette';\nexport type DndDropContext = 'dropline' | 'placeholder' | 'empty-placeholder';\nexport type DndDropTarget = 'root' | 'section' | 'column' | 'element';\n\nexport interface DuplicateEvent {\n name: 'duplicate';\n context: HotkeyContext | ToolbarContext | UnitSettingsContext;\n unitType: string;\n}\n\nexport interface DestroyEvent {\n name: 'destroy';\n context: HotkeyContext | ToolbarContext | UnitSettingsContext;\n unitType: string;\n}\n\nexport interface EditEvent {\n name: 'edit';\n context: OverlayContext | ToolbarContext;\n unitType: string;\n}\n\nexport interface HistoryEvent {\n name: 'history';\n type: 'undo' | 'redo';\n context: HotkeyContext;\n}\n\nexport interface PreviewEvent {\n name: 'preview';\n context: HotkeyContext;\n}\n\nexport interface ToolbarMoveEvent {\n name: 'up' | 'down';\n context: ToolbarContext;\n unitType: string;\n}\n\nexport interface BlockInsertEvent {\n name: 'block-insert';\n context: 'double-click';\n blockType: BlockType;\n blockName: string;\n blockCategory: string | number;\n blockId: string | number;\n}\n\nexport interface BlockSaveEvent {\n name: 'block-save';\n blockType: 'user';\n blockName: string;\n blockCategory: string | number;\n blockId: string | number;\n elements: Record<string, number> & { sum: number };\n}\n\nexport interface BlockDestroyEvent {\n name: 'block-destroy';\n}\n\nexport interface DropEventDragMeta {\n context?: DndDragContext;\n layer?: 'element' | 'section' | 'column' | 'root';\n type?: string | string[];\n blockCategory?: string | number;\n blockId?: string | number;\n blockName?: string;\n blockType?: BlockType;\n alt?: boolean;\n dropKind?: 'create' | 'move';\n}\n\nexport interface DropEventDropzoneMeta {\n context?: DndDropContext;\n dropTarget?: DndDropTarget;\n}\n\nexport interface DropEvent {\n name: 'drop';\n drag?: DropEventDragMeta;\n dropzone?: DropEventDropzoneMeta;\n}\n\nexport type AnalyticsParams =\n | DuplicateEvent\n | DestroyEvent\n | EditEvent\n | HistoryEvent\n | PreviewEvent\n | ToolbarMoveEvent\n | BlockInsertEvent\n | BlockSaveEvent\n | BlockDestroyEvent\n | DropEvent;\n\nexport interface Handlers {\n ready: EventHandler<ReadyParams>;\n error: EventHandler<ErrorParams>;\n save: EventHandler<SaveParams>;\n exit: EventHandler<ExitParams>;\n notification: EventHandler<NotificationParams>;\n autosave: EventHandler<AutosaveParams>;\n toggleGrid: EventHandler<ToggleGridParams>;\n togglePreview: EventHandler<TogglePreviewParams>;\n toggleViewMode: EventHandler<ToggleViewModeParams>;\n toggleMenu: EventHandler<ToggleMenuParams>;\n history: EventHandler<HistoryParams>;\n analytics: EventHandler<AnalyticsParams>;\n openGallery: EventHandler<(url?: string) => Promise<void>>;\n uploadImage: AsyncEventHandler<UploadImageParams, string>;\n loadPersonalization: AsyncEventHandler<void, PersonalizationDictionary>;\n}\n\nexport type HandlerParams<H> = H extends\n | EventHandler<infer P>\n | AsyncEventHandler<infer P, unknown>\n ? P\n : never;\n\nexport interface PluginConfig {\n container: string;\n locale?: string;\n autosave?:\n | boolean\n | {\n interval: number;\n };\n token: string;\n pluginId: string;\n apiUrl: string;\n disableHotkeysPassing?: boolean;\n __config?: object;\n on: Partial<Handlers>;\n}\n","import { PluginInstance } from './plugin';\nimport { PluginConfig } from './types';\n\nconst DEFAULT_CONFIG: Partial<PluginConfig> = {\n container: '#draft-plugin-container',\n disableHotkeysPassing: false,\n locale: 'ru',\n on: {},\n};\n\nclass PluginWrapper {\n create(config: PluginConfig) {\n return new PluginInstance({\n ...DEFAULT_CONFIG,\n ...config,\n });\n }\n}\n\nconst DraftPlugin = new PluginWrapper();\n\ndeclare global {\n interface Window {\n DraftPlugin: PluginWrapper;\n }\n}\n\nwindow.DraftPlugin = DraftPlugin;\n\nexport default DraftPlugin;\nexport * from './types';\n"],"names":["LOCAL_ENDPOINT","PROD_ENDPOINT","getImageFileSize","imageUrl","response","contentLength","getImageParamsFromFileUrl","imgSrc","resolve","reject","img","fileSize","error","getLatinKey","key","code","constructKeyCode","event","keyCode","macOs","prefix","CORE_HOTKEYS","getDeployLink","regex","match","value","_b","_a","major","mid","pkg","verifyConfig","token","pluginId","apiUrl","createIframe","iframe","PluginInstance","config","__publicField","target","ctrlKey","metaKey","shiftKey","altKey","id","data","url","params","personalizationDictionary","applyImage","imgParams","ev","source","eventName","timer","res","onEvent","e","handler","template","templateName","uid","containerEl","onInit","on","container","locale","autosave","__config","options","state","menu","viewMode","EditorMenu","ViewMode","DEFAULT_CONFIG","PluginWrapper","DraftPlugin"],"mappings":"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"}