@marlinjai/email-editor-core 0.2.0 → 0.4.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/README.md +1 -1
- package/dist/MjmlBuilder-DsiOyhiL.d.mts +667 -0
- package/dist/MjmlBuilder-DsiOyhiL.d.ts +667 -0
- package/dist/{TemplateModel-v__r5Nvi.d.mts → TemplateModel-CmpvrapI.d.ts} +2 -433
- package/dist/{TemplateModel-v__r5Nvi.d.ts → TemplateModel-PcCoyBdo.d.mts} +2 -433
- package/dist/browser.d.mts +13 -0
- package/dist/browser.d.ts +13 -0
- package/dist/browser.js +959 -0
- package/dist/browser.mjs +30 -0
- package/dist/chunk-NQPMHQ5G.mjs +490 -0
- package/dist/chunk-OOCYGZMB.mjs +1406 -0
- package/dist/index.d.mts +7 -30
- package/dist/index.d.ts +7 -30
- package/dist/index.js +881 -33
- package/dist/index.mjs +46 -51
- package/dist/server.d.mts +9 -167
- package/dist/server.d.ts +9 -167
- package/dist/server.js +172 -79
- package/dist/server.mjs +55 -817
- package/package.json +13 -7
- package/dist/TemplateModel-Dq4GoWpl.d.mts +0 -14743
- package/dist/TemplateModel-Dq4GoWpl.d.ts +0 -14743
- package/dist/chunk-2ZL7A2I6.mjs +0 -1029
- package/dist/chunk-MBTNU2JZ.mjs +0 -395
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
interface GradientStop {
|
|
2
|
+
color: string;
|
|
3
|
+
position: number;
|
|
4
|
+
}
|
|
5
|
+
interface BackgroundGradient {
|
|
6
|
+
type: 'linear' | 'radial';
|
|
7
|
+
angle: number;
|
|
8
|
+
stops: GradientStop[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Converts a BackgroundGradient to a CSS background-image value.
|
|
12
|
+
* Returns undefined when stops array is empty.
|
|
13
|
+
*
|
|
14
|
+
* Examples:
|
|
15
|
+
* buildGradientCSS({ type: 'linear', angle: 90, stops: [{color:'#f00',position:0},{color:'#00f',position:100}] })
|
|
16
|
+
* // => "linear-gradient(90deg, #f00 0%, #00f 100%)"
|
|
17
|
+
*
|
|
18
|
+
* buildGradientCSS({ type: 'radial', angle: 0, stops: [{color:'#f00',position:0},{color:'#00f',position:100}] })
|
|
19
|
+
* // => "radial-gradient(circle, #f00 0%, #00f 100%)"
|
|
20
|
+
*/
|
|
21
|
+
declare function buildGradientCSS(gradient: BackgroundGradient): string | undefined;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Spacing configuration for padding/margin
|
|
25
|
+
*/
|
|
26
|
+
interface Spacing {
|
|
27
|
+
top?: string;
|
|
28
|
+
right?: string;
|
|
29
|
+
bottom?: string;
|
|
30
|
+
left?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Custom font definition
|
|
34
|
+
*/
|
|
35
|
+
interface FontDefinition {
|
|
36
|
+
name: string;
|
|
37
|
+
href: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Theme color for reusable brand colors
|
|
41
|
+
*/
|
|
42
|
+
interface ThemeColor {
|
|
43
|
+
name: string;
|
|
44
|
+
value: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* MJML attributes the editor has no control for, kept as found (values as they
|
|
48
|
+
* stood in the source, XML entities included) and emitted again by the
|
|
49
|
+
* compiler after the attributes the editor sets. Written by the MJML import,
|
|
50
|
+
* so an imported document compiles the way its source did: a `css-class` the
|
|
51
|
+
* author's `mj-style` rules target, an `mj-class`, a button's `font-weight`.
|
|
52
|
+
*/
|
|
53
|
+
type ExtraAttributes = Record<string, string>;
|
|
54
|
+
/**
|
|
55
|
+
* What an imported document's `<mj-head>` and `<mj-body>` carried beyond the
|
|
56
|
+
* fields the editor has (title, preview, fonts, breakpoint, styles). When
|
|
57
|
+
* present, the compiler emits `attributes` instead of its own default
|
|
58
|
+
* `<mj-attributes>` (so the source's `mj-all`, per-component defaults and
|
|
59
|
+
* `mj-class` definitions apply as they did), `bodyAttributes` on `<mj-body>`,
|
|
60
|
+
* and `headRaw` (for example `mj-html-attributes`) verbatim inside `<mj-head>`.
|
|
61
|
+
*/
|
|
62
|
+
interface MjmlHead {
|
|
63
|
+
/** The inner markup of `<mj-attributes>`, verbatim. An empty string means "no defaults at all". */
|
|
64
|
+
attributes?: string;
|
|
65
|
+
/** Attributes of `<mj-body>`, e.g. `background-color`, `width`. */
|
|
66
|
+
bodyAttributes?: ExtraAttributes;
|
|
67
|
+
/** Other head elements, verbatim, in source order. */
|
|
68
|
+
headRaw?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Email template metadata
|
|
72
|
+
*/
|
|
73
|
+
interface TemplateMetadata {
|
|
74
|
+
subject?: string;
|
|
75
|
+
previewText?: string;
|
|
76
|
+
title?: string;
|
|
77
|
+
/** Epoch milliseconds (what the editor emits) or an ISO 8601 string */
|
|
78
|
+
createdAt?: string | number;
|
|
79
|
+
/** Epoch milliseconds (what the editor emits) or an ISO 8601 string */
|
|
80
|
+
updatedAt?: string | number;
|
|
81
|
+
fonts?: FontDefinition[];
|
|
82
|
+
themeColors?: ThemeColor[];
|
|
83
|
+
breakpoint?: string;
|
|
84
|
+
customCSS?: string;
|
|
85
|
+
inlineCSS?: string;
|
|
86
|
+
/** Set by the MJML import; see {@link MjmlHead}. */
|
|
87
|
+
mjmlHead?: MjmlHead;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Base block interface - all blocks extend this
|
|
91
|
+
*/
|
|
92
|
+
interface BaseBlock {
|
|
93
|
+
id: string;
|
|
94
|
+
type: string;
|
|
95
|
+
hidden?: boolean;
|
|
96
|
+
/** See {@link ExtraAttributes}. */
|
|
97
|
+
extraAttributes?: ExtraAttributes;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Text block with rich content
|
|
101
|
+
*/
|
|
102
|
+
interface TextBlock extends BaseBlock {
|
|
103
|
+
type: 'text';
|
|
104
|
+
content: string;
|
|
105
|
+
align?: 'left' | 'center' | 'right' | 'justify';
|
|
106
|
+
color?: string;
|
|
107
|
+
fontSize?: string;
|
|
108
|
+
fontFamily?: string;
|
|
109
|
+
padding?: Spacing;
|
|
110
|
+
lineHeight?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Image block
|
|
114
|
+
*/
|
|
115
|
+
interface ImageBlock extends BaseBlock {
|
|
116
|
+
type: 'image';
|
|
117
|
+
src: string;
|
|
118
|
+
alt?: string;
|
|
119
|
+
width?: string;
|
|
120
|
+
height?: string;
|
|
121
|
+
align?: 'left' | 'center' | 'right';
|
|
122
|
+
href?: string;
|
|
123
|
+
padding?: Spacing;
|
|
124
|
+
borderRadius?: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Button block
|
|
128
|
+
*/
|
|
129
|
+
interface ButtonBlock extends BaseBlock {
|
|
130
|
+
type: 'button';
|
|
131
|
+
label: string;
|
|
132
|
+
href: string;
|
|
133
|
+
align?: 'left' | 'center' | 'right';
|
|
134
|
+
backgroundColor?: string;
|
|
135
|
+
color?: string;
|
|
136
|
+
borderRadius?: string;
|
|
137
|
+
border?: string;
|
|
138
|
+
padding?: Spacing;
|
|
139
|
+
innerPadding?: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Divider block
|
|
143
|
+
*/
|
|
144
|
+
interface DividerBlock extends BaseBlock {
|
|
145
|
+
type: 'divider';
|
|
146
|
+
borderColor?: string;
|
|
147
|
+
borderWidth?: string;
|
|
148
|
+
borderStyle?: 'solid' | 'dashed' | 'dotted';
|
|
149
|
+
width?: string;
|
|
150
|
+
padding?: Spacing;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Spacer block for vertical spacing
|
|
154
|
+
*/
|
|
155
|
+
interface SpacerBlock extends BaseBlock {
|
|
156
|
+
type: 'spacer';
|
|
157
|
+
height: string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Custom branded header block (locked)
|
|
161
|
+
*/
|
|
162
|
+
interface HeaderBlock extends BaseBlock {
|
|
163
|
+
type: 'header';
|
|
164
|
+
locked: true;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Custom branded footer block (locked)
|
|
168
|
+
*/
|
|
169
|
+
interface FooterBlock extends BaseBlock {
|
|
170
|
+
type: 'footer';
|
|
171
|
+
locked: true;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Social link definition
|
|
175
|
+
*/
|
|
176
|
+
interface SocialLink {
|
|
177
|
+
platform: string;
|
|
178
|
+
url: string;
|
|
179
|
+
color?: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Social block for social media icons
|
|
183
|
+
*/
|
|
184
|
+
interface SocialBlock extends BaseBlock {
|
|
185
|
+
type: 'social';
|
|
186
|
+
links: SocialLink[];
|
|
187
|
+
iconSize?: string;
|
|
188
|
+
iconPadding?: string;
|
|
189
|
+
borderRadius?: string;
|
|
190
|
+
align?: 'left' | 'center' | 'right';
|
|
191
|
+
mode?: 'horizontal' | 'vertical';
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Hero block with background image
|
|
195
|
+
*/
|
|
196
|
+
interface HeroBlock extends BaseBlock {
|
|
197
|
+
type: 'hero';
|
|
198
|
+
backgroundImage: string;
|
|
199
|
+
backgroundHeight?: string;
|
|
200
|
+
backgroundWidth?: string;
|
|
201
|
+
backgroundColor?: string;
|
|
202
|
+
verticalAlign?: 'top' | 'middle' | 'bottom';
|
|
203
|
+
mode?: 'fixed-height' | 'fluid-height';
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Accordion item
|
|
207
|
+
*/
|
|
208
|
+
interface AccordionItem {
|
|
209
|
+
title: string;
|
|
210
|
+
content: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Accordion block for collapsible content
|
|
214
|
+
*/
|
|
215
|
+
interface AccordionBlock extends BaseBlock {
|
|
216
|
+
type: 'accordion';
|
|
217
|
+
items: AccordionItem[];
|
|
218
|
+
iconPosition?: 'left' | 'right';
|
|
219
|
+
borderColor?: string;
|
|
220
|
+
fontFamily?: string;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Raw HTML block
|
|
224
|
+
*/
|
|
225
|
+
interface RawBlock extends BaseBlock {
|
|
226
|
+
type: 'raw';
|
|
227
|
+
html: string;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Navbar link definition
|
|
231
|
+
*/
|
|
232
|
+
interface NavbarLink {
|
|
233
|
+
href: string;
|
|
234
|
+
label: string;
|
|
235
|
+
color?: string;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Navbar block for navigation
|
|
239
|
+
*/
|
|
240
|
+
interface NavbarBlock extends BaseBlock {
|
|
241
|
+
type: 'navbar';
|
|
242
|
+
links: NavbarLink[];
|
|
243
|
+
hamburger?: boolean;
|
|
244
|
+
baseUrl?: string;
|
|
245
|
+
align?: 'left' | 'center' | 'right';
|
|
246
|
+
icoColor?: string;
|
|
247
|
+
padding?: Spacing;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Carousel image definition
|
|
251
|
+
*/
|
|
252
|
+
interface CarouselImage {
|
|
253
|
+
src: string;
|
|
254
|
+
alt?: string;
|
|
255
|
+
href?: string;
|
|
256
|
+
thumbnailSrc?: string;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Carousel block for image slideshows
|
|
260
|
+
*/
|
|
261
|
+
interface CarouselBlock extends BaseBlock {
|
|
262
|
+
type: 'carousel';
|
|
263
|
+
images: CarouselImage[];
|
|
264
|
+
thumbnails?: 'visible' | 'hidden';
|
|
265
|
+
borderRadius?: string;
|
|
266
|
+
iconWidth?: string;
|
|
267
|
+
tbBorderRadius?: string;
|
|
268
|
+
padding?: Spacing;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Table block for tabular data
|
|
272
|
+
*/
|
|
273
|
+
interface TableBlock extends BaseBlock {
|
|
274
|
+
type: 'table';
|
|
275
|
+
headers: string[];
|
|
276
|
+
rows: string[][];
|
|
277
|
+
align?: 'left' | 'center' | 'right';
|
|
278
|
+
color?: string;
|
|
279
|
+
fontFamily?: string;
|
|
280
|
+
fontSize?: string;
|
|
281
|
+
cellpadding?: string;
|
|
282
|
+
cellspacing?: string;
|
|
283
|
+
border?: string;
|
|
284
|
+
padding?: Spacing;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Union type of all possible blocks
|
|
288
|
+
*/
|
|
289
|
+
type Block = TextBlock | ImageBlock | ButtonBlock | DividerBlock | SpacerBlock | HeaderBlock | FooterBlock | SocialBlock | HeroBlock | AccordionBlock | RawBlock | NavbarBlock | CarouselBlock | TableBlock;
|
|
290
|
+
/**
|
|
291
|
+
* Column within a section
|
|
292
|
+
*/
|
|
293
|
+
interface Column {
|
|
294
|
+
id: string;
|
|
295
|
+
width?: number;
|
|
296
|
+
blocks: Block[];
|
|
297
|
+
hidden?: boolean;
|
|
298
|
+
backgroundColor?: string;
|
|
299
|
+
backgroundGradient?: BackgroundGradient;
|
|
300
|
+
verticalAlign?: 'top' | 'middle' | 'bottom';
|
|
301
|
+
padding?: Spacing;
|
|
302
|
+
/**
|
|
303
|
+
* Optional sub-columns. When non-empty, `blocks` MUST be empty
|
|
304
|
+
* (a column is either a leaf with blocks, or a group with sub-columns).
|
|
305
|
+
* See `docs/superpowers/specs/2026-04-26-nested-columns-design.md`.
|
|
306
|
+
*/
|
|
307
|
+
subColumns?: SubColumn[];
|
|
308
|
+
/** See {@link ExtraAttributes}. */
|
|
309
|
+
extraAttributes?: ExtraAttributes;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Sub-column inside a "group"-kind Column. Holds only leaf blocks
|
|
313
|
+
* (text, image, button, divider, spacer, social). Cannot itself contain
|
|
314
|
+
* sub-columns (no nesting beyond depth 2).
|
|
315
|
+
*/
|
|
316
|
+
interface SubColumn {
|
|
317
|
+
id: string;
|
|
318
|
+
width: number;
|
|
319
|
+
blocks: Block[];
|
|
320
|
+
backgroundColor?: string;
|
|
321
|
+
verticalAlign?: 'top' | 'middle' | 'bottom';
|
|
322
|
+
paddingTop?: string;
|
|
323
|
+
paddingRight?: string;
|
|
324
|
+
paddingBottom?: string;
|
|
325
|
+
paddingLeft?: string;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Section containing columns
|
|
329
|
+
*/
|
|
330
|
+
interface Section {
|
|
331
|
+
id: string;
|
|
332
|
+
type: 'section';
|
|
333
|
+
backgroundColor?: string;
|
|
334
|
+
backgroundImage?: string;
|
|
335
|
+
backgroundGradient?: BackgroundGradient;
|
|
336
|
+
backgroundPosition?: string;
|
|
337
|
+
backgroundRepeat?: 'repeat' | 'no-repeat';
|
|
338
|
+
backgroundSize?: string;
|
|
339
|
+
/**
|
|
340
|
+
* Full width (`full-width` in MJML). Inside a full-width {@link Wrapper},
|
|
341
|
+
* MJML renders the section at standard width whatever this says.
|
|
342
|
+
*/
|
|
343
|
+
fullWidth?: boolean;
|
|
344
|
+
noStack?: boolean;
|
|
345
|
+
hidden?: boolean;
|
|
346
|
+
padding?: Spacing;
|
|
347
|
+
columns: Column[];
|
|
348
|
+
/**
|
|
349
|
+
* Emit the section's raw blocks straight into the parent (`<mj-body>`, or
|
|
350
|
+
* the `<mj-wrapper>` the section sits in) instead of inside an
|
|
351
|
+
* `<mj-section>`: markup that sat there in an imported document. Ignored as
|
|
352
|
+
* soon as the section holds any block that is not raw.
|
|
353
|
+
*/
|
|
354
|
+
bodyRaw?: boolean;
|
|
355
|
+
/** See {@link ExtraAttributes}. */
|
|
356
|
+
extraAttributes?: ExtraAttributes;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* A container around several sections (`<mj-wrapper>`): one background,
|
|
360
|
+
* border, radius and padding shared by the sections inside, with an optional
|
|
361
|
+
* vertical `gap` between them. Sits at the top level of a document, next to
|
|
362
|
+
* sections; it never holds another wrapper and never sits inside a section.
|
|
363
|
+
* It may be empty (it keeps its styling, and sections can be moved back in).
|
|
364
|
+
* Added in schema version 1.1.
|
|
365
|
+
*/
|
|
366
|
+
interface Wrapper {
|
|
367
|
+
id: string;
|
|
368
|
+
type: 'wrapper';
|
|
369
|
+
hidden?: boolean;
|
|
370
|
+
backgroundColor?: string;
|
|
371
|
+
/** `background-url` in MJML. */
|
|
372
|
+
backgroundImage?: string;
|
|
373
|
+
backgroundGradient?: BackgroundGradient;
|
|
374
|
+
backgroundPosition?: string;
|
|
375
|
+
backgroundRepeat?: 'repeat' | 'no-repeat';
|
|
376
|
+
backgroundSize?: string;
|
|
377
|
+
/** CSS border shorthand for all four sides, e.g. `1px solid #dddddd`. */
|
|
378
|
+
border?: string;
|
|
379
|
+
borderTop?: string;
|
|
380
|
+
borderRight?: string;
|
|
381
|
+
borderBottom?: string;
|
|
382
|
+
borderLeft?: string;
|
|
383
|
+
borderRadius?: string;
|
|
384
|
+
/** MJML's default when unset is `20px 0`. */
|
|
385
|
+
padding?: Spacing;
|
|
386
|
+
/** Full width (`full-width`): the background spans the whole mail width; the sections inside stay at standard width. */
|
|
387
|
+
fullWidth?: boolean;
|
|
388
|
+
/** Class names for `css-class`, space separated, for the document's own `mj-style` rules. */
|
|
389
|
+
cssClass?: string;
|
|
390
|
+
/** Vertical space between the sections inside, in px (`gap`, MJML 4.15 and later). */
|
|
391
|
+
gap?: string;
|
|
392
|
+
textAlign?: 'left' | 'center' | 'right';
|
|
393
|
+
/** See {@link ExtraAttributes}. */
|
|
394
|
+
extraAttributes?: ExtraAttributes;
|
|
395
|
+
sections: Section[];
|
|
396
|
+
}
|
|
397
|
+
/** What the top level of a document holds, in order: sections and wrappers. */
|
|
398
|
+
type TopLevelItem = Section | Wrapper;
|
|
399
|
+
/** The document schema versions: 1.1 added {@link Wrapper}. */
|
|
400
|
+
type TemplateVersion = '1.0' | '1.1';
|
|
401
|
+
/**
|
|
402
|
+
* Complete email template structure
|
|
403
|
+
*/
|
|
404
|
+
interface EmailTemplate {
|
|
405
|
+
/**
|
|
406
|
+
* The document's id. Optional: a document without one is valid, and the
|
|
407
|
+
* editor store assigns one when it opens it (see `TemplateModel`).
|
|
408
|
+
* `migrateTemplate` never adds or changes it.
|
|
409
|
+
*/
|
|
410
|
+
id?: string;
|
|
411
|
+
/**
|
|
412
|
+
* The schema version. `migrateTemplate` brings every document it accepts to
|
|
413
|
+
* the current one (1.1); a 1.0 document cannot hold a wrapper.
|
|
414
|
+
*/
|
|
415
|
+
version: TemplateVersion;
|
|
416
|
+
metadata: TemplateMetadata;
|
|
417
|
+
/** The document's top level, in order: sections and wrappers (see {@link TopLevelItem}). */
|
|
418
|
+
sections: TopLevelItem[];
|
|
419
|
+
}
|
|
420
|
+
/** Whether a top-level item is a {@link Wrapper}. */
|
|
421
|
+
declare function isWrapper(item: TopLevelItem): item is Wrapper;
|
|
422
|
+
/** Every section of a document in order, the ones inside wrappers included. */
|
|
423
|
+
declare function allSections(items: readonly TopLevelItem[]): Section[];
|
|
424
|
+
/**
|
|
425
|
+
* Result of MJML compilation
|
|
426
|
+
*/
|
|
427
|
+
interface CompileResult {
|
|
428
|
+
mjml: string;
|
|
429
|
+
html: string;
|
|
430
|
+
errors?: string[];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** The `<mj-attributes>` a document gets unless it brings its own (`metadata.mjmlHead.attributes`). */
|
|
434
|
+
declare const DEFAULT_MJML_ATTRIBUTES = "\n <mj-all font-family=\"Georgia, serif\" />\n <mj-text font-size=\"14px\" line-height=\"1.6\" />\n ";
|
|
435
|
+
/** Marks the editor's own raw markup so the MJML import can read the block back exactly. */
|
|
436
|
+
declare const EDITOR_DATA_ATTRIBUTE: {
|
|
437
|
+
readonly social: "data-ee-social";
|
|
438
|
+
readonly subColumns: "data-ee-subcols";
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* Standard, padded base64 of a UTF-8 string, without Node's `Buffer`, so the
|
|
442
|
+
* builder runs in the browser too. Byte-identical to `Buffer.toString('base64')`.
|
|
443
|
+
*/
|
|
444
|
+
declare function base64Utf8(text: string): string;
|
|
445
|
+
/**
|
|
446
|
+
* The comments that fence everything the editor's own compile adds to a
|
|
447
|
+
* document (`toMJML(template, { editor: true })`): ghosts for hidden nodes, a
|
|
448
|
+
* floor for empty columns, markers around a Raw block's verbatim markup. MJML
|
|
449
|
+
* keeps body comments in place, so the same fence survives into the HTML and
|
|
450
|
+
* {@link stripEditorMarkup} takes the additions out of either.
|
|
451
|
+
*/
|
|
452
|
+
declare const EDITOR_MARKER: {
|
|
453
|
+
readonly begin: "<!--ee:begin-->";
|
|
454
|
+
readonly end: "<!--ee:end-->";
|
|
455
|
+
};
|
|
456
|
+
/** The editor's additions removed from MJML or HTML, leaving the plain document. */
|
|
457
|
+
declare function stripEditorMarkup(markup: string): string;
|
|
458
|
+
/**
|
|
459
|
+
* The inline styles a Text block's content is wrapped in (`<div style>` inside
|
|
460
|
+
* MJML's own div), so nested tags inherit them. When this is empty the content
|
|
461
|
+
* sits directly in MJML's div. The canvas's in-place editor uses the same
|
|
462
|
+
* function to find the element that holds exactly `block.content`, so there
|
|
463
|
+
* is one rule for where the content lives.
|
|
464
|
+
*/
|
|
465
|
+
declare function textInlineStyles(block: Pick<TextBlock, 'align' | 'color' | 'fontSize' | 'fontFamily' | 'lineHeight'>): string[];
|
|
466
|
+
/** Options of {@link MjmlBuilder.toMJML}. */
|
|
467
|
+
interface BuildOptions {
|
|
468
|
+
/**
|
|
469
|
+
* Emit the editor's own affordances, fenced by {@link EDITOR_MARKER} so they
|
|
470
|
+
* strip back to the plain document: a ghost row for a hidden block, a ghost
|
|
471
|
+
* section or wrapper for a hidden one (never a ghost column: a column's
|
|
472
|
+
* width depends on its siblings' count, so a ghost would move the visible
|
|
473
|
+
* ones), a 48px floor in an empty column so it can be a drop target, and
|
|
474
|
+
* id markers around Raw blocks. Display only; never sent.
|
|
475
|
+
*/
|
|
476
|
+
editor?: boolean;
|
|
477
|
+
}
|
|
478
|
+
/** A block's (or sub-columns') JSON, base64-encoded, for an {@link EDITOR_DATA_ATTRIBUTE}. */
|
|
479
|
+
declare function encodeEditorData(value: unknown): string;
|
|
480
|
+
/** `a + b` for two px lengths (`0` counts as px); undefined when either is something else. */
|
|
481
|
+
declare function addPx(a: string, b: string): string | undefined;
|
|
482
|
+
/**
|
|
483
|
+
* A section's padding with a wrapper's gap added on top. MJML's `mj-wrapper`
|
|
484
|
+
* has no gap of its own, so the editor's `gap` is compiled this way: every
|
|
485
|
+
* section after the first inside the wrapper gets the gap added to its top
|
|
486
|
+
* padding (MJML's default 20px when the section declares none). The MJML
|
|
487
|
+
* import takes it off again, so a document survives export and import.
|
|
488
|
+
* A top padding that is not a px length is left alone.
|
|
489
|
+
*/
|
|
490
|
+
declare function paddingWithGap(spacing: Spacing | undefined, gap: string): Spacing;
|
|
491
|
+
/** Options for one {@link MJMLCompiler.compile} call. */
|
|
492
|
+
interface CompileOptions {
|
|
493
|
+
/**
|
|
494
|
+
* MJML adds a Google Fonts `<link>` and `@import` on its own whenever a font
|
|
495
|
+
* family it knows (Open Sans, Droid Sans, Lato, Roboto, Ubuntu) appears in
|
|
496
|
+
* the document. `false` leaves them out, so the font falls back to the rest
|
|
497
|
+
* of its stack and the mail loads nothing from Google. Fonts the document
|
|
498
|
+
* declares itself (`metadata.fonts`) are always emitted. Default `true`.
|
|
499
|
+
*/
|
|
500
|
+
webFonts?: boolean;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* The options both engines compile with, in one place: the server's `mjml`
|
|
504
|
+
* and the browser's `mjml-browser` must receive the same ones, or their
|
|
505
|
+
* output differs by configuration rather than by build, and the parity test
|
|
506
|
+
* between them means nothing.
|
|
507
|
+
*/
|
|
508
|
+
declare function mjmlOptions(options?: CompileOptions): {
|
|
509
|
+
fonts?: {} | undefined;
|
|
510
|
+
ignoreIncludes: boolean;
|
|
511
|
+
validationLevel: "soft";
|
|
512
|
+
minify: boolean;
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Turns an EmailTemplate into MJML markup. `MJMLCompiler` (server) and
|
|
516
|
+
* `compileInBrowser` (browser) both compile what this emits.
|
|
517
|
+
*/
|
|
518
|
+
declare class MjmlBuilder {
|
|
519
|
+
/**
|
|
520
|
+
* Set true while emitting a column with sub-columns. Causes generateHead
|
|
521
|
+
* to inject the responsive media query exactly once per template.
|
|
522
|
+
*/
|
|
523
|
+
protected needsSubColumnStyles: boolean;
|
|
524
|
+
/** Set for the duration of one build in editor mode; see {@link BuildOptions}. */
|
|
525
|
+
protected editor: boolean;
|
|
526
|
+
/** `fragment` fenced as an editor addition, or nothing outside editor mode. */
|
|
527
|
+
protected ee(fragment: string): string;
|
|
528
|
+
/** The label of a ghost: what the node is, so the person can find and unhide it. */
|
|
529
|
+
private ghostText;
|
|
530
|
+
/**
|
|
531
|
+
* The MJML markup for a document, without compiling it to HTML.
|
|
532
|
+
*/
|
|
533
|
+
toMJML(template: EmailTemplate, options?: BuildOptions): string;
|
|
534
|
+
/**
|
|
535
|
+
* Convert template to MJML markup
|
|
536
|
+
*/
|
|
537
|
+
protected templateToMJML(template: EmailTemplate): string;
|
|
538
|
+
/**
|
|
539
|
+
* Generate MJML head section with all head components
|
|
540
|
+
* Supports: mj-title, mj-preview, mj-font, mj-breakpoint, mj-style
|
|
541
|
+
*/
|
|
542
|
+
private generateHead;
|
|
543
|
+
/**
|
|
544
|
+
* Collect background-image CSS rules for all wrappers, sections and columns that have gradients.
|
|
545
|
+
* Returns a string of CSS rules to inject as a single mj-style block.
|
|
546
|
+
*/
|
|
547
|
+
private collectGradientStyles;
|
|
548
|
+
/**
|
|
549
|
+
* Convert a wrapper to an `<mj-wrapper>` around its sections. The wrapper's
|
|
550
|
+
* attributes are MJML's own for `mj-wrapper`; the sections inside follow
|
|
551
|
+
* MJML's rules there (a full-width section inside a full-width wrapper
|
|
552
|
+
* renders at standard width, which the inspector explains).
|
|
553
|
+
*/
|
|
554
|
+
private wrapperToMJML;
|
|
555
|
+
/** Background attributes shared by sections and wrappers: a gradient (with its Outlook fallback colour), or colour and image. */
|
|
556
|
+
private pushBackground;
|
|
557
|
+
/**
|
|
558
|
+
* Convert section to MJML
|
|
559
|
+
* Supports background images, full-width, and mj-group for non-stacking columns
|
|
560
|
+
*/
|
|
561
|
+
private sectionToMJML;
|
|
562
|
+
/**
|
|
563
|
+
* Convert column to MJML
|
|
564
|
+
*/
|
|
565
|
+
private columnToMJML;
|
|
566
|
+
/**
|
|
567
|
+
* Emit a group column as an mj-column wrapping a hand-built nested
|
|
568
|
+
* table inside an mj-raw island. The parent mj-section structure
|
|
569
|
+
* stays standard MJML; only the nested area escapes MJML's parser.
|
|
570
|
+
*
|
|
571
|
+
* The responsive media query (table.ee-sub-cols td.ee-sub-col) is
|
|
572
|
+
* injected once at document head via generateHead when the
|
|
573
|
+
* needsSubColumnStyles flag is set.
|
|
574
|
+
*/
|
|
575
|
+
private subColumnsToMJML;
|
|
576
|
+
/**
|
|
577
|
+
* Convert block to MJML based on type
|
|
578
|
+
*/
|
|
579
|
+
private blockToMJML;
|
|
580
|
+
/**
|
|
581
|
+
* Convert text block to MJML
|
|
582
|
+
*
|
|
583
|
+
* Note: MJML applies styles to the container <td>, but TipTap content
|
|
584
|
+
* (wrapped in <p> tags) doesn't inherit these styles. We solve this by
|
|
585
|
+
* wrapping the content in a <div> with explicit inline styles.
|
|
586
|
+
*/
|
|
587
|
+
private textBlockToMJML;
|
|
588
|
+
/**
|
|
589
|
+
* Convert image block to MJML
|
|
590
|
+
*/
|
|
591
|
+
private imageBlockToMJML;
|
|
592
|
+
/**
|
|
593
|
+
* Convert button block to MJML
|
|
594
|
+
*/
|
|
595
|
+
private buttonBlockToMJML;
|
|
596
|
+
/**
|
|
597
|
+
* Convert divider block to MJML
|
|
598
|
+
*/
|
|
599
|
+
private dividerBlockToMJML;
|
|
600
|
+
/**
|
|
601
|
+
* Convert spacer block to MJML
|
|
602
|
+
*/
|
|
603
|
+
private spacerBlockToMJML;
|
|
604
|
+
/**
|
|
605
|
+
* Platform brand colors for social icons
|
|
606
|
+
*/
|
|
607
|
+
private readonly SOCIAL_COLORS;
|
|
608
|
+
/**
|
|
609
|
+
* Get white SVG icon as data URI for a platform
|
|
610
|
+
* Uses clean, minimal SVGs optimized for email
|
|
611
|
+
*/
|
|
612
|
+
private getSocialIconDataUri;
|
|
613
|
+
/**
|
|
614
|
+
* Convert social block to MJML using raw HTML table for reliable horizontal layout
|
|
615
|
+
* Uses styled anchor tags with background colors for full width control
|
|
616
|
+
*/
|
|
617
|
+
private socialBlockToMJML;
|
|
618
|
+
/**
|
|
619
|
+
* Convert hero block to MJML
|
|
620
|
+
*/
|
|
621
|
+
private heroBlockToMJML;
|
|
622
|
+
/**
|
|
623
|
+
* Convert accordion block to MJML
|
|
624
|
+
*/
|
|
625
|
+
private accordionBlockToMJML;
|
|
626
|
+
/**
|
|
627
|
+
* Convert raw HTML block to MJML
|
|
628
|
+
*/
|
|
629
|
+
private rawBlockToMJML;
|
|
630
|
+
/**
|
|
631
|
+
* Convert navbar block to MJML
|
|
632
|
+
*/
|
|
633
|
+
private navbarBlockToMJML;
|
|
634
|
+
/**
|
|
635
|
+
* Convert carousel block to MJML
|
|
636
|
+
*/
|
|
637
|
+
private carouselBlockToMJML;
|
|
638
|
+
/**
|
|
639
|
+
* Convert table block to MJML
|
|
640
|
+
*/
|
|
641
|
+
private tableBlockToMJML;
|
|
642
|
+
/**
|
|
643
|
+
* Generate the locked header block.
|
|
644
|
+
*
|
|
645
|
+
* It used to render one client's company name and a logo from an image host;
|
|
646
|
+
* both are gone. What is left is a placeholder image slot. Keep this in step
|
|
647
|
+
* with `headerBlockDefinition` in `packages/blocks/src/branded/index.ts`:
|
|
648
|
+
* which of the two runs depends on how the registry is configured.
|
|
649
|
+
*
|
|
650
|
+
* The placeholder is a `data:` URI rather than an address on an image host,
|
|
651
|
+
* so a workspace whose `asset_policy` is `service_only` can still send a mail
|
|
652
|
+
* that contains this block.
|
|
653
|
+
*/
|
|
654
|
+
private headerBlockToMJML;
|
|
655
|
+
/**
|
|
656
|
+
* Generate the locked footer block: the unsubscribe link and nothing else.
|
|
657
|
+
*
|
|
658
|
+
* It used to carry a copyright line naming one client's company. A locked
|
|
659
|
+
* block carries no props, so whoever sends the mail cannot correct a name
|
|
660
|
+
* that is not theirs; the line is gone rather than replaced with a fake one.
|
|
661
|
+
* Keep this in step with `footerBlockDefinition` in
|
|
662
|
+
* `packages/blocks/src/branded/index.ts`.
|
|
663
|
+
*/
|
|
664
|
+
private footerBlockToMJML;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export { type AccordionBlock as A, type BuildOptions as B, type CompileOptions as C, DEFAULT_MJML_ATTRIBUTES as D, type EmailTemplate as E, type FontDefinition as F, type GradientStop as G, type HeaderBlock as H, type ImageBlock as I, type TextBlock as J, type ThemeColor as K, type TopLevelItem as L, MjmlBuilder as M, type NavbarBlock as N, addPx as O, allSections as P, base64Utf8 as Q, type RawBlock as R, type Section as S, type TableBlock as T, buildGradientCSS as U, encodeEditorData as V, type Wrapper as W, isWrapper as X, paddingWithGap as Y, textInlineStyles as Z, type CompileResult as a, EDITOR_MARKER as b, type Block as c, type BackgroundGradient as d, type MjmlHead as e, type AccordionItem as f, type BaseBlock as g, type ButtonBlock as h, type CarouselBlock as i, type CarouselImage as j, type Column as k, type DividerBlock as l, mjmlOptions as m, EDITOR_DATA_ATTRIBUTE as n, type ExtraAttributes as o, type FooterBlock as p, type HeroBlock as q, type NavbarLink as r, stripEditorMarkup as s, type SocialBlock as t, type SocialLink as u, type SpacerBlock as v, type Spacing as w, type SubColumn as x, type TemplateMetadata as y, type TemplateVersion as z };
|