best-email-builder 0.3.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +214 -19
  3. package/dist/builder.js +4 -4
  4. package/dist/{chunk-ZSUHOCCE.js → chunk-AT73Y7JA.js} +159 -83
  5. package/dist/chunk-AT73Y7JA.js.map +1 -0
  6. package/dist/chunk-HHLOE6OZ.js +1682 -0
  7. package/dist/chunk-HHLOE6OZ.js.map +1 -0
  8. package/dist/chunk-JNHFZXDL.js +1776 -0
  9. package/dist/chunk-JNHFZXDL.js.map +1 -0
  10. package/dist/chunk-MY6WPDT4.js +6110 -0
  11. package/dist/chunk-MY6WPDT4.js.map +1 -0
  12. package/dist/{chunk-JOC752I3.js → chunk-NAEGU6PR.js} +13 -4
  13. package/dist/{chunk-JOC752I3.js.map → chunk-NAEGU6PR.js.map} +1 -1
  14. package/dist/{chunk-BVOVAEEJ.js → chunk-TGDZ4NFI.js} +44 -3
  15. package/dist/chunk-TGDZ4NFI.js.map +1 -0
  16. package/dist/index.js +45 -8
  17. package/dist/index.js.map +1 -1
  18. package/dist/library.js +3 -3
  19. package/dist/server.js +544 -204
  20. package/dist/server.js.map +1 -1
  21. package/dist/styles.css +1 -1
  22. package/dist/types/blocks/shared.d.ts +20 -0
  23. package/dist/types/core/EmailEditor.d.ts +72 -19
  24. package/dist/types/core/constants.d.ts +1 -1
  25. package/dist/types/core/mjmlDefaults.d.ts +13 -0
  26. package/dist/types/core/scrollbars.d.ts +1 -1
  27. package/dist/types/defaultEditor.d.ts +4 -1
  28. package/dist/types/engine/NativeAdapter.d.ts +28 -0
  29. package/dist/types/engine/canvas/CanvasView.d.ts +88 -0
  30. package/dist/types/engine/canvas/canvasStyles.d.ts +3 -0
  31. package/dist/types/engine/canvas/icons.d.ts +14 -0
  32. package/dist/types/engine/canvas/morph.d.ts +21 -0
  33. package/dist/types/engine/canvasMjml.d.ts +30 -0
  34. package/dist/types/engine/mjml.d.ts +31 -0
  35. package/dist/types/engine/model.d.ts +130 -0
  36. package/dist/types/engine/placement.d.ts +69 -0
  37. package/dist/types/engine/richText.d.ts +15 -0
  38. package/dist/types/engine/unwrapModule.d.ts +29 -0
  39. package/dist/types/features/brand/BrandButton.d.ts +4 -0
  40. package/dist/types/features/brand/brand.d.ts +108 -0
  41. package/dist/types/features/brand/extract.d.ts +61 -0
  42. package/dist/types/features/brand/index.d.ts +2 -0
  43. package/dist/types/features/brand/messages.d.ts +13 -0
  44. package/dist/types/features/brand/useBrand.d.ts +22 -0
  45. package/dist/types/features/generate/GenerateButton.d.ts +3 -0
  46. package/dist/types/features/generate/RewriteText.d.ts +5 -0
  47. package/dist/types/features/generate/SubjectIdeas.d.ts +16 -0
  48. package/dist/types/features/generate/chip.d.ts +1 -0
  49. package/dist/types/features/generate/claude.d.ts +21 -0
  50. package/dist/types/features/generate/draft.d.ts +122 -0
  51. package/dist/types/features/generate/generator.d.ts +118 -0
  52. package/dist/types/features/generate/index.d.ts +2 -0
  53. package/dist/types/features/generate/prompt.d.ts +76 -0
  54. package/dist/types/features/generate/purposes.d.ts +48 -0
  55. package/dist/types/features/generate/rewrite.d.ts +83 -0
  56. package/dist/types/features/generate/subjects.d.ts +83 -0
  57. package/dist/types/features/preview/PreviewButton.d.ts +1 -1
  58. package/dist/types/features/preview/darkMode.d.ts +48 -0
  59. package/dist/types/index.d.ts +10 -1
  60. package/dist/types/server/brand.d.ts +32 -0
  61. package/dist/types/server/brandImport.d.ts +93 -0
  62. package/dist/types/server/compileMjml.d.ts +2 -0
  63. package/dist/types/server/handler.d.ts +25 -0
  64. package/dist/types/server/index.d.ts +11 -0
  65. package/dist/types/storage/client.d.ts +75 -3
  66. package/dist/types/ui/fields.d.ts +2 -1
  67. package/dist/types/ui/primitives/button.d.ts +1 -1
  68. package/dist/types/ui/primitives/color-picker.d.ts +2 -1
  69. package/dist/types/ui/useLayoutWidth.d.ts +1 -1
  70. package/package.json +13 -7
  71. package/dist/chunk-3M36VVXS.js +0 -665
  72. package/dist/chunk-3M36VVXS.js.map +0 -1
  73. package/dist/chunk-57CTQNTK.js +0 -56
  74. package/dist/chunk-57CTQNTK.js.map +0 -1
  75. package/dist/chunk-BVOVAEEJ.js.map +0 -1
  76. package/dist/chunk-ILRAQTHS.js +0 -3677
  77. package/dist/chunk-ILRAQTHS.js.map +0 -1
  78. package/dist/chunk-ZSUHOCCE.js.map +0 -1
  79. package/dist/types/adapters/grapesjs/GrapesJsAdapter.d.ts +0 -33
  80. package/dist/types/adapters/grapesjs/engineConfig.d.ts +0 -148
  81. package/dist/types/adapters/grapesjs/unwrapModule.d.ts +0 -18
@@ -1,665 +0,0 @@
1
- // src/core/constants.js
2
- var EMAIL_BUILDER_VERSION = "0.3.0";
3
- var EMAIL_DOCUMENT_SCHEMA_VERSION = 1;
4
- var EMAIL_DEVICES = ["desktop", "mobile"];
5
- var EMAIL_MOBILE_WIDTH = 375;
6
-
7
- // src/core/document.js
8
- var MIGRATIONS = {};
9
- var EmailDocumentError = class extends Error {
10
- /** @param {string} message */
11
- constructor(message) {
12
- super(message);
13
- this.name = "EmailDocumentError";
14
- this.status = 422;
15
- }
16
- };
17
- function isPlainObject(value) {
18
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
19
- }
20
- function createDocument({ engine, engineVersion, project }) {
21
- if (!engine) throw new EmailDocumentError("Document: missing engine");
22
- if (!isPlainObject(project)) throw new EmailDocumentError("Document: invalid engine project");
23
- return {
24
- schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,
25
- engine,
26
- engineVersion: String(engineVersion || ""),
27
- builderVersion: EMAIL_BUILDER_VERSION,
28
- project: (
29
- /** @type {Record<string, any>} */
30
- project
31
- )
32
- };
33
- }
34
- function readDocument(raw) {
35
- let value = raw;
36
- if (typeof value === "string") {
37
- try {
38
- value = JSON.parse(value);
39
- } catch {
40
- throw new EmailDocumentError("Unreadable document: invalid JSON");
41
- }
42
- }
43
- if (!isPlainObject(value)) throw new EmailDocumentError("Unreadable document: object expected");
44
- let document = { .../** @type {object} */
45
- value };
46
- const version = Number(document.schemaVersion);
47
- if (!Number.isInteger(version) || version < 1) {
48
- throw new EmailDocumentError("Unreadable document: missing schema version");
49
- }
50
- if (version > EMAIL_DOCUMENT_SCHEMA_VERSION) {
51
- throw new EmailDocumentError(
52
- `Document written by a newer version of the builder (schema ${version}) \u2014 reload the page`
53
- );
54
- }
55
- for (let v = version; v < EMAIL_DOCUMENT_SCHEMA_VERSION; v += 1) {
56
- const migrate = MIGRATIONS[v];
57
- if (!migrate) throw new EmailDocumentError(`Document: no migration from schema ${v}`);
58
- document = migrate(document);
59
- }
60
- if (typeof document.engine !== "string" || !document.engine) {
61
- throw new EmailDocumentError("Unreadable document: missing engine");
62
- }
63
- if (!isPlainObject(document.project)) {
64
- throw new EmailDocumentError("Unreadable document: missing engine project");
65
- }
66
- return {
67
- schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,
68
- engine: document.engine,
69
- engineVersion: String(document.engineVersion || ""),
70
- builderVersion: String(document.builderVersion || ""),
71
- project: document.project
72
- };
73
- }
74
- function serializeDocument(document) {
75
- return JSON.stringify(readDocument(document));
76
- }
77
-
78
- // src/i18n/fr.js
79
- var fr = {
80
- "1. Complete your profile": "1. Compl\xE9tez votre profil",
81
- "2. Explore": "2. Explorez",
82
- "3. Ask us anything": "3. Posez-nous vos questions",
83
- "30% off everything": "-30 % sur tout",
84
- "30% off this weekend only": "-30 % ce week-end seulement",
85
- "4 columns": "4 colonnes",
86
- "4-column section": "Section en 4 colonnes",
87
- "A new feature": "Une nouveaut\xE9",
88
- "A sale with a discount code and two products": "Une vente avec un code promo et deux produits",
89
- "A tip": "Une astuce",
90
- "Add a block": "Ajouter un bloc",
91
- "Add to favorites": "Ajouter aux favoris",
92
- "All templates": "Tous les mod\xE8les",
93
- "All your templates are in the favorites.": "Tous vos mod\xE8les sont dans les favoris.",
94
- "An empty page to build from scratch": "Une page vide pour partir de z\xE9ro",
95
- "An evening of talks and drinks with the team and our customers.": "Une soir\xE9e de rencontres autour d'un verre avec l'\xE9quipe et nos clients.",
96
- "An event": "Un \xE9v\xE9nement",
97
- "Article {n}": "Article {n}",
98
- "Background image": "Image de fond",
99
- "Background image with a title and a button": "Image de fond avec un titre et un bouton",
100
- "Base": "Base",
101
- "Base \u2014 not editable: start from it to create your template.": "Base non modifiable \u2014 partez-en pour cr\xE9er votre mod\xE8le.",
102
- "Bases": "Bases",
103
- "Blank": "Vierge",
104
- "Book my seat": "Je r\xE9serve ma place",
105
- "Builder": "Constructeur",
106
- "Buy": "Acheter",
107
- "by {name}": "par {name}",
108
- "Choose a template": "Choisissez un mod\xE8le",
109
- "Clear the search": "Effacer la recherche",
110
- "Copy HTML": "Copier le HTML",
111
- "Could not load the templates.": "Impossible de charger les mod\xE8les.",
112
- "Created": "Cr\xE9\xE9",
113
- "Date, place and a button to book a seat": "Date, lieu et un bouton pour r\xE9server",
114
- "Delete \u201C{name}\u201D?": "Supprimer \xAB {name} \xBB ?",
115
- "Direction": "Disposition",
116
- "Download": "T\xE9l\xE9charger",
117
- "Edit: {element}": "Modifier : {element}",
118
- "Email settings": "R\xE9glages de l'e-mail",
119
- "Email template library": "Banque de mod\xE8les emails",
120
- "Event invitation": "Invitation",
121
- "Facebook": "Facebook",
122
- "Favorites": "Favoris",
123
- "Fill the section": "Remplir la section",
124
- "Get started": "Commencer",
125
- "GitHub": "GitHub",
126
- "Greets a new customer and lists the first steps": "Accueille un nouveau client et liste les premi\xE8res \xE9tapes",
127
- "Heading": "Titre",
128
- "Hero banner": "Banni\xE8re",
129
- "Horizontal": "Horizontale",
130
- "HTML copied.": "HTML copi\xE9.",
131
- "Icon background": "Fond de l'ic\xF4ne",
132
- "Icon size": "Taille des ic\xF4nes",
133
- "Icons linking to your profiles": "Ic\xF4nes vers vos profils",
134
- "Image + text": "Image + texte",
135
- "Image beside a title, text and button": "Image \xE0 c\xF4t\xE9 d'un titre, d'un texte et d'un bouton",
136
- "Image position": "Position de l'image",
137
- "Image size": "Taille de l'image",
138
- "In brief": "En bref",
139
- "Instagram": "Instagram",
140
- "Large bold title": "Grand titre en gras",
141
- "LinkedIn": "LinkedIn",
142
- "Logo, headline, text and a button": "Logo, titre, texte et un bouton",
143
- "Menu": "Menu",
144
- "Menu link": "Lien du menu",
145
- "Monthly news: banner, two articles, highlights": "Les nouvelles du mois : banni\xE8re, deux articles, br\xE8ves",
146
- "Network": "R\xE9seau",
147
- "Newsletter": "Newsletter",
148
- "No preview: this storage cannot render emails.": "Pas d'aper\xE7u : ce stockage ne sait pas compiler les e-mails.",
149
- "No template yet. Start from a base, or create a new one.": "Aucun mod\xE8le pour l\u2019instant. Partez d\u2019une base, ou cr\xE9ez-en un.",
150
- "Nothing matches \u201C{query}\u201D.": "Aucun r\xE9sultat pour \xAB {query} \xBB.",
151
- "Offer valid until Sunday midnight, while stocks last.": "Offre valable jusqu'\xE0 dimanche minuit, dans la limite des stocks.",
152
- "One sentence about it.": "Une phrase pour en parler.",
153
- "Origin": "Origine",
154
- "Original size": "Taille d'origine",
155
- "Our offices, 1 Main Street": "Nos bureaux, 1 rue Principale",
156
- "Picking a template replaces the email being edited.": "Choisir un mod\xE8le remplace l'e-mail en cours de modification.",
157
- "Product one": "Produit un",
158
- "Product two": "Produit deux",
159
- "Profile URL": "Adresse du profil",
160
- "Promotion": "Promotion",
161
- "Read more": "Lire la suite",
162
- "Ready-made templates to copy into your campaigns.": "Mod\xE8les pr\xEAts \xE0 copier dans vos campagnes.",
163
- "Remove from favorites": "Retirer des favoris",
164
- "Replace": "Remplacer",
165
- "Replace the current email?": "Remplacer l'e-mail actuel ?",
166
- "Reply to this email: a person reads it.": "R\xE9pondez \xE0 cet e-mail : une personne le lit.",
167
- "Row of links to your site": "Rang\xE9e de liens vers votre site",
168
- "Save the date \u2014 seats are limited.": "Notez la date \u2014 les places sont limit\xE9es.",
169
- "Search templates": "Rechercher un mod\xE8le",
170
- "Seats are limited: book yours now, it takes ten seconds.": "Les places sont limit\xE9es : r\xE9servez la v\xF4tre, cela prend dix secondes.",
171
- "Select a template on the left.": "S\xE9lectionnez un mod\xE8le \xE0 gauche.",
172
- "Shop the sale": "Voir les offres",
173
- "Simple": "Simple",
174
- "So we can tailor what we send you.": "Pour adapter ce que nous vous envoyons.",
175
- "Social link": "R\xE9seau social",
176
- "Social links": "R\xE9seaux sociaux",
177
- "Space around each icon": "Espace autour de chaque ic\xF4ne",
178
- "Start from a template": "Partir d'un mod\xE8le",
179
- "Start from this base": "Partir de cette base",
180
- "Take a tour of what you can do.": "Faites le tour de ce que vous pouvez faire.",
181
- "Template deleted.": "Mod\xE8le supprim\xE9.",
182
- "Template duplicated.": "Mod\xE8le dupliqu\xE9.",
183
- "Templates": "Mod\xE8les",
184
- "Thank you for joining us. Here is how to get the most out of your account.": "Merci de nous avoir rejoints. Voici comment tirer le meilleur de votre compte.",
185
- "The browser refused to copy.": "Le navigateur a refus\xE9 la copie.",
186
- "The news of the month": "Les nouvelles du mois",
187
- "The template and its versions will be deleted.": "Le mod\xE8le et ses versions seront supprim\xE9s.",
188
- "The template preview appears here.": "L'aper\xE7u du mod\xE8le s'affiche ici.",
189
- "This weekend only, with the code SPRING30.": "Ce week-end seulement, avec le code SPRING30.",
190
- "Three steps to get started.": "Trois \xE9tapes pour bien commencer.",
191
- "Thursday, 7 pm": "Jeudi, 19 h",
192
- "Two or three sentences that sum up the story and make people want the rest.": "Deux ou trois phrases qui r\xE9sument l'article et donnent envie de lire la suite.",
193
- "Updated": "Modifi\xE9",
194
- "Vertical": "Verticale",
195
- "Welcome": "Bienvenue",
196
- "Welcome aboard!": "Bienvenue \xE0 bord !",
197
- "Welcome, {name}!": "Bienvenue, {name} !",
198
- "What changed, what is coming, and two stories worth your time.": "Ce qui a chang\xE9, ce qui arrive, et deux articles qui valent le d\xE9tour.",
199
- "When": "Quand",
200
- "Where": "O\xF9",
201
- "Whole image": "Image enti\xE8re",
202
- "With the code SPRING30, until Sunday midnight.": "Avec le code SPRING30, jusqu'\xE0 dimanche minuit.",
203
- "X": "X",
204
- "You're invited": "On vous invite !",
205
- "YouTube": "YouTube",
206
- "{label} \u2014 {side}": "{label} \u2014 {side}",
207
- "{max} test addresses at most": "{max} adresses de test au maximum",
208
- "{message} Your version can be saved as a copy.": "{message} Votre version peut \xEAtre enregistr\xE9e comme copie.",
209
- "{name} (copy)": "{name} (copie)",
210
- "{sent} sent, {failed} failed.": "{sent} parti(s), {failed} en \xE9chec.",
211
- "{sent} test(s) sent \u2014 check your inbox.": "{sent} test(s) parti(s) \u2014 ouvrez votre messagerie avant de continuer.",
212
- "1 column": "1 colonne",
213
- "2 columns": "2 colonnes",
214
- "2-column section": "Section en 2 colonnes",
215
- "25%": "25 %",
216
- "3 columns": "3 colonnes",
217
- "3-column section": "Section en 3 colonnes",
218
- "50%": "50 %",
219
- "600 px (standard)": "600 px (standard)",
220
- "640 px": "640 px",
221
- "75%": "75 %",
222
- "Address not allowed": "Adresse refus\xE9e",
223
- "Addresses": "Adresses",
224
- "Alignment": "Alignement",
225
- "Alt text": "Texte alternatif",
226
- "An https:// address is required": "Une adresse https:// est requise",
227
- "attribute {name} not allowed": "attribut {name} interdit",
228
- "Authentication required": "Authentification requise",
229
- "Autosave": "Sauvegarde automatique",
230
- "Autosave paused": "Sauvegarde automatique suspendue",
231
- "Autosaved at {time}": "Enregistr\xE9 automatiquement \xE0 {time}",
232
- "Autosaving\u2026": "Sauvegarde automatique\u2026",
233
- "Available in every template.": "Disponible dans tous les mod\xE8les.",
234
- "Back": "Retour",
235
- "Background": "Fond",
236
- "base tag not allowed": "balise base interdite",
237
- "Blocks": "Blocs",
238
- "Bold": "Gras",
239
- "Bottom": "Bas",
240
- "Button": "Bouton",
241
- "Button background": "Fond du bouton",
242
- "Button padding": "Marges du bouton",
243
- "Button without link ({count})": "Bouton sans lien ({count})",
244
- "Call to action": "Appel \xE0 l\u2019action",
245
- "Campaign": "Campagne",
246
- "Campaign name": "Nom de la campagne",
247
- "Cancel": "Annuler",
248
- "CDATA section not allowed": "section CDATA interdite",
249
- "Center": "Centre",
250
- "Close": "Fermer",
251
- "Colors": "Couleurs",
252
- "Column": "Colonne",
253
- "Company": "Soci\xE9t\xE9",
254
- "component script not allowed": "script de composant interdit",
255
- "conditional comment not allowed": "commentaire conditionnel interdit",
256
- "Confirm": "Confirmer",
257
- "Content": "Contenu",
258
- "Content width": "Largeur du contenu",
259
- "Copy saved: \u201C{name}\u201D.": "Copie enregistr\xE9e : \xAB {name} \xBB.",
260
- "Corner radius": "Arrondi",
261
- "Create": "Cr\xE9er",
262
- "Create \u201C{name}\u201D": "Cr\xE9er \xAB {name} \xBB",
263
- "current": "actuelle",
264
- "Custom": "Personnalis\xE9es",
265
- "Custom variables are not configured": "Les variables personnalis\xE9es ne sont pas configur\xE9es",
266
- "Dashed": "Tirets",
267
- "Default": "Par d\xE9faut",
268
- "Delete": "Supprimer",
269
- "Delete (Del)": "Supprimer (Suppr)",
270
- "Delete the variable \u201C{name}\u201D?": "Supprimer la variable \xAB {name} \xBB ?",
271
- "Describes the image if it is blocked": "D\xE9crit l\u2019image si elle est bloqu\xE9e",
272
- "Deselect": "D\xE9s\xE9lectionner",
273
- "Desktop": "Bureau",
274
- "Device": "Appareil",
275
- "Dismiss": "Masquer",
276
- "Divider": "S\xE9parateur",
277
- "Dotted": "Pointill\xE9s",
278
- "Double-click the text to edit it.": "Double-cliquez sur le texte pour le modifier.",
279
- "Duplicate": "Dupliquer",
280
- "Edit": "Modifier",
281
- "Edit \u201C{name}\u201D": "Modifier \xAB {name} \xBB",
282
- "Edit variable": "Modifier la variable",
283
- "Element actions": "Actions sur l\u2019\xE9l\xE9ment",
284
- "Email": "Courriel",
285
- "Email address": "Adresse e-mail",
286
- "Email preview": "Aper\xE7u du courriel",
287
- "embed not allowed": "embed interdit",
288
- "Empty file": "Fichier vide",
289
- "Empty MJML": "MJML vide",
290
- "Empty MJML compilation": "Compilation MJML vide",
291
- "Empty subject": "Objet vide",
292
- "executable content not allowed": "contenu ex\xE9cutable interdit",
293
- "external resource not allowed": "ressource externe interdite",
294
- "File missing": "Fichier manquant",
295
- "Final rendering, variables filled with samples.": "Rendu final, variables remplies d\u2019exemples.",
296
- "First name": "Pr\xE9nom",
297
- "Fit to text": "Ajust\xE9e au texte",
298
- "Font": "Police",
299
- "forbidden element in project: {tag}": "\xE9l\xE9ment interdit dans le projet : {tag}",
300
- "form field not allowed": "champ de formulaire interdit",
301
- "form not allowed": "formulaire interdit",
302
- "Format rejected: PNG, JPEG or GIF expected": "Format refus\xE9 : PNG, JPEG ou GIF attendu",
303
- "frame not allowed": "frame interdite",
304
- "Full length": "Toute la largeur",
305
- "Full width": "Pleine largeur",
306
- "Full-width section": "Section pleine largeur",
307
- "Give the template a name before saving it.": "Donnez un libell\xE9 au mod\xE8le avant de l\u2019enregistrer.",
308
- "Group": "Groupe",
309
- "Height": "Hauteur",
310
- "Hello {name},": "Bonjour {name},",
311
- "Hex code": "Code hexad\xE9cimal",
312
- "Horizontal rule": "Filet horizontal",
313
- "HTML button not allowed (use the Button block)": "bouton HTML interdit (utiliser le bloc Bouton)",
314
- "iframe not allowed": "iframe interdite",
315
- "Ignored (invalid): {email}": "Ignor\xE9e (invalide) : {email}",
316
- "Image": "Image",
317
- "image rejected ({name})": "image refus\xE9e ({name})",
318
- "image rejected ({name}): https only": "image refus\xE9e ({name}) : https seulement",
319
- "Image too large: {max} MB maximum": "Image trop lourde : {max} Mo au maximum",
320
- "Image without alt text ({count})": "Texte alternatif manquant ({count} image(s))",
321
- "Image without file ({count})": "Image sans fichier ({count})",
322
- "Inbox": "Bo\xEEte de r\xE9ception",
323
- "Indicative preview \u2014 check it with \u201CSend test\u201D.": "Aper\xE7u indicatif \u2014 validez avec \xAB Envoyer un test \xBB.",
324
- "inline style rejected": "style en ligne refus\xE9",
325
- "Insecure http link ({count})": "Lien non s\xE9curis\xE9 en http ({count})",
326
- "Insert a variable in the preview text": "Ins\xE9rer une variable dans le texte d\u2019aper\xE7u",
327
- "Insert a variable in the subject": "Ins\xE9rer une variable dans l\u2019objet",
328
- "Internal error": "Erreur interne",
329
- "Invalid JSON": "JSON invalide",
330
- "Justify": "Justifi\xE9",
331
- "Key is required": "Balise requise",
332
- "Last name": "Nom",
333
- "Layout": "Mise en page",
334
- "Learn more": "D\xE9couvrir",
335
- "Leave": "Quitter",
336
- "Leave the builder?": "Quitter le constructeur ?",
337
- "Left": "Gauche",
338
- "Length": "Longueur",
339
- "Line color": "Couleur du filet",
340
- "Line height": "Interligne",
341
- "Line style": "Trait",
342
- "Link": "Lien",
343
- "Link on click": "Lien au clic",
344
- "Link URL": "Adresse du lien",
345
- "Links": "Liens",
346
- "Loading the builder": "Chargement du constructeur",
347
- "Loading\u2026": "Chargement\u2026",
348
- 'Lowercase letters, digits, "_" and "." only, starting with a letter': "Minuscules, chiffres, \xAB _ \xBB et \xAB . \xBB seulement, en commen\xE7ant par une lettre",
349
- "MathML not allowed": "MathML interdit",
350
- "Message is {size} KB: Gmail clips messages over {limit} KB": "Message de {size} Ko : Gmail le coupe au-del\xE0 de {limit} Ko",
351
- "meta tag not allowed": "balise meta interdite",
352
- "Middle": "Milieu",
353
- "missing <mj-body>": "<mj-body> manquant",
354
- "Missing unsubscribe link": "Lien de d\xE9sinscription absent",
355
- "mj-font (external font) is outside the builder": "mj-font (police externe) est hors du constructeur",
356
- "mj-include would read a file from the server": "mj-include lirait un fichier du serveur",
357
- "mj-raw (free HTML) is not allowed": "mj-raw (HTML libre) est refus\xE9",
358
- "mj-style (free CSS) is outside the builder": "mj-style (CSS libre) est hors du constructeur",
359
- "MJML too large ({size} KB, maximum {max} KB)": "MJML trop volumineux ({size} ko, maximum {max} ko)",
360
- "Mobile": "Mobile",
361
- "Move down": "Descendre",
362
- "Move left": "D\xE9placer \xE0 gauche",
363
- "Move right": "D\xE9placer \xE0 droite",
364
- "Move up": "Monter",
365
- "Multipart form expected": "Formulaire multipart attendu",
366
- "Name": "Libell\xE9",
367
- "Name is required": "Libell\xE9 requis",
368
- "Name required": "Libell\xE9 requis",
369
- "New template": "Nouveau mod\xE8le",
370
- "New variable": "Nouvelle variable",
371
- "No changes": "Aucune modification",
372
- "No preview text": "Aucun texte d\u2019aper\xE7u",
373
- "No subject": "Sans objet",
374
- "No usable test address": "Aucune adresse de test exploitable",
375
- "No variable found.": "Aucune variable trouv\xE9e.",
376
- "No versions yet.": "Aucune version.",
377
- "Normal": "Normale",
378
- "Nothing to fix.": "Aucun point \xE0 corriger.",
379
- "Nothing to show yet.": "Rien \xE0 afficher pour l\u2019instant.",
380
- "object not allowed": "object interdit",
381
- "Order number": "Num\xE9ro de commande",
382
- "Padding": "Marges",
383
- "Page background": "Fond de page",
384
- "Paragraph or heading": "Paragraphe ou titre",
385
- "Photo, visual or logo": "Photo, visuel ou logo",
386
- "Pick a colour": "Choisir une couleur",
387
- "Points to check": "Points \xE0 v\xE9rifier",
388
- "Preview": "Aper\xE7u",
389
- "Preview and tests": "Aper\xE7u et tests",
390
- "Preview text": "Texte d\u2019aper\xE7u",
391
- "Project rejected: {issues}": "Projet refus\xE9 : {issues}",
392
- "project too deep": "projet trop profond",
393
- "project too large ({size} KB, maximum {max} KB)": "projet trop volumineux ({size} ko, maximum {max} ko)",
394
- "Properties": "Propri\xE9t\xE9s",
395
- "Recipient": "Destinataire",
396
- "Redo": "R\xE9tablir",
397
- "Redo (Ctrl+Y)": "R\xE9tablir (Ctrl+Y)",
398
- "Refresh": "Rafra\xEEchir",
399
- "Rendering\u2026": "Rendu en cours\u2026",
400
- "Request too large": "Requ\xEAte trop volumineuse",
401
- "Restore": "Recharger",
402
- "Restore version {n}": "Recharger la version {n}",
403
- "Restore version {n}?": "Recharger la version {n} ?",
404
- "Results": "R\xE9sultats",
405
- "Resume": "Reprendre",
406
- "Resume the draft?": "Reprendre le brouillon ?",
407
- "Retry": "R\xE9essayer",
408
- "Right": "Droite",
409
- "Route not found": "Route introuvable",
410
- "Sample value": "Valeur d\u2019exemple",
411
- "Save": "Enregistrer",
412
- "Save a copy": "Enregistrer une copie",
413
- "Save the template first": "Enregistrez d\u2019abord le mod\xE8le",
414
- "Saved": "Enregistr\xE9",
415
- "Saved \u2014 version {n}.": "Enregistr\xE9 \u2014 version {n}.",
416
- "Saved at {time}": "Enregistr\xE9 \xE0 {time}",
417
- "Saved, with {count} MJML warning(s): {message}": "Enregistr\xE9, avec {count} avertissement(s) MJML : {message}",
418
- "Saving\u2026": "Enregistrement\u2026",
419
- "script not allowed": "script interdit",
420
- "Search variables\u2026": "Rechercher une variable\u2026",
421
- "Section": "Section",
422
- "Select a text or a button first.": "S\xE9lectionnez d\u2019abord un texte ou un bouton.",
423
- "Send": "Envoyer",
424
- "Send a test": "Envoyer un test",
425
- "Shown after the subject in the inbox": "Affich\xE9 apr\xE8s l\u2019objet dans la bo\xEEte de r\xE9ception",
426
- "Size": "Taille",
427
- "Solid": "Plein",
428
- "Spacer": "Espace",
429
- "Spacing": "Espacement",
430
- "Start over": "Partir de z\xE9ro",
431
- "style sheet not allowed": "feuille de style interdite",
432
- "Subject": "Objet",
433
- "SVG not allowed": "SVG interdit",
434
- "Tag": "Balise",
435
- "tag not allowed: <{tag}>": "balise non admise : <{tag}>",
436
- "Template": "Mod\xE8le",
437
- "Template changed in the meantime": "Mod\xE8le modifi\xE9 entre-temps",
438
- "Template changed in the meantime \u2014 reload it before saving": "Mod\xE8le modifi\xE9 entre-temps \u2014 rechargez-le avant d\u2019enregistrer",
439
- "Template changed in the meantime by {user} \u2014 reload it before saving": "Mod\xE8le modifi\xE9 entre-temps par {user} \u2014 rechargez-le avant d\u2019enregistrer",
440
- "Template not found": "Mod\xE8le introuvable",
441
- "Template rejected: {issues}": "Mod\xE8le refus\xE9 : {issues}",
442
- "Template rejected: {issues} (+{more})": "Mod\xE8le refus\xE9 : {issues} (+{more})",
443
- "Template saved.": "Mod\xE8le enregistr\xE9.",
444
- "Templates that use it will show its \u201Cvalue if empty\u201D.": "Les mod\xE8les qui l\u2019utilisent afficheront sa \xAB valeur si vide \xBB.",
445
- "Test": "Tester",
446
- "Test sending is not configured": "L\u2019envoi de test n\u2019est pas configur\xE9",
447
- "Text": "Texte",
448
- "The builder could not load.": "Le constructeur n\u2019a pas pu se charger.",
449
- "The builder could not start.": "Le constructeur n\u2019a pas pu d\xE9marrer.",
450
- "the document must start with <mjml>": "le document doit commencer par <mjml>",
451
- "The draft will be offered again next time.": "Le brouillon reste propos\xE9 \xE0 la prochaine ouverture.",
452
- "The email on screen, to reviewers\u2019 addresses.": "Le courriel \xE0 l\u2019\xE9cran, aux adresses de relecture.",
453
- "The template could not be loaded.": "Le mod\xE8le n\u2019a pas pu \xEAtre charg\xE9.",
454
- "Thickness": "\xC9paisseur",
455
- "This key is already used": "Cette balise existe d\xE9j\xE0",
456
- "To check": "\xC0 v\xE9rifier",
457
- "Too many test sends \u2014 try again later": "Trop d\u2019envois de test \u2014 r\xE9essayez plus tard",
458
- "Too many uploads \u2014 try again later": "Trop de d\xE9p\xF4ts d\u2019images \u2014 r\xE9essayez plus tard",
459
- "Top": "Haut",
460
- "Typography": "Typographie",
461
- "Undo": "Annuler",
462
- "Undo (Ctrl+Z)": "Annuler (Ctrl+Z)",
463
- "Unknown variables (rendered empty): {tags}": "Variables inconnues (affich\xE9es vides) : {tags}",
464
- "Unsaved changes": "Modifications non enregistr\xE9es",
465
- "Unsaved changes will be lost.": "Les modifications non enregistr\xE9es seront perdues.",
466
- "Unsaved changes will be replaced.": "Les modifications non enregistr\xE9es seront remplac\xE9es.",
467
- "Unsaved template from {when}.": "Mod\xE8le non enregistr\xE9 du {when}.",
468
- "Unsubscribe": "Se d\xE9sinscrire",
469
- "Unsubscribe link": "Lien de d\xE9sinscription",
470
- "Upload": "D\xE9poser",
471
- "Upload failed": "D\xE9p\xF4t impossible",
472
- "URL rejected ({name})": "adresse refus\xE9e ({name})",
473
- "Value if empty": "Valeur si vide",
474
- "Variable \u201C{name}\u201D created and inserted.": "Variable \xAB {name} \xBB cr\xE9\xE9e et ins\xE9r\xE9e.",
475
- "Variable \u201C{name}\u201D created.": "Variable \xAB {name} \xBB cr\xE9\xE9e.",
476
- "Variable \u201C{name}\u201D saved.": "Variable \xAB {name} \xBB enregistr\xE9e.",
477
- "Variable deleted.": "Variable supprim\xE9e.",
478
- "Variable not found": "Variable introuvable",
479
- "Variables": "Variables",
480
- "Version {n}": "Version {n}",
481
- "Version {n} restored \u2014 save to keep it.": "Version {n} recharg\xE9e \u2014 enregistrez pour la garder.",
482
- "Version not found": "Version introuvable",
483
- "Versions": "Versions",
484
- "Vertical alignment": "Alignement vertical",
485
- "Vertical space": "Vide vertical",
486
- "Weight": "Graisse",
487
- "When a recipient has none": "Quand un destinataire n\u2019en a pas",
488
- "Width": "Largeur",
489
- "Your company": "Votre soci\xE9t\xE9",
490
- "Your headline": "Titre du message",
491
- "Your text.": "Votre texte.",
492
- "\u201C{name}\u201D template applied.": "Mod\xE8le \xAB {name} \xBB appliqu\xE9.",
493
- "\u20AC49 instead of \u20AC70": "49 \u20AC au lieu de 70 \u20AC"
494
- };
495
-
496
- // src/i18n/index.js
497
- var LOCALES = (
498
- /** @type {Record<string, Messages>} */
499
- { en: {}, fr }
500
- );
501
- function createTranslator(messages = {}) {
502
- return (text, params) => {
503
- const template = Object.prototype.hasOwnProperty.call(messages, text) ? messages[text] : text;
504
- if (!params) return template;
505
- return template.replace(
506
- /\{(\w+)\}/g,
507
- (match, key) => params[key] === void 0 || params[key] === null ? match : String(params[key])
508
- );
509
- };
510
- }
511
- function translatorFor(locale = "en", overrides = {}) {
512
- const base = LOCALES[String(locale).toLowerCase()] || LOCALES[String(locale).slice(0, 2).toLowerCase()] || {};
513
- return createTranslator({ ...base, ...overrides });
514
- }
515
- var identity = createTranslator();
516
-
517
- // src/features/variables/catalog.js
518
- var DEFAULT_VARIABLES = [
519
- { key: "contact.firstname", label: "First name", group: "Recipient", sample: "Jane" },
520
- { key: "contact.lastname", label: "Last name", group: "Recipient", sample: "Doe" },
521
- { key: "contact.email", label: "Email address", group: "Recipient", sample: "jane.doe@example.com" },
522
- { key: "company.name", label: "Company", group: "Recipient", sample: "Example Inc." },
523
- { key: "campaign.name", label: "Campaign name", group: "Campaign", sample: "Newsletter" },
524
- {
525
- key: "unsubscribe",
526
- label: "Unsubscribe link",
527
- group: "Links",
528
- sample: "",
529
- token: "{{ unsubscribe }}",
530
- kind: "link"
531
- }
532
- ];
533
- var CUSTOM_GROUP = "Custom";
534
- var RESERVED_KEYS = /* @__PURE__ */ new Set(["unsubscribe", "mirror"]);
535
- var VARIABLE_LIMITS = { key: 60, label: 80, group: 40, sample: 200, fallback: 200, description: 200 };
536
- var KEY_SHAPE = /^[a-z][a-z0-9_]*(\.[a-z0-9_]+)*$/;
537
- function tokenOf(variable) {
538
- return variable.token || `{{${variable.key}}}`;
539
- }
540
- function keyFromLabel(label) {
541
- const key = String(label || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/^[^a-z]+/, "").slice(0, VARIABLE_LIMITS.key).replace(/_+$/, "");
542
- return key || "variable";
543
- }
544
- function defineVariables(variables) {
545
- const seen = /* @__PURE__ */ new Set();
546
- for (const variable of variables) {
547
- if (!variable || !KEY_SHAPE.test(variable.key || "")) {
548
- throw new Error(`variables: invalid key "${variable?.key}"`);
549
- }
550
- if (seen.has(variable.key)) throw new Error(`variables: duplicate key "${variable.key}"`);
551
- seen.add(variable.key);
552
- }
553
- return variables;
554
- }
555
- var clean = (value, max) => String(value ?? "").replace(/\s+/g, " ").trim().slice(0, max);
556
- function validateCustomVariable(input, { existing = [], editing = null, t = identity } = {}) {
557
- const errors = {};
558
- const label = clean(input?.label, VARIABLE_LIMITS.label);
559
- const key = editing || String(input?.key || "").trim();
560
- if (!label) errors.label = t("Name is required");
561
- if (!editing) {
562
- if (!key) errors.key = t("Key is required");
563
- else if (key.length > VARIABLE_LIMITS.key || !KEY_SHAPE.test(key)) {
564
- errors.key = t('Lowercase letters, digits, "_" and "." only, starting with a letter');
565
- } else if (RESERVED_KEYS.has(key) || existing.some((v) => v.key === key)) {
566
- errors.key = t("This key is already used");
567
- }
568
- }
569
- if (Object.keys(errors).length) return { errors };
570
- return {
571
- variable: {
572
- key,
573
- label,
574
- group: clean(input?.group, VARIABLE_LIMITS.group) || CUSTOM_GROUP,
575
- sample: clean(input?.sample, VARIABLE_LIMITS.sample),
576
- fallback: clean(input?.fallback, VARIABLE_LIMITS.fallback),
577
- description: clean(input?.description, VARIABLE_LIMITS.description),
578
- custom: true
579
- }
580
- };
581
- }
582
- function mergeVariables(system, custom = []) {
583
- const keys = new Set(system.map((v) => v.key));
584
- return [...system, ...custom.filter((v) => !keys.has(v.key)).map((v) => ({ ...v, custom: true }))];
585
- }
586
-
587
- // src/renderer/links.js
588
- var UNSUBSCRIBE = /\{\{\s*unsubscribe\s*\}\}|\[UNSUBSCRIBE\]/gi;
589
- var MIRROR = /\{\{\s*mirror\s*\}\}/gi;
590
- var DEFAULT_ASSETS_PATH = "/email-assets/";
591
- var ASSET_PATH_PATTERN = /^\/(?:[A-Za-z0-9._~-]+\/)*[a-f0-9]{32}\.(png|jpg|gif)$/;
592
- var RELATIVE_ASSET = /(^|["'(=\s])(\/(?:[A-Za-z0-9._~-]+\/)*[a-f0-9]{32}\.(?:png|jpg|gif))(?=["')\s>]|$)/g;
593
- function isAssetPath(value) {
594
- const path = String(value || "").trim();
595
- return ASSET_PATH_PATTERN.test(path) && !path.split("/").some((part) => part === ".." || part === ".");
596
- }
597
- function absoluteAssetUrls(html, baseUrl) {
598
- const root = String(baseUrl || "").replace(/\/+$/, "");
599
- if (!root) return String(html || "");
600
- return String(html || "").replace(
601
- RELATIVE_ASSET,
602
- (match, before, path) => isAssetPath(path) ? `${before}${root}${path}` : match
603
- );
604
- }
605
- function escapeAttribute(text) {
606
- return String(text ?? "").replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
607
- }
608
- function applyServiceLinks(html, { unsubscribeUrl }) {
609
- return String(html || "").replace(UNSUBSCRIBE, () => escapeAttribute(unsubscribeUrl)).replace(MIRROR, "#");
610
- }
611
-
612
- // src/validation/images.js
613
- var MAX_IMAGE_BYTES = 2 * 1024 * 1024;
614
- var IMAGE_TYPES = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif" };
615
- var ASSET_KEY_PATTERN = /^[a-f0-9]{32}\.(png|jpg|gif)$/;
616
- var SIGNATURES = [
617
- { type: "image/png", bytes: [137, 80, 78, 71, 13, 10, 26, 10] },
618
- { type: "image/jpeg", bytes: [255, 216, 255] },
619
- { type: "image/gif", bytes: [71, 73, 70, 56] }
620
- ];
621
- function detectImage(bytes) {
622
- if (!bytes || bytes.length < 8) return null;
623
- const found = SIGNATURES.find((s) => s.bytes.every((b, i) => bytes[i] === b));
624
- return found ? { type: found.type, ext: IMAGE_TYPES[found.type] } : null;
625
- }
626
- function imageUploadError(file, { t = identity } = {}) {
627
- if (!(String(file?.type || "") in IMAGE_TYPES)) return t("Format rejected: PNG, JPEG or GIF expected");
628
- if (Number(file?.size || 0) > MAX_IMAGE_BYTES) {
629
- return t("Image too large: {max} MB maximum", { max: MAX_IMAGE_BYTES / (1024 * 1024) });
630
- }
631
- return null;
632
- }
633
-
634
- export {
635
- EMAIL_BUILDER_VERSION,
636
- EMAIL_DOCUMENT_SCHEMA_VERSION,
637
- EMAIL_DEVICES,
638
- EMAIL_MOBILE_WIDTH,
639
- EmailDocumentError,
640
- createDocument,
641
- readDocument,
642
- serializeDocument,
643
- LOCALES,
644
- createTranslator,
645
- translatorFor,
646
- identity,
647
- DEFAULT_VARIABLES,
648
- CUSTOM_GROUP,
649
- VARIABLE_LIMITS,
650
- tokenOf,
651
- keyFromLabel,
652
- defineVariables,
653
- validateCustomVariable,
654
- mergeVariables,
655
- DEFAULT_ASSETS_PATH,
656
- isAssetPath,
657
- absoluteAssetUrls,
658
- applyServiceLinks,
659
- MAX_IMAGE_BYTES,
660
- IMAGE_TYPES,
661
- ASSET_KEY_PATTERN,
662
- detectImage,
663
- imageUploadError
664
- };
665
- //# sourceMappingURL=chunk-3M36VVXS.js.map