best-email-builder 0.5.0 → 0.6.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/CHANGELOG.md +57 -0
- package/README.md +50 -10
- package/dist/ai-server.js +6 -4
- package/dist/ai.js +167 -39
- package/dist/ai.js.map +1 -1
- package/dist/builder.js +9 -9
- package/dist/{chunk-ECHGJZ2W.js → chunk-4U7L2NYQ.js} +32 -3
- package/dist/chunk-4U7L2NYQ.js.map +1 -0
- package/dist/{chunk-6D3SLT5W.js → chunk-5W4TUUPD.js} +4 -4
- package/dist/{chunk-LYMJ2TBX.js → chunk-CNJZLZRH.js} +2 -2
- package/dist/{chunk-DFNHICTF.js → chunk-JI36FNWE.js} +3 -1
- package/dist/chunk-JI36FNWE.js.map +1 -0
- package/dist/{chunk-LXR4QTGP.js → chunk-JU3OUO6X.js} +3 -3
- package/dist/{chunk-PB7YZ3WP.js → chunk-NC2YDZSP.js} +19 -65
- package/dist/chunk-NC2YDZSP.js.map +1 -0
- package/dist/{chunk-MPX5CWTZ.js → chunk-NZIQF7IW.js} +172 -29
- package/dist/chunk-NZIQF7IW.js.map +1 -0
- package/dist/{chunk-2RZHGAH5.js → chunk-OWVXKMLI.js} +59 -2
- package/dist/chunk-OWVXKMLI.js.map +1 -0
- package/dist/{chunk-7U6BAKM7.js → chunk-QPAZLCPO.js} +3 -3
- package/dist/{chunk-7U6BAKM7.js.map → chunk-QPAZLCPO.js.map} +1 -1
- package/dist/{chunk-KXJJSVN5.js → chunk-SAIW46IC.js} +2 -2
- package/dist/{chunk-5SASKCUC.js → chunk-W424KPCR.js} +2 -2
- package/dist/{chunk-3LKF4QBL.js → chunk-XEFBPP5Y.js} +4 -4
- package/dist/index.js +11 -11
- package/dist/library.js +5 -5
- package/dist/server.js +12 -29
- package/dist/server.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/ai/ConnectAi.d.ts +4 -0
- package/dist/types/ai/claude.d.ts +30 -5
- package/dist/types/ai/createAi.d.ts +5 -3
- package/dist/types/ai/draft.d.ts +12 -0
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/keys.d.ts +15 -26
- package/dist/types/ai/providers.d.ts +24 -2
- package/dist/types/ai/server.d.ts +1 -1
- package/dist/types/core/constants.d.ts +1 -1
- package/dist/types/engine/NativeAdapter.d.ts +1 -1
- package/dist/types/renderer/prepareMjml.d.ts +8 -2
- package/dist/types/server/index.d.ts +1 -1
- package/dist/types/server/render.d.ts +5 -2
- package/package.json +1 -1
- package/dist/chunk-2RZHGAH5.js.map +0 -1
- package/dist/chunk-DFNHICTF.js.map +0 -1
- package/dist/chunk-ECHGJZ2W.js.map +0 -1
- package/dist/chunk-MPX5CWTZ.js.map +0 -1
- package/dist/chunk-PB7YZ3WP.js.map +0 -1
- /package/dist/{chunk-6D3SLT5W.js.map → chunk-5W4TUUPD.js.map} +0 -0
- /package/dist/{chunk-LYMJ2TBX.js.map → chunk-CNJZLZRH.js.map} +0 -0
- /package/dist/{chunk-LXR4QTGP.js.map → chunk-JU3OUO6X.js.map} +0 -0
- /package/dist/{chunk-KXJJSVN5.js.map → chunk-SAIW46IC.js.map} +0 -0
- /package/dist/{chunk-5SASKCUC.js.map → chunk-W424KPCR.js.map} +0 -0
- /package/dist/{chunk-3LKF4QBL.js.map → chunk-XEFBPP5Y.js.map} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
useBrand,
|
|
6
6
|
useLayoutWidth,
|
|
7
7
|
withPlaceholderImages
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XEFBPP5Y.js";
|
|
9
9
|
import {
|
|
10
10
|
Button,
|
|
11
11
|
Input,
|
|
@@ -18,11 +18,11 @@ import {
|
|
|
18
18
|
createHttpStorage,
|
|
19
19
|
useSearchShortcut,
|
|
20
20
|
useUi
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-JU3OUO6X.js";
|
|
22
22
|
import {
|
|
23
23
|
brandTemplate,
|
|
24
24
|
brandedSender
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-JI36FNWE.js";
|
|
26
26
|
|
|
27
27
|
// src/ui/library/TemplateLibrary.jsx
|
|
28
28
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -524,4 +524,4 @@ function TemplateLibrary({ locale = "en", messages, onNotify, ...props }) {
|
|
|
524
524
|
export {
|
|
525
525
|
TemplateLibrary
|
|
526
526
|
};
|
|
527
|
-
//# sourceMappingURL=chunk-
|
|
527
|
+
//# sourceMappingURL=chunk-5W4TUUPD.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_VARIABLES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JI36FNWE.js";
|
|
4
4
|
|
|
5
5
|
// src/features/variables/service.js
|
|
6
6
|
function escapeHtml(text) {
|
|
@@ -70,4 +70,4 @@ export {
|
|
|
70
70
|
unknownVariables,
|
|
71
71
|
sampleValues
|
|
72
72
|
};
|
|
73
|
-
//# sourceMappingURL=chunk-
|
|
73
|
+
//# sourceMappingURL=chunk-CNJZLZRH.js.map
|
|
@@ -369,6 +369,7 @@ var fr = {
|
|
|
369
369
|
"Saved \u2014 version {n}.": "Enregistr\xE9 \u2014 version {n}.",
|
|
370
370
|
"Saved at {time}": "Enregistr\xE9 \xE0 {time}",
|
|
371
371
|
"Saved, with {count} MJML warning(s): {message}": "Enregistr\xE9, avec {count} avertissement(s) MJML : {message}",
|
|
372
|
+
"Saved. {count} image(s) have no picture yet: they are left out of the email until you add one.": "Enregistr\xE9. {count} image(s) sans visuel pour l\u2019instant : elles restent hors de l\u2019e-mail tant que vous n\u2019en ajoutez pas.",
|
|
372
373
|
"Saving\u2026": "Enregistrement\u2026",
|
|
373
374
|
"script not allowed": "script interdit",
|
|
374
375
|
"Search variables\u2026": "Rechercher une variable\u2026",
|
|
@@ -1314,6 +1315,7 @@ export {
|
|
|
1314
1315
|
inWhiteSection,
|
|
1315
1316
|
SECTION_TAGS,
|
|
1316
1317
|
LOCKED_TAGS,
|
|
1318
|
+
createId,
|
|
1317
1319
|
createNode,
|
|
1318
1320
|
cloneWithNewIds,
|
|
1319
1321
|
pathTo,
|
|
@@ -1356,4 +1358,4 @@ export {
|
|
|
1356
1358
|
absoluteAssetUrls,
|
|
1357
1359
|
applyServiceLinks
|
|
1358
1360
|
};
|
|
1359
|
-
//# sourceMappingURL=chunk-
|
|
1361
|
+
//# sourceMappingURL=chunk-JI36FNWE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/i18n/fr.js","../src/i18n/index.js","../src/engine/model.js","../src/engine/mjml.js","../src/blocks/shared.js","../src/features/brand/brand.js","../src/features/variables/catalog.js","../src/renderer/links.js"],"sourcesContent":["// French locale. Keys are the English source strings (see src/i18n/index.js).\n// Missing keys: `node scripts/i18n-keys.mjs fr`.\n\n/** @type {Record<string, string>} */\nexport const fr = {\n \"1. Complete your profile\": \"1. Complétez votre profil\",\n \"2. Explore\": \"2. Explorez\",\n \"3. Ask us anything\": \"3. Posez-nous vos questions\",\n \"30% off everything\": \"-30 % sur tout\",\n \"30% off this weekend only\": \"-30 % ce week-end seulement\",\n \"4 columns\": \"4 colonnes\",\n \"4-column section\": \"Section en 4 colonnes\",\n \"A few words about it.\": \"Quelques mots pour le présenter.\",\n \"A new feature\": \"Une nouveauté\",\n \"A sale with a discount code and two products\": \"Une vente avec un code promo et deux produits\",\n \"A tip\": \"Une astuce\",\n \"Add a block\": \"Ajouter un bloc\",\n \"Add to favorites\": \"Ajouter aux favoris\",\n \"All blocks\": \"Tous les blocs\",\n \"All capitals\": \"Tout en majuscules\",\n \"All templates\": \"Tous les modèles\",\n \"All your templates are in the favorites.\": \"Tous vos modèles sont dans les favoris.\",\n \"An empty page to build from scratch\": \"Une page vide pour partir de zéro\",\n \"An evening of talks and drinks with the team and our customers.\": \"Une soirée de rencontres autour d'un verre avec l'équipe et nos clients.\",\n \"An event\": \"Un événement\",\n \"Apply the link\": \"Appliquer le lien\",\n \"Article {n}\": \"Article {n}\",\n \"As typed\": \"Comme saisi\",\n \"Background image\": \"Image de fond\",\n \"Background image with a title and a button\": \"Image de fond avec un titre et un bouton\",\n \"Background width\": \"Largeur du fond\",\n \"Base\": \"Base\",\n \"Base — not editable: start from it to create your template.\": \"Base non modifiable — partez-en pour créer votre modèle.\",\n \"Bases\": \"Bases\",\n \"Blank\": \"Vierge\",\n \"Blog\": \"Blog\",\n \"Bold (Ctrl+B)\": \"Gras (Ctrl+B)\",\n \"Book my seat\": \"Je réserve ma place\",\n \"Builder\": \"Constructeur\",\n \"Buy\": \"Acheter\",\n \"by {name}\": \"par {name}\",\n \"Capitals\": \"Majuscules\",\n \"Choose a template\": \"Choisissez un modèle\",\n \"Choose an image in the panel\": \"Choisissez une image dans le panneau\",\n \"Clear formatting\": \"Effacer la mise en forme\",\n \"Clear the search\": \"Effacer la recherche\",\n \"Contact\": \"Contact\",\n \"Copy HTML\": \"Copier le HTML\",\n \"Could not load the templates.\": \"Impossible de charger les modèles.\",\n \"Created\": \"Créé\",\n \"Date, place and a button to book a seat\": \"Date, lieu et un bouton pour réserver\",\n \"Default ({value})\": \"Par défaut ({value})\",\n \"Delete “{name}”?\": \"Supprimer « {name} » ?\",\n \"Direction\": \"Disposition\",\n \"Download\": \"Télécharger\",\n \"Drag to move\": \"Glisser pour déplacer\",\n \"Drop a block here\": \"Déposez un bloc ici\",\n \"Edit: {element}\": \"Modifier : {element}\",\n \"Email canvas\": \"Aperçu modifiable de l’e-mail\",\n \"Email settings\": \"Réglages de l'e-mail\",\n \"Email template library\": \"Bibliothèque de modèles e-mail\",\n \"Equal\": \"Égales\",\n \"Facebook\": \"Facebook\",\n \"Favorites\": \"Favoris\",\n \"Fill the section\": \"Remplir la section\",\n \"First letters\": \"Premières lettres\",\n \"Get started\": \"Commencer\",\n \"GitHub\": \"GitHub\",\n \"Greets a new customer and lists the first steps\": \"Accueille un nouveau client et liste les premières étapes\",\n \"Heading\": \"Titre\",\n \"Hero banner\": \"Bannière\",\n \"Horizontal\": \"Horizontale\",\n \"HTML copied.\": \"HTML copié.\",\n \"Icon background\": \"Fond de l'icône\",\n \"Icon size\": \"Taille des icônes\",\n \"Icons linking to your profiles\": \"Icônes vers vos profils\",\n \"Image + text\": \"Image + texte\",\n \"Image beside a title, text and button\": \"Image à côté d'un titre, d'un texte et d'un bouton\",\n \"Image position\": \"Position de l'image\",\n \"Image size\": \"Taille de l'image\",\n \"In brief\": \"En bref\",\n \"Instagram\": \"Instagram\",\n \"Italic (Ctrl+I)\": \"Italique (Ctrl+I)\",\n \"Keep its width\": \"Garder sa largeur\",\n \"Label\": \"Texte affiché\",\n \"Large bold title\": \"Grand titre en gras\",\n \"LinkedIn\": \"LinkedIn\",\n \"Logo, headline, text and a button\": \"Logo, titre, texte et un bouton\",\n \"Menu\": \"Menu\",\n \"Menu link\": \"Lien du menu\",\n \"Monthly news: banner, two articles, highlights\": \"Les nouvelles du mois : bannière, deux articles, brèves\",\n \"Network\": \"Réseau\",\n \"No preview: this storage cannot render emails.\": \"Pas d'aperçu : ce stockage ne sait pas compiler les e-mails.\",\n \"No template yet. Start from a base, or create a new one.\": \"Aucun modèle pour l’instant. Partez d’une base, ou créez-en un.\",\n \"Nothing matches “{query}”.\": \"Aucun résultat pour « {query} ».\",\n \"Offer valid until Sunday midnight, while stocks last.\": \"Offre valable jusqu'à dimanche minuit, dans la limite des stocks.\",\n \"On mobile\": \"Sur mobile\",\n \"One sentence about it.\": \"Une phrase pour en parler.\",\n \"One sentence that makes people want to read on.\": \"Une phrase qui donne envie de lire la suite.\",\n \"Origin\": \"Origine\",\n \"Original size\": \"Taille d'origine\",\n \"Our offices, 1 Main Street\": \"Nos bureaux, 1 rue Principale\",\n \"Picking a template replaces the email being edited.\": \"Choisir un modèle remplace l'e-mail en cours de modification.\",\n \"Product one\": \"Produit un\",\n \"Product two\": \"Produit deux\",\n \"Profile URL\": \"Adresse du profil\",\n \"Promotion\": \"Promotion\",\n \"Read more\": \"Lire la suite\",\n \"Ready-made templates to copy into your campaigns.\": \"Modèles prêts à copier dans vos campagnes.\",\n \"Remove from favorites\": \"Retirer des favoris\",\n \"Remove the link\": \"Retirer le lien\",\n \"Replace\": \"Remplacer\",\n \"Replace the current email?\": \"Remplacer l'e-mail actuel ?\",\n \"Reply to this email: a person reads it.\": \"Répondez à cet e-mail : une personne le lit.\",\n \"Row of links to your site\": \"Rangée de liens vers votre site\",\n \"Save the date — seats are limited.\": \"Notez la date — les places sont limitées.\",\n \"Search templates\": \"Rechercher un modèle\",\n \"Seats are limited: book yours now, it takes ten seconds.\": \"Les places sont limitées : réservez la vôtre, cela prend dix secondes.\",\n \"Select a template on the left.\": \"Sélectionnez un modèle à gauche.\",\n \"Select the parent\": \"Sélectionner le bloc parent\",\n \"Shop\": \"Boutique\",\n \"Shop the sale\": \"Voir les offres\",\n \"Simple\": \"Simple\",\n \"So we can tailor what we send you.\": \"Pour adapter ce que nous vous envoyons.\",\n \"Social link\": \"Réseau social\",\n \"Social links\": \"Réseaux sociaux\",\n \"Space around each icon\": \"Espace autour de chaque icône\",\n \"Start from a template\": \"Partir d'un modèle\",\n \"Start from this base\": \"Partir de cette base\",\n \"Strikethrough\": \"Barré\",\n \"Take a tour of what you can do.\": \"Faites le tour de ce que vous pouvez faire.\",\n \"Template deleted.\": \"Modèle supprimé.\",\n \"Template duplicated.\": \"Modèle dupliqué.\",\n \"Templates\": \"Modèles\",\n \"Text formatting\": \"Mise en forme du texte\",\n \"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.\",\n \"The browser refused to copy.\": \"Le navigateur a refusé la copie.\",\n \"The news of the month\": \"Les nouvelles du mois\",\n \"The template and its versions will be deleted.\": \"Le modèle et ses versions seront supprimés.\",\n \"The template preview appears here.\": \"L'aperçu du modèle s'affiche ici.\",\n \"This template was saved in a format this builder cannot open.\": \"Ce modèle a été enregistré dans un format que ce constructeur ne sait pas ouvrir.\",\n \"This weekend only, with the code SPRING30.\": \"Ce week-end seulement, avec le code SPRING30.\",\n \"Three steps to get started.\": \"Trois étapes pour bien commencer.\",\n \"Thursday, 7 pm\": \"Jeudi, 19 h\",\n \"Title\": \"Titre\",\n \"Two or three sentences that sum up the story and make people want the rest.\": \"Deux ou trois phrases qui résument l'article et donnent envie de lire la suite.\",\n \"Underline (Ctrl+U)\": \"Souligné (Ctrl+U)\",\n \"Updated\": \"Modifié\",\n \"Vertical\": \"Verticale\",\n \"Welcome aboard!\": \"Bienvenue à bord !\",\n \"Welcome, {name}!\": \"Bienvenue, {name} !\",\n \"What changed, what is coming, and two stories worth your time.\": \"Ce qui a changé, ce qui arrive, et deux articles qui valent le détour.\",\n \"When\": \"Quand\",\n \"Where\": \"Où\",\n \"Whole image\": \"Image entière\",\n \"Whole screen\": \"Tout l’écran\",\n \"With the code SPRING30, until Sunday midnight.\": \"Avec le code SPRING30, jusqu'à dimanche minuit.\",\n \"X\": \"X\",\n \"You're invited\": \"On vous invite !\",\n \"Your big announcement\": \"Votre grande annonce\",\n \"YouTube\": \"YouTube\",\n \"{count} sections selected. Press Delete to remove them (Ctrl+Z brings them back).\": \"{count} sections sélectionnées. Appuyez sur Suppr pour les supprimer (Ctrl+Z les rétablit).\",\n \"{label} — {side}\": \"{label} — {side}\",\n \"{max} test addresses at most\": \"{max} adresses de test au maximum\",\n \"{message} Your version can be saved as a copy.\": \"{message} Votre version peut être enregistrée comme copie.\",\n \"{name} (copy)\": \"{name} (copie)\",\n \"{sent} sent, {failed} failed.\": \"{sent} parti(s), {failed} en échec.\",\n \"{sent} test(s) sent — check your inbox.\": \"{sent} test(s) parti(s) — ouvrez votre messagerie avant de continuer.\",\n \"1 column\": \"1 colonne\",\n \"2 columns\": \"2 colonnes\",\n \"2-column section\": \"Section en 2 colonnes\",\n \"25%\": \"25 %\",\n \"3 columns\": \"3 colonnes\",\n \"3-column section\": \"Section en 3 colonnes\",\n \"50%\": \"50 %\",\n \"600 px (standard)\": \"600 px (standard)\",\n \"640 px\": \"640 px\",\n \"75%\": \"75 %\",\n \"Address not allowed\": \"Adresse refusée\",\n \"Addresses\": \"Adresses\",\n \"Alignment\": \"Alignement\",\n \"Alt text\": \"Texte alternatif\",\n \"An https:// address is required\": \"Une adresse https:// est requise\",\n \"attribute {name} not allowed\": \"attribut {name} interdit\",\n \"Authentication required\": \"Authentification requise\",\n \"Autosave\": \"Sauvegarde automatique\",\n \"Autosave paused\": \"Sauvegarde automatique suspendue\",\n \"Autosaved at {time}\": \"Enregistré automatiquement à {time}\",\n \"Autosaving…\": \"Sauvegarde automatique…\",\n \"Available in every template.\": \"Disponible dans tous les modèles.\",\n \"Back\": \"Retour\",\n \"Background\": \"Fond\",\n \"base tag not allowed\": \"balise base interdite\",\n \"Blocks\": \"Blocs\",\n \"Bold\": \"Gras\",\n \"Bottom\": \"Bas\",\n \"Button\": \"Bouton\",\n \"Button background\": \"Fond du bouton\",\n \"Button padding\": \"Marges du bouton\",\n \"Button without link ({count})\": \"Bouton sans lien ({count})\",\n \"Call to action\": \"Appel à l’action\",\n \"Campaign\": \"Campagne\",\n \"Campaign name\": \"Nom de la campagne\",\n \"Cancel\": \"Annuler\",\n \"CDATA section not allowed\": \"section CDATA interdite\",\n \"Center\": \"Centre\",\n \"Close\": \"Fermer\",\n \"Colors\": \"Couleurs\",\n \"Column\": \"Colonne\",\n \"Company\": \"Société\",\n \"component script not allowed\": \"script de composant interdit\",\n \"conditional comment not allowed\": \"commentaire conditionnel interdit\",\n \"Confirm\": \"Confirmer\",\n \"Content\": \"Contenu\",\n \"Content width\": \"Largeur du contenu\",\n \"Copy saved: “{name}”.\": \"Copie enregistrée : « {name} ».\",\n \"Corner radius\": \"Arrondi\",\n \"Create\": \"Créer\",\n \"Create “{name}”\": \"Créer « {name} »\",\n \"current\": \"actuelle\",\n \"Custom\": \"Personnalisées\",\n \"Custom variables are not configured\": \"Les variables personnalisées ne sont pas configurées\",\n \"Dashed\": \"Tirets\",\n \"Default\": \"Par défaut\",\n \"Delete\": \"Supprimer\",\n \"Delete (Del)\": \"Supprimer (Suppr)\",\n \"Delete the variable “{name}”?\": \"Supprimer la variable « {name} » ?\",\n \"Describes the image if it is blocked\": \"Décrit l’image si elle est bloquée\",\n \"Deselect\": \"Désélectionner\",\n \"Desktop\": \"Bureau\",\n \"Device\": \"Appareil\",\n \"Dismiss\": \"Masquer\",\n \"Divider\": \"Séparateur\",\n \"Dotted\": \"Pointillés\",\n \"Double-click the text to edit it.\": \"Double-cliquez sur le texte pour le modifier.\",\n \"Duplicate\": \"Dupliquer\",\n \"Edit\": \"Modifier\",\n \"Edit “{name}”\": \"Modifier « {name} »\",\n \"Edit variable\": \"Modifier la variable\",\n \"Element actions\": \"Actions sur l’élément\",\n \"Email\": \"Courriel\",\n \"Email address\": \"Adresse e-mail\",\n \"Email preview\": \"Aperçu du courriel\",\n \"embed not allowed\": \"embed interdit\",\n \"Empty file\": \"Fichier vide\",\n \"Empty MJML\": \"MJML vide\",\n \"Empty MJML compilation\": \"Compilation MJML vide\",\n \"Empty subject\": \"Objet vide\",\n \"executable content not allowed\": \"contenu exécutable interdit\",\n \"external resource not allowed\": \"ressource externe interdite\",\n \"File missing\": \"Fichier manquant\",\n \"Final rendering, variables filled with samples.\": \"Rendu final, variables remplies d’exemples.\",\n \"First name\": \"Prénom\",\n \"Fit to text\": \"Ajustée au texte\",\n \"Font\": \"Police\",\n \"forbidden element in project: {tag}\": \"élément interdit dans le projet : {tag}\",\n \"form field not allowed\": \"champ de formulaire interdit\",\n \"form not allowed\": \"formulaire interdit\",\n \"Format rejected: PNG, JPEG or GIF expected\": \"Format refusé : PNG, JPEG ou GIF attendu\",\n \"frame not allowed\": \"frame interdite\",\n \"Full length\": \"Toute la largeur\",\n \"Full width\": \"Pleine largeur\",\n \"Full-width section\": \"Section pleine largeur\",\n \"Give the template a name before saving it.\": \"Donnez un libellé au modèle avant de l’enregistrer.\",\n \"Group\": \"Groupe\",\n \"Height\": \"Hauteur\",\n \"Hello {name},\": \"Bonjour {name},\",\n \"Hex code\": \"Code hexadécimal\",\n \"Horizontal rule\": \"Filet horizontal\",\n \"HTML button not allowed (use the Button block)\": \"bouton HTML interdit (utiliser le bloc Bouton)\",\n \"iframe not allowed\": \"iframe interdite\",\n \"Ignored (invalid): {email}\": \"Ignorée (invalide) : {email}\",\n \"Image\": \"Image\",\n \"image rejected ({name})\": \"image refusée ({name})\",\n \"image rejected ({name}): https only\": \"image refusée ({name}) : https seulement\",\n \"Image too large: {max} MB maximum\": \"Image trop lourde : {max} Mo au maximum\",\n \"Image without alt text ({count})\": \"Texte alternatif manquant ({count} image(s))\",\n \"Image without file ({count})\": \"Image sans fichier ({count})\",\n \"Inbox\": \"Boîte de réception\",\n \"Indicative preview — check it with “Send test”.\": \"Aperçu indicatif — validez avec « Envoyer un test ».\",\n \"inline style rejected\": \"style en ligne refusé\",\n \"Insecure http link ({count})\": \"Lien non sécurisé en http ({count})\",\n \"Insert a variable in the preview text\": \"Insérer une variable dans le texte d’aperçu\",\n \"Insert a variable in the subject\": \"Insérer une variable dans l’objet\",\n \"Internal error\": \"Erreur interne\",\n \"Invalid JSON\": \"JSON invalide\",\n \"Justify\": \"Justifié\",\n \"Key is required\": \"Balise requise\",\n \"Last name\": \"Nom\",\n \"Layout\": \"Mise en page\",\n \"Learn more\": \"Découvrir\",\n \"Leave\": \"Quitter\",\n \"Leave the builder?\": \"Quitter le constructeur ?\",\n \"Left\": \"Gauche\",\n \"Length\": \"Longueur\",\n \"Line color\": \"Couleur du filet\",\n \"Line height\": \"Interligne\",\n \"Line style\": \"Trait\",\n \"Link\": \"Lien\",\n \"Link on click\": \"Lien au clic\",\n \"Link URL\": \"Adresse du lien\",\n \"Links\": \"Liens\",\n \"Loading the builder\": \"Chargement du constructeur\",\n \"Loading…\": \"Chargement…\",\n \"Lowercase letters, digits, \\\"_\\\" and \\\".\\\" only, starting with a letter\": \"Minuscules, chiffres, « _ » et « . » seulement, en commençant par une lettre\",\n \"MathML not allowed\": \"MathML interdit\",\n \"Message is {size} KB: Gmail clips messages over {limit} KB\": \"Message de {size} Ko : Gmail le coupe au-delà de {limit} Ko\",\n \"meta tag not allowed\": \"balise meta interdite\",\n \"Middle\": \"Milieu\",\n \"missing <mj-body>\": \"<mj-body> manquant\",\n \"Missing unsubscribe link\": \"Lien de désinscription absent\",\n \"mj-font (external font) is outside the builder\": \"mj-font (police externe) est hors du constructeur\",\n \"mj-include would read a file from the server\": \"mj-include lirait un fichier du serveur\",\n \"mj-raw (free HTML) is not allowed\": \"mj-raw (HTML libre) est refusé\",\n \"mj-style (free CSS) is outside the builder\": \"mj-style (CSS libre) est hors du constructeur\",\n \"MJML too large ({size} KB, maximum {max} KB)\": \"MJML trop volumineux ({size} ko, maximum {max} ko)\",\n \"Mobile\": \"Mobile\",\n \"Move down\": \"Descendre\",\n \"Move left\": \"Déplacer à gauche\",\n \"Move right\": \"Déplacer à droite\",\n \"Move up\": \"Monter\",\n \"Multipart form expected\": \"Formulaire multipart attendu\",\n \"Name\": \"Libellé\",\n \"Name is required\": \"Libellé requis\",\n \"Name required\": \"Libellé requis\",\n \"New template\": \"Nouveau modèle\",\n \"New variable\": \"Nouvelle variable\",\n \"No changes\": \"Aucune modification\",\n \"No preview text\": \"Aucun texte d’aperçu\",\n \"No subject\": \"Sans objet\",\n \"No usable test address\": \"Aucune adresse de test exploitable\",\n \"No variable found.\": \"Aucune variable trouvée.\",\n \"No versions yet.\": \"Aucune version.\",\n \"Normal\": \"Normale\",\n \"Nothing to fix.\": \"Aucun point à corriger.\",\n \"Nothing to show yet.\": \"Rien à afficher pour l’instant.\",\n \"object not allowed\": \"object interdit\",\n \"Order number\": \"Numéro de commande\",\n \"Padding\": \"Marges\",\n \"Page background\": \"Fond de page\",\n \"Paragraph or heading\": \"Paragraphe ou titre\",\n \"Photo, visual or logo\": \"Photo, visuel ou logo\",\n \"Pick a colour\": \"Choisir une couleur\",\n \"Points to check\": \"Points à vérifier\",\n \"Preview\": \"Aperçu\",\n \"Preview and tests\": \"Aperçu et tests\",\n \"Preview text\": \"Texte d’aperçu\",\n \"Project rejected: {issues}\": \"Projet refusé : {issues}\",\n \"project too deep\": \"projet trop profond\",\n \"project too large ({size} KB, maximum {max} KB)\": \"projet trop volumineux ({size} ko, maximum {max} ko)\",\n \"Properties\": \"Propriétés\",\n \"Recipient\": \"Destinataire\",\n \"Redo\": \"Rétablir\",\n \"Redo (Ctrl+Y)\": \"Rétablir (Ctrl+Y)\",\n \"Refresh\": \"Rafraîchir\",\n \"Rendering…\": \"Rendu en cours…\",\n \"Request too large\": \"Requête trop volumineuse\",\n \"Restore\": \"Recharger\",\n \"Restore version {n}\": \"Recharger la version {n}\",\n \"Restore version {n}?\": \"Recharger la version {n} ?\",\n \"Results\": \"Résultats\",\n \"Resume\": \"Reprendre\",\n \"Resume the draft?\": \"Reprendre le brouillon ?\",\n \"Retry\": \"Réessayer\",\n \"Right\": \"Droite\",\n \"Route not found\": \"Route introuvable\",\n \"Sample value\": \"Valeur d’exemple\",\n \"Save\": \"Enregistrer\",\n \"Save a copy\": \"Enregistrer une copie\",\n \"Save the template first\": \"Enregistrez d’abord le modèle\",\n \"Saved\": \"Enregistré\",\n \"Saved — version {n}.\": \"Enregistré — version {n}.\",\n \"Saved at {time}\": \"Enregistré à {time}\",\n \"Saved, with {count} MJML warning(s): {message}\": \"Enregistré, avec {count} avertissement(s) MJML : {message}\",\n \"Saved. {count} image(s) have no picture yet: they are left out of the email until you add one.\": \"Enregistré. {count} image(s) sans visuel pour l’instant : elles restent hors de l’e-mail tant que vous n’en ajoutez pas.\",\n \"Saving…\": \"Enregistrement…\",\n \"script not allowed\": \"script interdit\",\n \"Search variables…\": \"Rechercher une variable…\",\n \"Section\": \"Section\",\n \"Select a text or a button first.\": \"Sélectionnez d’abord un texte ou un bouton.\",\n \"Send\": \"Envoyer\",\n \"Send a test\": \"Envoyer un test\",\n \"Shown after the subject in the inbox\": \"Affiché après l’objet dans la boîte de réception\",\n \"Size\": \"Taille\",\n \"Solid\": \"Plein\",\n \"Spacer\": \"Espace\",\n \"Spacing\": \"Espacement\",\n \"Start over\": \"Partir de zéro\",\n \"style sheet not allowed\": \"feuille de style interdite\",\n \"Subject\": \"Objet\",\n \"SVG not allowed\": \"SVG interdit\",\n \"Tag\": \"Balise\",\n \"tag not allowed: <{tag}>\": \"balise non admise : <{tag}>\",\n \"Template\": \"Modèle\",\n \"Template changed in the meantime\": \"Modèle modifié entre-temps\",\n \"Template changed in the meantime — reload it before saving\": \"Modèle modifié entre-temps — rechargez-le avant d’enregistrer\",\n \"Template changed in the meantime by {user} — reload it before saving\": \"Modèle modifié entre-temps par {user} — rechargez-le avant d’enregistrer\",\n \"Template not found\": \"Modèle introuvable\",\n \"Template rejected: {issues}\": \"Modèle refusé : {issues}\",\n \"Template rejected: {issues} (+{more})\": \"Modèle refusé : {issues} (+{more})\",\n \"Template saved.\": \"Modèle enregistré.\",\n \"Templates that use it will show its “value if empty”.\": \"Les modèles qui l’utilisent afficheront sa « valeur si vide ».\",\n \"Test\": \"Tester\",\n \"Test sending is not configured\": \"L’envoi de test n’est pas configuré\",\n \"Text\": \"Texte\",\n \"The builder could not load.\": \"Le constructeur n’a pas pu se charger.\",\n \"The builder could not start.\": \"Le constructeur n’a pas pu démarrer.\",\n \"the document must start with <mjml>\": \"le document doit commencer par <mjml>\",\n \"The draft will be offered again next time.\": \"Le brouillon reste proposé à la prochaine ouverture.\",\n \"The email on screen, to reviewers’ addresses.\": \"Le courriel à l’écran, aux adresses de relecture.\",\n \"The template could not be loaded.\": \"Le modèle n’a pas pu être chargé.\",\n \"Thickness\": \"Épaisseur\",\n \"This key is already used\": \"Cette balise existe déjà\",\n \"To check\": \"À vérifier\",\n \"Too many test sends — try again later\": \"Trop d’envois de test — réessayez plus tard\",\n \"Too many uploads — try again later\": \"Trop de dépôts d’images — réessayez plus tard\",\n \"Top\": \"Haut\",\n \"Typography\": \"Typographie\",\n \"Undo\": \"Annuler\",\n \"Undo (Ctrl+Z)\": \"Annuler (Ctrl+Z)\",\n \"Unknown variables (rendered empty): {tags}\": \"Variables inconnues (affichées vides) : {tags}\",\n \"Unsaved changes\": \"Modifications non enregistrées\",\n \"Unsaved changes will be lost.\": \"Les modifications non enregistrées seront perdues.\",\n \"Unsaved changes will be replaced.\": \"Les modifications non enregistrées seront remplacées.\",\n \"Unsaved template from {when}.\": \"Modèle non enregistré du {when}.\",\n \"Unsubscribe\": \"Se désinscrire\",\n \"Unsubscribe link\": \"Lien de désinscription\",\n \"Upload\": \"Déposer\",\n \"Upload failed\": \"Dépôt impossible\",\n \"URL rejected ({name})\": \"adresse refusée ({name})\",\n \"Value if empty\": \"Valeur si vide\",\n \"Variable “{name}” created and inserted.\": \"Variable « {name} » créée et insérée.\",\n \"Variable “{name}” created.\": \"Variable « {name} » créée.\",\n \"Variable “{name}” saved.\": \"Variable « {name} » enregistrée.\",\n \"Variable deleted.\": \"Variable supprimée.\",\n \"Variable not found\": \"Variable introuvable\",\n \"Variables\": \"Variables\",\n \"Version {n}\": \"Version {n}\",\n \"Version {n} restored — save to keep it.\": \"Version {n} rechargée — enregistrez pour la garder.\",\n \"Version not found\": \"Version introuvable\",\n \"Versions\": \"Versions\",\n \"Vertical alignment\": \"Alignement vertical\",\n \"Vertical space\": \"Vide vertical\",\n \"Weight\": \"Graisse\",\n \"When a recipient has none\": \"Quand un destinataire n’en a pas\",\n \"Width\": \"Largeur\",\n \"Your company\": \"Votre société\",\n \"Your headline\": \"Titre du message\",\n \"Your text.\": \"Votre texte.\",\n \"“{name}” template applied.\": \"Modèle « {name} » appliqué.\",\n \"€49 instead of €70\": \"49 € au lieu de 70 €\",\n \"Cancelled\": \"Annulé\",\n \"Email generation is not configured\": \"La génération d'e-mails n'est pas configurée\",\n \"Too many generated emails — try again later\": \"Trop d'e-mails générés — réessayez plus tard\",\n \"The AI service did not answer — try again in a moment.\": \"Le service d'IA n'a pas répondu — réessayez dans un instant.\",\n \"Color scheme\": \"Thème de couleurs\",\n \"Light mode\": \"Mode clair\",\n \"Dark mode\": \"Mode sombre\",\n \"Dark mode as Gmail and Outlook apps show it: light backgrounds turn dark, dark text turns light, images stay as they are.\": \"Mode sombre tel que l'affichent les applis Gmail et Outlook : les fonds clairs deviennent sombres, le texte foncé devient clair, les images restent telles quelles.\",\n \"Subject ideas are not configured\": \"Les idées d'objet ne sont pas configurées\",\n \"Too many subject ideas — try again later\": \"Trop de demandes d'idées d'objet — réessayez plus tard\",\n \"Text rewriting is not configured\": \"La réécriture de texte n'est pas configurée\",\n \"Too many rewrites — try again later\": \"Trop de réécritures — réessayez plus tard\",\n \"Brand\": \"Marque\",\n \"Your brand\": \"Votre marque\",\n \"Set it once: new emails, templates and blocks take your logo, colors and font.\": \"À définir une fois : les nouveaux e-mails, modèles et blocs prennent votre logo, vos couleurs et votre police.\",\n \"Your website\": \"Votre site web\",\n \"Get my brand\": \"Récupérer ma marque\",\n \"Reading the website…\": \"Lecture du site…\",\n \"The logo, colors and font are read from it. You can change them before saving.\": \"Le logo, les couleurs et la police y sont lus. Vous pourrez les modifier avant d'enregistrer.\",\n \"The website could not be read.\": \"Le site n'a pas pu être lu.\",\n \"Logo\": \"Logo\",\n \"Remove\": \"Retirer\",\n \"or an image address: https://…\": \"ou l'adresse d'une image : https://…\",\n \"Company name\": \"Nom de l'entreprise\",\n \"Main color\": \"Couleur principale\",\n \"Buttons, banners, links\": \"Boutons, bandeaux, liens\",\n \"Headings color\": \"Couleur des titres\",\n \"Also apply to this email\": \"Appliquer aussi à cet e-mail\",\n \"Brand applied. Ctrl+Z brings back the previous look.\": \"Marque appliquée. Ctrl+Z rétablit l'apparence précédente.\",\n \"Brand saved. New emails and blocks use it.\": \"Marque enregistrée. Les nouveaux e-mails et blocs l'utilisent.\",\n \"The brand could not be saved.\": \"La marque n'a pas pu être enregistrée.\",\n \"Big news\": \"Grande nouvelle\",\n \"A short line under the title.\": \"Une courte ligne sous le titre.\",\n \"A headline\": \"Un titre\",\n \"Some text, with\": \"Un peu de texte, avec\",\n \"a link\": \"un lien\",\n \"The brand is not configured\": \"La marque n'est pas configurée\",\n \"Reading a brand from a website is not configured\": \"La lecture d'une marque depuis un site n'est pas configurée\",\n \"Too many websites read — try again later\": \"Trop de sites lus — réessayez plus tard\",\n \"Enter the address of a website, like acme.com\": \"Saisissez l'adresse d'un site, par exemple acme.com\",\n \"This address is not a public website\": \"Cette adresse n'est pas un site public\",\n \"The website could not be reached. Check the address.\": \"Le site est injoignable. Vérifiez l'adresse.\",\n \"The website took too long to answer\": \"Le site a mis trop de temps à répondre\",\n \"The website does not let the builder read it. Fill in your brand below.\": \"Le site ne se laisse pas lire par l'éditeur. Renseignez votre marque ci-dessous.\",\n \"This address is not a web page\": \"Cette adresse n'est pas une page web\",\n \"The website redirects too many times\": \"Le site redirige trop de fois\",\n \"The website sent too much data\": \"Le site a envoyé trop de données\",\n \"No logo was found on the website. Upload it below.\": \"Aucun logo trouvé sur le site. Importez-le ci-dessous.\",\n \"The logo on the website is an SVG or WebP image, which most email apps do not show. Upload a PNG or JPEG version.\": \"Le logo du site est une image SVG ou WebP, que la plupart des messageries n'affichent pas. Importez une version PNG ou JPEG.\",\n \"The website icon is used as the logo. Upload your full logo if you have one.\": \"L'icône du site sert de logo. Importez votre logo complet si vous en avez un.\",\n \"No main color was found on the website. Pick one below.\": \"Aucune couleur principale trouvée sur le site. Choisissez-en une ci-dessous.\",\n};\n","// @ts-check\n/**\n * Translations.\n *\n * Source strings are written in English directly in the code and double as\n * translation keys (`t('Save')`). A locale is a plain object mapping those\n * English strings to translated ones; a missing entry falls back to English.\n * Placeholders use `{name}`: `t('Saved — version {n}.', { n: 3 })`.\n *\n * The same translator serves the browser (builder interface) and the server\n * (validation and API error messages).\n */\n\nimport { fr } from './fr.js';\n\n/** @typedef {Record<string, string>} Messages */\n/** @typedef {(text: string, params?: Record<string, string|number|null|undefined>) => string} Translate */\n\n/** Built-in locales. English is the source language: an empty dictionary. */\nexport const LOCALES = /** @type {Record<string, Messages>} */ ({ en: {}, fr });\n\n/**\n * @param {Messages} [messages]\n * @returns {Translate}\n */\nexport function createTranslator(messages = {}) {\n return (text, params) => {\n const template = Object.prototype.hasOwnProperty.call(messages, text) ? messages[text] : text;\n if (!params) return template;\n return template.replace(/\\{(\\w+)\\}/g, (match, key) =>\n params[key] === undefined || params[key] === null ? match : String(params[key])\n );\n };\n}\n\n/**\n * Translator for a locale code (`'fr'`, `'fr-FR'`, `'en'`…), optionally\n * extended with application-specific strings.\n *\n * @param {string} [locale]\n * @param {Messages} [overrides]\n * @returns {Translate}\n */\nexport function translatorFor(locale = 'en', overrides = {}) {\n return createTranslator({ ...messagesOf(LOCALES, locale), ...overrides });\n}\n\n/**\n * The dictionary of a locale among several (`'fr-FR'` finds `fr`); an empty\n * one when there is none.\n * @param {Record<string, Messages>|null|undefined} dictionaries\n * @param {string} [locale]\n * @returns {Messages}\n */\nexport function messagesOf(dictionaries, locale = 'en') {\n if (!dictionaries) return {};\n const code = String(locale).toLowerCase();\n const own = (/** @type {string} */ key) => (Object.prototype.hasOwnProperty.call(dictionaries, key) ? dictionaries[key] : null);\n return own(code) || own(code.slice(0, 2)) || {};\n}\n\n/** English, untouched: the default everywhere a translator is optional. */\nexport const identity = createTranslator();\n","// @ts-check\n/**\n * THE EMAIL MODEL — the builder's own document, an MJML tree with ids.\n *\n * { id, tag: 'mj-text', attributes: { color: '#2b3038' }, children: [], content: 'Hello' }\n *\n * One node per MJML element. \"Ending\" elements (`mj-text`, `mj-button`…)\n * keep their inner HTML in `content`; the others hold `children`. Ids are\n * opaque, unique within a tree, made of `[a-z0-9]` so they can live in a CSS\n * class (the canvas finds an element by them, see `canvasMjml.js`).\n *\n * Every operation is PURE: it returns a new tree and never changes the one it\n * got, sharing the untouched branches. The undo history is then just a list\n * of trees.\n *\n * Dependency-free: runs in the browser, on the server and in unit tests.\n */\n\n/**\n * @typedef {{\n * id: string,\n * tag: string,\n * attributes: Record<string, string>,\n * children: EmailNode[],\n * content: string|null,\n * }} EmailNode\n */\n\n/** MJML elements whose inner content is HTML (MJML calls them \"ending tags\"). */\nexport const ENDING_TAGS = new Set([\n 'mj-text',\n 'mj-button',\n 'mj-navbar-link',\n 'mj-social-element',\n 'mj-table',\n 'mj-raw',\n 'mj-title',\n 'mj-preview',\n 'mj-style',\n 'mj-accordion-title',\n 'mj-accordion-text',\n]);\n\n/** Elements that hold a whole section's place in the body. */\nexport const SECTION_TAGS = new Set(['mj-section', 'mj-wrapper', 'mj-hero']);\n\n/** Elements that are never removed, moved or duplicated. */\nexport const LOCKED_TAGS = new Set(['mjml', 'mj-head', 'mj-body']);\n\nlet counter = 0;\n\n/** A fresh id: short, unique for the page's lifetime, `[a-z0-9]` only. */\nexport function createId() {\n counter = (counter + 1) % 1679616; // 36^4\n return `n${Date.now().toString(36).slice(-5)}${counter.toString(36).padStart(4, '0')}${Math.floor(Math.random() * 1296)\n .toString(36)\n .padStart(2, '0')}`;\n}\n\n/**\n * @param {string} tag\n * @param {{attributes?: Record<string, string>, children?: EmailNode[], content?: string|null, id?: string}} [parts]\n * @returns {EmailNode}\n */\nexport function createNode(tag, { attributes = {}, children = [], content = null, id = createId() } = {}) {\n return { id, tag, attributes: { ...attributes }, children: [...children], content: ENDING_TAGS.has(tag) ? String(content ?? '') : null };\n}\n\n/**\n * Deep copy with NEW ids everywhere (duplicate, paste, insert a block twice).\n * @param {EmailNode} node\n * @returns {EmailNode}\n */\nexport function cloneWithNewIds(node) {\n return {\n id: createId(),\n tag: node.tag,\n attributes: { ...node.attributes },\n children: node.children.map(cloneWithNewIds),\n content: node.content,\n };\n}\n\n/* ── Reading ─────────────────────────────────────────────────────────────── */\n\n/**\n * The node with this id and its ancestors (root first), or null.\n * @param {EmailNode} root @param {string} id\n * @returns {EmailNode[]|null}\n */\nexport function pathTo(root, id) {\n if (root.id === id) return [root];\n for (const child of root.children) {\n const path = pathTo(child, id);\n if (path) return [root, ...path];\n }\n return null;\n}\n\n/** @param {EmailNode} root @param {string|null|undefined} id */\nexport function findNode(root, id) {\n if (!id) return null;\n const path = pathTo(root, id);\n return path ? path[path.length - 1] : null;\n}\n\n/** @param {EmailNode} root @param {string} id */\nexport function parentOf(root, id) {\n const path = pathTo(root, id);\n return path && path.length > 1 ? path[path.length - 2] : null;\n}\n\n/**\n * Closest node (itself included) whose tag is one of `tags`.\n * @param {EmailNode} root @param {string} id @param {Set<string>|string[]} tags\n */\nexport function closestOfTags(root, id, tags) {\n const set = tags instanceof Set ? tags : new Set(tags);\n const path = pathTo(root, id);\n if (!path) return null;\n for (let i = path.length - 1; i >= 0; i -= 1) if (set.has(path[i].tag)) return path[i];\n return null;\n}\n\n/** First node of a tag, depth first. @param {EmailNode} root @param {string} tag @returns {EmailNode|null} */\nexport function findFirst(root, tag) {\n if (root.tag === tag) return root;\n for (const child of root.children) {\n const found = findFirst(child, tag);\n if (found) return found;\n }\n return null;\n}\n\n/** Every node, depth first. @param {EmailNode} root @param {(node: EmailNode) => void} visit */\nexport function walk(root, visit) {\n visit(root);\n for (const child of root.children) walk(child, visit);\n}\n\n/* ── Writing (pure) ──────────────────────────────────────────────────────── */\n\n/**\n * The tree with the node `id` replaced by `update(node)`.\n * @param {EmailNode} root @param {string} id @param {(node: EmailNode) => EmailNode} update\n * @returns {EmailNode}\n */\nexport function updateNode(root, id, update) {\n if (root.id === id) return update(root);\n let changed = false;\n const children = root.children.map((child) => {\n const next = updateNode(child, id, update);\n if (next !== child) changed = true;\n return next;\n });\n return changed ? { ...root, children } : root;\n}\n\n/**\n * Sets (string) or removes (null, '') attributes.\n * @param {EmailNode} root @param {string} id @param {Record<string, string|null|undefined>} attributes\n */\nexport function setAttributes(root, id, attributes) {\n return updateNode(root, id, (node) => {\n const next = { ...node.attributes };\n for (const [key, value] of Object.entries(attributes)) {\n if (value === null || value === undefined || value === '') delete next[key];\n else next[key] = String(value);\n }\n return { ...node, attributes: next };\n });\n}\n\n/** @param {EmailNode} root @param {string} id @param {string} content */\nexport function setContent(root, id, content) {\n return updateNode(root, id, (node) => (node.content === null ? node : { ...node, content: String(content ?? '') }));\n}\n\n/**\n * Inserts nodes into `parentId` at `index` (clamped).\n * @param {EmailNode} root @param {string} parentId @param {number} index @param {EmailNode[]} nodes\n */\nexport function insertNodes(root, parentId, index, nodes) {\n return updateNode(root, parentId, (parent) => {\n const at = Math.max(0, Math.min(index, parent.children.length));\n const children = [...parent.children];\n children.splice(at, 0, ...nodes);\n return { ...parent, children };\n });\n}\n\n/** @param {EmailNode} root @param {string} id */\nexport function removeNode(root, id) {\n const parent = parentOf(root, id);\n if (!parent) return root;\n return updateNode(root, parent.id, (p) => ({ ...p, children: p.children.filter((c) => c.id !== id) }));\n}\n\n/**\n * Moves a node under `parentId` at `index`, where `index` counts the target's\n * children BEFORE the node leaves its place (what a drop indicator shows).\n * @param {EmailNode} root @param {string} id @param {string} parentId @param {number} index\n */\nexport function moveNode(root, id, parentId, index) {\n const node = findNode(root, id);\n const from = parentOf(root, id);\n if (!node || !from) return root;\n // A node cannot go inside itself.\n if (pathTo(node, parentId)) return root;\n let at = index;\n if (from.id === parentId) {\n const current = from.children.findIndex((c) => c.id === id);\n if (current < at) at -= 1;\n if (current === at) return root;\n }\n return insertNodes(removeNode(root, id), parentId, at, [node]);\n}\n\n/**\n * Index of a node among its siblings, and their count.\n * @param {EmailNode} root @param {string} id\n */\nexport function positionOf(root, id) {\n const parent = parentOf(root, id);\n if (!parent) return { parent: null, index: -1, count: 0 };\n return { parent, index: parent.children.findIndex((c) => c.id === id), count: parent.children.length };\n}\n\n/* ── Snapshot (the stored project) ───────────────────────────────────────── */\n\n/** Shape version of the stored project (`{ format, root }`). */\nexport const PROJECT_FORMAT = 1;\n\n/** @param {EmailNode} root */\nexport function toProject(root) {\n return { format: PROJECT_FORMAT, root: strip(root) };\n}\n\n/** @param {EmailNode} node @returns {any} */\nfunction strip(node) {\n /** @type {any} */\n const out = { id: node.id, tag: node.tag };\n if (Object.keys(node.attributes).length) out.attributes = { ...node.attributes };\n if (node.content !== null) out.content = node.content;\n if (node.children.length) out.children = node.children.map(strip);\n return out;\n}\n\n/**\n * Reads a stored project back into a tree. Ids are kept when they are valid\n * and unique, replaced otherwise. Throws on anything that is not a project of\n * this format.\n * @param {unknown} project\n * @returns {EmailNode}\n */\nexport function fromProject(project) {\n const value = /** @type {any} */ (project);\n if (!value || typeof value !== 'object' || value.format !== PROJECT_FORMAT || !value.root) {\n throw new Error('Not a project of this builder');\n }\n const seen = new Set();\n /** @param {any} raw @returns {EmailNode} */\n const read = (raw) => {\n if (!raw || typeof raw !== 'object' || typeof raw.tag !== 'string' || !/^(mjml|mj-[a-z-]+)$/.test(raw.tag)) {\n throw new Error('Invalid element in the project');\n }\n let id = typeof raw.id === 'string' && /^[a-z0-9]{1,40}$/.test(raw.id) ? raw.id : createId();\n if (seen.has(id)) id = createId();\n seen.add(id);\n /** @type {Record<string, string>} */\n const attributes = {};\n for (const [k, v] of Object.entries(raw.attributes || {})) {\n if (/^[a-z][a-z0-9-]*$/i.test(k) && v !== null && v !== undefined) attributes[k] = String(v);\n }\n return createNode(raw.tag, {\n id,\n attributes,\n content: ENDING_TAGS.has(raw.tag) ? String(raw.content ?? '') : null,\n children: ENDING_TAGS.has(raw.tag) ? [] : (Array.isArray(raw.children) ? raw.children : []).map(read),\n });\n };\n const root = read(value.root);\n if (root.tag !== 'mjml' || !findFirst(root, 'mj-body')) throw new Error('The project has no email body');\n return root;\n}\n","// @ts-check\n/**\n * MJML ⇄ MODEL — reading MJML into the builder's tree, writing it back.\n *\n * MJML is close enough to XML to be read by a small scanner, and only two\n * kinds of element exist:\n * - containers (`mj-section`, `mj-column`…), made of other MJML elements;\n * - \"ending\" elements (`mj-text`, `mj-button`…), whose inside is HTML kept\n * as is, up to the matching closing tag.\n * Anything else between containers (stray text, comments, HTML outside an\n * ending element) is not MJML and is dropped.\n *\n * Attribute values are DECODED in the model (`&` → `&`) and encoded again\n * on write: the model holds what the attribute means, the MJML how to write\n * it.\n *\n * Dependency-free: runs in the browser, on the server and in unit tests.\n */\n\nimport { ENDING_TAGS, createNode, findFirst } from './model.js';\n\n/** @typedef {import('./model.js').EmailNode} EmailNode */\n\nconst NAMED_ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '\"', apos: \"'\", nbsp: ' ' };\n\n/** @param {string} value */\nexport function decodeAttribute(value) {\n return value.replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z]+);/gi, (match, entity) => {\n if (entity[0] === '#') {\n const code = entity[1] === 'x' || entity[1] === 'X' ? parseInt(entity.slice(2), 16) : parseInt(entity.slice(1), 10);\n return Number.isFinite(code) && code > 0 && code < 0x110000 ? String.fromCodePoint(code) : match;\n }\n const named = /** @type {Record<string, string>} */ (NAMED_ENTITIES)[entity.toLowerCase()];\n return named ?? match;\n });\n}\n\n/** @param {string} value */\nexport function encodeAttribute(value) {\n return String(value).replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\n}\n\n/**\n * Reads the attributes of an opening tag, from `start` (just after the name)\n * to its `>`. Quotes may contain `>`.\n * @param {string} source @param {number} start\n * @returns {{attributes: Record<string, string>, end: number, selfClosing: boolean}}\n */\nfunction readAttributes(source, start) {\n /** @type {Record<string, string>} */\n const attributes = {};\n let i = start;\n const n = source.length;\n while (i < n) {\n while (i < n && /\\s/.test(source[i])) i += 1;\n if (source[i] === '>') return { attributes, end: i + 1, selfClosing: false };\n if (source[i] === '/' && source[i + 1] === '>') return { attributes, end: i + 2, selfClosing: true };\n if (i >= n) break;\n const nameMatch = /^[^\\s=/>\"']+/.exec(source.slice(i, i + 200));\n if (!nameMatch) {\n i += 1;\n continue;\n }\n const name = nameMatch[0].toLowerCase();\n i += nameMatch[0].length;\n while (i < n && /\\s/.test(source[i])) i += 1;\n let value = '';\n if (source[i] === '=') {\n i += 1;\n while (i < n && /\\s/.test(source[i])) i += 1;\n const quote = source[i];\n if (quote === '\"' || quote === \"'\") {\n const close = source.indexOf(quote, i + 1);\n const stop = close === -1 ? n : close;\n value = source.slice(i + 1, stop);\n i = stop + 1;\n } else {\n let bare = /^[^\\s>]*/.exec(source.slice(i))?.[0] || '';\n // `width=100/>`: the slash closes the tag, it is not in the value.\n if (bare.endsWith('/') && source[i + bare.length] === '>') bare = bare.slice(0, -1);\n value = bare;\n i += bare.length;\n }\n }\n attributes[name] = decodeAttribute(value);\n }\n return { attributes, end: n, selfClosing: false };\n}\n\n/**\n * MJML elements of a fragment (no `<mjml>` needed): `<mj-section>…</mj-section><mj-section>…`.\n * @param {string} source\n * @returns {EmailNode[]}\n */\nexport function parseMjmlFragment(source) {\n const text = String(source || '').replace(/^\\uFEFF/, '');\n /** @type {EmailNode} */\n const top = createNode('mj-fragment');\n /** @type {EmailNode[]} */\n const stack = [top];\n let i = 0;\n const n = text.length;\n\n while (i < n) {\n const lt = text.indexOf('<', i);\n if (lt === -1) break;\n i = lt;\n if (text.startsWith('<!--', i)) {\n const close = text.indexOf('-->', i + 4);\n i = close === -1 ? n : close + 3;\n continue;\n }\n if (text.startsWith('<?', i) || text.startsWith('<!', i)) {\n const close = text.indexOf('>', i);\n i = close === -1 ? n : close + 1;\n continue;\n }\n const closing = text[i + 1] === '/';\n const nameMatch = /^[a-z][a-z0-9-]*/i.exec(text.slice(i + (closing ? 2 : 1), i + 60));\n if (!nameMatch) {\n i += 1;\n continue;\n }\n const tag = nameMatch[0].toLowerCase();\n const afterName = i + (closing ? 2 : 1) + nameMatch[0].length;\n const isMjml = tag === 'mjml' || tag.startsWith('mj-');\n\n if (closing) {\n const close = text.indexOf('>', afterName);\n i = close === -1 ? n : close + 1;\n if (!isMjml) continue;\n // Close up to the matching element (tolerates a forgotten closing tag).\n for (let depth = stack.length - 1; depth > 0; depth -= 1) {\n if (stack[depth].tag === tag) {\n stack.length = depth;\n break;\n }\n }\n continue;\n }\n\n const { attributes, end, selfClosing } = readAttributes(text, afterName);\n i = end;\n if (!isMjml) continue;\n\n const parent = stack[stack.length - 1];\n if (ENDING_TAGS.has(tag)) {\n let content = '';\n if (!selfClosing) {\n const closeRe = new RegExp(`</${tag}\\\\s*>`, 'ig');\n closeRe.lastIndex = i;\n const close = closeRe.exec(text);\n const stop = close ? close.index : n;\n content = text.slice(i, stop).trim();\n i = close ? close.index + close[0].length : n;\n }\n parent.children.push(createNode(tag, { attributes, content }));\n continue;\n }\n const node = createNode(tag, { attributes });\n parent.children.push(node);\n if (!selfClosing) stack.push(node);\n }\n return top.children;\n}\n\n/**\n * A whole email. Always returns an `mjml` root holding an `mj-body`: a bare\n * fragment is wrapped, a missing body is added.\n * @param {string} source\n * @returns {EmailNode}\n */\nexport function parseMjml(source) {\n const nodes = parseMjmlFragment(source);\n let root = nodes.find((node) => node.tag === 'mjml') || null;\n if (!root) {\n const body = nodes.find((node) => node.tag === 'mj-body');\n const head = nodes.find((node) => node.tag === 'mj-head');\n root = createNode('mjml', {\n children: [\n ...(head ? [head] : []),\n body || createNode('mj-body', { children: nodes.filter((node) => node.tag !== 'mj-head') }),\n ],\n });\n }\n if (!findFirst(root, 'mj-body')) root.children.push(createNode('mj-body'));\n return root;\n}\n\n/**\n * The MJML of a tree. `pretty`: one element per line, indented (what is\n * stored and downloaded); otherwise on one line.\n * @param {EmailNode} node\n * @param {{pretty?: boolean, attributes?: (node: EmailNode) => Record<string, string>, extraChildren?: (node: EmailNode) => string}} [options]\n * `attributes`: replaces a node's attributes on write (the canvas adds its\n * markers there); `extraChildren`: MJML appended inside a container.\n */\nexport function serializeMjml(node, options = {}) {\n const { pretty = true } = options;\n /** @param {EmailNode} current @param {number} depth @returns {string} */\n const write = (current, depth) => {\n const indent = pretty ? ' '.repeat(depth) : '';\n const attributes = options.attributes ? options.attributes(current) : current.attributes;\n const attrs = Object.entries(attributes)\n .map(([key, value]) => ` ${key}=\"${encodeAttribute(value)}\"`)\n .join('');\n if (current.content !== null) {\n return `${indent}<${current.tag}${attrs}>${current.content}</${current.tag}>`;\n }\n const inner = current.children.map((child) => write(child, depth + 1));\n const extra = options.extraChildren ? options.extraChildren(current) : '';\n if (extra) inner.push(pretty ? `${' '.repeat(depth + 1)}${extra}` : extra);\n if (!inner.length) return `${indent}<${current.tag}${attrs} />`;\n return pretty\n ? `${indent}<${current.tag}${attrs}>\\n${inner.join('\\n')}\\n${indent}</${current.tag}>`\n : `<${current.tag}${attrs}>${inner.join('')}</${current.tag}>`;\n };\n return write(node, 0);\n}\n","// @ts-check\n/**\n * Building blocks shared by all blocks: palette, fonts, recurring properties.\n *\n * -- DEFAULT PALETTE ---------------------------------------------------------\n * Plain hex values: email clients load no stylesheet and understand neither\n * `var()` nor modern color functions, so every value ends up inline in a\n * `style=\"\"` attribute.\n *\n * -- EVERY BLOCK SETS ITS OWN TEXT ATTRIBUTES --------------------------------\n * Font, size, line height and color are written on the element itself, never\n * left to MJML defaults (the \"Ubuntu\" font loaded from Google, line height 1):\n * the canvas and the sent email then render the same thing, and Outlook gets\n * explicit values.\n *\n * Fonts: only web-safe stacks, installed everywhere and understood by\n * Outlook - a web font only renders in a minority of email clients.\n */\n\n/** @typedef {import('../core/EmailEditor.js').BlockProperty} BlockProperty */\n\n/** Text for an MJML attribute or element. @param {string} text */\nexport function escapeXml(text) {\n return String(text ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/** The default palette. */\nexport const PALETTE = {\n ink: '#2b3038',\n inkSoft: '#41506b',\n navy: '#212e5a',\n brand: '#2977bc',\n border: '#dfe6f0',\n canvas: '#eceff4',\n surface: '#ffffff',\n white: '#ffffff',\n};\n\n/** Default link of a new button. */\nexport const DEFAULT_BUTTON_URL = 'https://example.com';\n\nexport const FONT_STACK = 'Arial, Helvetica, sans-serif';\n\n/** @type {{value: string, label: string}[]} */\nexport const FONT_OPTIONS = [\n { value: 'Arial, Helvetica, sans-serif', label: 'Arial' },\n { value: 'Verdana, Geneva, sans-serif', label: 'Verdana' },\n { value: 'Tahoma, Geneva, sans-serif', label: 'Tahoma' },\n { value: \"'Trebuchet MS', Helvetica, sans-serif\", label: 'Trebuchet' },\n { value: 'Georgia, Times, serif', label: 'Georgia' },\n { value: \"'Times New Roman', Times, serif\", label: 'Times' },\n { value: \"'Courier New', Courier, monospace\", label: 'Courier' },\n];\n\n/** @type {{value: string, label: string}[]} */\nexport const ALIGN_OPTIONS = [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n];\n\n/** Properties panel groups, in display order. */\nexport const GROUPS = {\n content: 'Content',\n link: 'Link',\n typography: 'Typography',\n colors: 'Colors',\n layout: 'Layout',\n spacing: 'Spacing',\n};\n\n/** Recurring properties. */\nexport const prop = {\n /** @returns {BlockProperty} */\n fontFamily: () => ({ key: 'font-family', label: 'Font', type: 'select', group: GROUPS.typography, options: FONT_OPTIONS }),\n /** @returns {BlockProperty} */\n fontSize: (min = 10, max = 48) => ({ key: 'font-size', label: 'Size', type: 'size', group: GROUPS.typography, min, max, unit: 'px' }),\n /** @returns {BlockProperty} */\n lineHeight: () => ({ key: 'line-height', label: 'Line height', type: 'size', group: GROUPS.typography, min: 10, max: 80, unit: 'px' }),\n /** @returns {BlockProperty} */\n color: (label = 'Text') => ({ key: 'color', label, type: 'color', group: GROUPS.colors }),\n /** @returns {BlockProperty} */\n background: (key = 'background-color', label = 'Background') => ({ key, label, type: 'color', group: GROUPS.colors }),\n /** @returns {BlockProperty} */\n align: (options = ALIGN_OPTIONS) => ({ key: 'align', label: 'Alignment', type: 'align', group: GROUPS.layout, options }),\n /** @returns {BlockProperty} */\n padding: (key = 'padding', label = 'Padding') => ({ key, label, type: 'spacing', group: GROUPS.spacing }),\n /** @returns {BlockProperty} */\n href: (label = 'Link URL') => ({ key: 'href', label, type: 'url', group: GROUPS.link, placeholder: 'https://' }),\n /** @returns {BlockProperty} */\n radius: (max = 40) => ({ key: 'border-radius', label: 'Corner radius', type: 'size', group: GROUPS.layout, min: 0, max, unit: 'px' }),\n};\n\n/**\n * White one-column section: the wrapper of a content block dropped outside\n * any column (MJML does not accept text directly inside the body).\n *\n * Warning: padding in LONGHAND form, never `padding=\"...\"`. The canvas starts\n * from the longhand defaults (20 px for a section), which override the\n * shorthand, while the export omits them - the screen and the email would\n * diverge (see core/mjmlAttributes).\n *\n * @param {string} inner\n */\nexport function inWhiteSection(inner) {\n return (\n `<mj-section background-color=\"${PALETTE.surface}\" padding-top=\"0px\" padding-bottom=\"0px\">` +\n `<mj-column>${inner}</mj-column></mj-section>`\n );\n}\n","// @ts-check\n/**\n * BRAND — the look of a company's emails: its name, its logo, a main color,\n * a color for headings and a font. Set once (by hand, or read from the\n * company's website), it is used by every new email, template, block and\n * generated email, and can be applied to the email being edited.\n *\n * `applyBrand` repaints an email by ROLE, not by exact color, so it works on\n * any email — a template, a generated one, one the user built:\n *\n * buttons filled with the main color, text in the color that\n * reads on it (white or ink);\n * colored sections (banners) take the main color; their texts and\n * buttons switch to what reads on it;\n * headings (bold, 18 px and more, on white) take the headings\n * color; body text and gray footers keep theirs;\n * colored links take the main color, or the headings color when the\n * main one is too light to read on white;\n * fonts every text, button and menu link;\n * header (optional) the logo — or the name — at the top, and\n * the name in the footer.\n *\n * Dependency-free: runs in the browser, on the server and in unit tests.\n */\n\nimport { FONT_OPTIONS, PALETTE, escapeXml } from '../../blocks/shared.js';\nimport { createNode } from '../../engine/model.js';\nimport { parseMjml, parseMjmlFragment, serializeMjml } from '../../engine/mjml.js';\n\n/**\n * @typedef {{\n * name?: string,\n * logoUrl?: string,\n * logoWidth?: number,\n * primary?: string,\n * heading?: string,\n * font?: string,\n * website?: string,\n * }} Brand\n * `primary`: buttons, banners, links; `heading`: headings; colors as\n * `#rrggbb`. `font`: one of the builder's web-safe stacks (FONT_OPTIONS).\n * `website`: where it was read from, to read it again.\n *\n * @typedef {import('../../engine/model.js').EmailNode} EmailNode\n */\n\nconst HEX = /^#[0-9a-f]{6}$/;\nconst FONTS = new Set(FONT_OPTIONS.map((option) => option.value));\nexport const MAX_LOGO_WIDTH = 240;\n\n/* ── Colors ─────────────────────────────────────────────────────────────── */\n\n/**\n * A CSS color as RGB(A), or null: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(),\n * rgba(), hsl(), hsla(), and the keywords white and black.\n * @param {unknown} value\n * @returns {{r: number, g: number, b: number, a: number}|null}\n */\nexport function parseColor(value) {\n const source = String(value ?? '').trim().toLowerCase();\n if (source === 'white') return { r: 255, g: 255, b: 255, a: 1 };\n if (source === 'black') return { r: 0, g: 0, b: 0, a: 1 };\n let match = source.match(/^#([0-9a-f]{3,8})$/);\n if (match) {\n const hex = match[1];\n if (hex.length === 3 || hex.length === 4) {\n const [r, g, b, a = 'ff'] = hex.split('').map((c) => c + c);\n return { r: parseInt(r, 16), g: parseInt(g, 16), b: parseInt(b, 16), a: parseInt(a, 16) / 255 };\n }\n if (hex.length === 6 || hex.length === 8) {\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n a: hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1,\n };\n }\n return null;\n }\n match = source.match(/^(rgba?|hsla?)\\(([^)]*)\\)$/);\n if (!match) return null;\n const parts = match[2].split(/[\\s,/]+/).filter(Boolean);\n if (parts.length < 3) return null;\n const alpha = parts[3] === undefined ? 1 : parts[3].endsWith('%') ? parseFloat(parts[3]) / 100 : parseFloat(parts[3]);\n if (match[1].startsWith('rgb')) {\n const channel = (/** @type {string} */ part) => (part.endsWith('%') ? (parseFloat(part) * 255) / 100 : parseFloat(part));\n const [r, g, b] = parts.slice(0, 3).map(channel);\n if ([r, g, b, alpha].some((n) => Number.isNaN(n))) return null;\n const clamp = (/** @type {number} */ n) => Math.max(0, Math.min(255, Math.round(n)));\n return { r: clamp(r), g: clamp(g), b: clamp(b), a: alpha };\n }\n const h = parseFloat(parts[0]);\n const s = parseFloat(parts[1]) / 100;\n const l = parseFloat(parts[2]) / 100;\n if ([h, s, l, alpha].some((n) => Number.isNaN(n))) return null;\n return { ...fromHsl(((h % 360) + 360) % 360, Math.max(0, Math.min(1, s)), Math.max(0, Math.min(1, l))), a: alpha };\n}\n\n/** @param {number} h @param {number} s @param {number} l */\nfunction fromHsl(h, s, l) {\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = l - c / 2;\n const [r, g, b] = h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];\n return { r: Math.round((r + m) * 255), g: Math.round((g + m) * 255), b: Math.round((b + m) * 255) };\n}\n\n/** @param {{r: number, g: number, b: number}} color */\nexport const toHex = ({ r, g, b }) => `#${[r, g, b].map((n) => n.toString(16).padStart(2, '0')).join('')}`;\n\n/** Hue (0-360), saturation and lightness (0-1). @param {{r: number, g: number, b: number}} color */\nexport function toHsl({ r, g, b }) {\n const [R, G, B] = [r / 255, g / 255, b / 255];\n const max = Math.max(R, G, B);\n const min = Math.min(R, G, B);\n const l = (max + min) / 2;\n if (max === min) return { h: 0, s: 0, l };\n const d = max - min;\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n const h = max === R ? ((G - B) / d + (G < B ? 6 : 0)) * 60 : max === G ? ((B - R) / d + 2) * 60 : ((R - G) / d + 4) * 60;\n return { h, s, l };\n}\n\n/** WCAG relative luminance. @param {{r: number, g: number, b: number}} color */\nfunction luminance({ r, g, b }) {\n const linear = (/** @type {number} */ n) => {\n const c = n / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);\n}\n\n/** WCAG contrast ratio of two colors (1 to 21). @param {string} a @param {string} b */\nexport function contrastRatio(a, b) {\n const ca = parseColor(a);\n const cb = parseColor(b);\n if (!ca || !cb) return 1;\n const [hi, lo] = [luminance(ca), luminance(cb)].sort((x, y) => y - x);\n return (hi + 0.05) / (lo + 0.05);\n}\n\n/** The text color that reads on `background`: white or ink. @param {string} background */\nexport function readableOn(background) {\n return contrastRatio(background, PALETTE.white) >= contrastRatio(background, PALETTE.ink) ? PALETTE.white : PALETTE.ink;\n}\n\n/**\n * A color meant to be noticed (a banner, a filled button), as opposed to\n * white, light tints, grays and near-black.\n * @param {unknown} value\n */\nexport function isColored(value) {\n const color = parseColor(value);\n if (!color || color.a < 0.5) return false;\n const { s, l } = toHsl(color);\n return s >= 0.2 && l >= 0.08 && l <= 0.88;\n}\n\n/** Dark enough to carry white text (navy, charcoal…). @param {unknown} value */\nfunction isDark(value) {\n const color = parseColor(value);\n return Boolean(color && color.a >= 0.5 && luminance(color) < 0.12);\n}\n\n/** A light gray (footers, captions): left as it is. @param {unknown} value */\nfunction isMuted(value) {\n const color = parseColor(value);\n if (!color) return false;\n const { s, l } = toHsl(color);\n return s < 0.2 && l > 0.4;\n}\n\n/* ── The brand itself ───────────────────────────────────────────────────── */\n\n/** @param {unknown} value */\nconst cleanColor = (value) => {\n const color = parseColor(value);\n return color && color.a >= 0.5 ? toHex(color) : '';\n};\n\n/**\n * A brand as stored and used: unknown fields dropped, colors as #rrggbb,\n * the font one of the builder's stacks, the logo an https or site-relative\n * image. Whatever is invalid is left out (the builder's default is used).\n * @param {unknown} input\n * @returns {Brand}\n */\nexport function cleanBrand(input) {\n const source = /** @type {Record<string, unknown>} */ (input && typeof input === 'object' ? input : {});\n /** @type {Brand} */\n const brand = {};\n const name = typeof source.name === 'string' ? source.name.replace(/\\s+/g, ' ').trim().slice(0, 80) : '';\n if (name) brand.name = name;\n const logo = typeof source.logoUrl === 'string' ? source.logoUrl.trim() : '';\n if (logo && logo.length <= 2000 && (/^https:\\/\\/[^\\s\"'<>]+$/i.test(logo) || /^\\/(?!\\/)[^\\s\"'<>]*$/.test(logo))) {\n brand.logoUrl = logo;\n const width = Math.round(Number(source.logoWidth));\n if (width >= 24 && width <= MAX_LOGO_WIDTH) brand.logoWidth = width;\n }\n const primary = cleanColor(source.primary);\n if (primary && HEX.test(primary)) brand.primary = primary;\n const heading = cleanColor(source.heading);\n if (heading && HEX.test(heading)) brand.heading = heading;\n if (typeof source.font === 'string' && FONTS.has(source.font)) brand.font = source.font;\n const website = typeof source.website === 'string' ? source.website.trim() : '';\n if (/^https?:\\/\\/[^\\s\"'<>]+$/i.test(website) && website.length <= 500) brand.website = website;\n return brand;\n}\n\n/** Does the brand change anything? @param {Brand|null|undefined} brand */\nexport const hasBrand = (brand) => Boolean(brand && Object.keys(cleanBrand(brand)).some((key) => key !== 'website'));\n\n/**\n * The sender the templates use: the brand's name and logo over the host's\n * sender (whose postal address stays).\n * @param {import('../templates/starter.js').StarterSender|null|undefined} sender\n * @param {Brand|null|undefined} brand\n */\nexport function brandedSender(sender, brand) {\n const base = { ...(sender || {}) };\n if (!brand) return base;\n if (brand.name) base.name = brand.name;\n if (brand.logoUrl) {\n base.logoUrl = brand.logoUrl;\n base.logoWidth = brand.logoWidth || base.logoWidth;\n }\n return base;\n}\n\n/**\n * The colors applyBrand paints with.\n * @param {Brand} brand\n */\nfunction paletteOf(brand) {\n const primary = brand.primary || null;\n return {\n primary,\n onPrimary: primary ? readableOn(primary) : null,\n heading: brand.heading || null,\n // A link must read on white: a too-light main color gives way.\n link: primary ? (contrastRatio(primary, PALETTE.white) >= 3 ? primary : brand.heading || PALETTE.ink) : null,\n font: brand.font || null,\n };\n}\n\n/* ── Painting ───────────────────────────────────────────────────────────── */\n\nconst SECTION_LIKE = new Set(['mj-section', 'mj-wrapper', 'mj-hero', 'mj-column', 'mj-group']);\nconst FONT_TAGS = new Set(['mj-text', 'mj-button', 'mj-navbar-link', 'mj-social-element', 'mj-table']);\n\n/** @param {string|undefined} size */\nconst px = (size) => {\n const n = parseFloat(String(size ?? ''));\n return Number.isNaN(n) ? 0 : n;\n};\n\n/** @param {string|undefined} weight */\nconst isBold = (weight) => /^(bold|bolder|[6-9]00)$/.test(String(weight ?? '').trim());\n\n/**\n * The links of a text in a new color, when theirs is a color (a gray or a\n * black link is a choice, and stays).\n * @param {string} html @param {string} color\n */\nfunction recolorLinks(html, color) {\n return html.replace(/<a\\b[^>]*>/gi, (tag) =>\n tag.replace(/(style\\s*=\\s*\")([^\"]*)(\")/i, (_, open, style, close) =>\n `${open}${style.replace(/(^|;)(\\s*color\\s*:\\s*)([^;]+)/i, (whole, sep, prop, value) =>\n isColored(value) ? `${sep}${prop}${color}` : whole\n )}${close}`\n )\n );\n}\n\n/**\n * One node and what it holds, in the brand's colors.\n * @param {EmailNode} node\n * @param {ReturnType<typeof paletteOf>} palette\n * @param {boolean} onColor the node sits on a colored background\n * @returns {EmailNode}\n */\nfunction paint(node, palette, onColor) {\n const attributes = { ...node.attributes };\n let content = node.content;\n let childrenOnColor = onColor;\n const set = (/** @type {string} */ key, /** @type {string|null} */ value) => {\n if (value) attributes[key] = value;\n };\n\n if (SECTION_LIKE.has(node.tag)) {\n const background = attributes['background-color'];\n if (background && palette.primary && (isColored(background) || (isDark(background) && node.tag !== 'mj-column'))) {\n attributes['background-color'] = palette.primary;\n childrenOnColor = true;\n } else if (background && parseColor(background)) {\n childrenOnColor = false;\n }\n }\n\n if (FONT_TAGS.has(node.tag) && palette.font) set('font-family', palette.font);\n\n if (node.tag === 'mj-text' && content !== null) {\n if (onColor) {\n set('color', palette.onPrimary);\n } else {\n const heading = isBold(attributes['font-weight']) && px(attributes['font-size']) >= 18;\n if (heading && palette.heading && !isMuted(attributes.color)) attributes.color = palette.heading;\n if (palette.link) content = recolorLinks(content, palette.link);\n }\n }\n\n if (node.tag === 'mj-button') {\n const background = attributes['background-color'];\n // MJML's own default is a dark gray: a button without color is filled.\n const filled = !background || isColored(background) || isDark(background);\n if (palette.primary && filled) {\n set('background-color', onColor ? palette.onPrimary : palette.primary);\n set('color', onColor ? palette.primary : palette.onPrimary);\n } else if (palette.primary && onColor) {\n // A light button on a banner: kept light, its text in the main color.\n set('color', readableOn(background) === PALETTE.ink && contrastRatio(palette.primary, background) < 3 ? PALETTE.ink : palette.primary);\n } else if (palette.link && isColored(attributes.color)) {\n // An outlined button on white.\n attributes.color = palette.link;\n if (isColored(attributes['border-color'])) attributes['border-color'] = palette.link;\n }\n }\n\n if (node.tag === 'mj-navbar-link' && isColored(attributes.color)) {\n set('color', onColor ? palette.onPrimary : palette.link);\n }\n\n const children = node.children.map((child) => paint(child, palette, childrenOnColor));\n return { ...node, attributes, content, children };\n}\n\n/** @param {string} html */\nconst plainText = (html) =>\n html\n .replace(/<[^>]*>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/\\s+/g, ' ')\n .trim();\n\n/** @param {EmailNode} body */\nconst sectionsOf = (body) =>\n body.children.flatMap((child) => (child.tag === 'mj-wrapper' ? child.children.filter((c) => c.tag === 'mj-section') : [child]));\n\n/**\n * The logo, or the name, at the top; the name in the footer.\n * @param {EmailNode} root\n * @param {Brand} brand\n * @param {{names: string[], logos: string[]}} previous what the email may still show\n * @returns {EmailNode}\n */\nfunction rebrandHeader(root, brand, previous) {\n const body = root.children.find((child) => child.tag === 'mj-body');\n if (!body) return root;\n const names = previous.names.map((name) => String(name || '').trim()).filter((name) => name.length >= 2);\n const known = new Set(names.map((name) => name.toLowerCase()));\n const sections = sectionsOf(body);\n /** @type {Map<string, EmailNode>} */\n const replaced = new Map();\n\n const top = sections[0];\n const column = top?.children.find((child) => child.tag === 'mj-column');\n const first = column?.children[0];\n if (first?.tag === 'mj-image') {\n const small = px(first.attributes.width) > 0 && px(first.attributes.width) <= MAX_LOGO_WIDTH;\n const wasLogo = small || previous.logos.includes(first.attributes.src) || known.has(String(first.attributes.alt || '').toLowerCase());\n if (wasLogo && brand.logoUrl) {\n replaced.set(first.id, {\n ...first,\n attributes: {\n ...first.attributes,\n src: brand.logoUrl,\n alt: brand.name || first.attributes.alt || '',\n width: `${brand.logoWidth || Math.min(px(first.attributes.width) || 140, MAX_LOGO_WIDTH)}px`,\n },\n });\n }\n } else if (first?.tag === 'mj-text' && first.content !== null && known.has(plainText(first.content).toLowerCase())) {\n if (brand.logoUrl) {\n const keep = Object.fromEntries(Object.entries(first.attributes).filter(([key]) => key.startsWith('padding') || key === 'align'));\n replaced.set(\n first.id,\n createNode('mj-image', {\n id: first.id,\n attributes: { src: brand.logoUrl, alt: brand.name || '', width: `${brand.logoWidth || 140}px`, align: 'left', ...keep },\n })\n );\n } else if (brand.name) {\n replaced.set(first.id, { ...first, content: escapeXml(brand.name) });\n }\n }\n\n // The footer: the old name in its line becomes the new one.\n const bottom = sections.length > 1 ? sections[sections.length - 1] : null;\n const newName = brand.name;\n if (bottom && newName) {\n const swap = (/** @type {EmailNode} */ node) => {\n if (node.tag === 'mj-text' && node.content !== null) {\n let html = node.content;\n for (const name of names) {\n if (name !== newName) html = html.split(escapeXml(name)).join(escapeXml(newName));\n }\n if (html !== node.content) replaced.set(node.id, { ...node, content: html });\n }\n node.children.forEach(swap);\n };\n swap(bottom);\n }\n\n if (!replaced.size) return root;\n const rebuild = (/** @type {EmailNode} */ node) => replaced.get(node.id) || { ...node, children: node.children.map(rebuild) };\n return rebuild(root);\n}\n\n/**\n * An email in the brand's look.\n * @param {string} mjml\n * @param {Brand|null|undefined} brand\n * @param {{header?: boolean, previous?: {names?: (string|undefined)[], logos?: (string|undefined)[]}}} [options]\n * `header`: also put the logo (or name) at the top and the name in the\n * footer, replacing what `previous` lists (the names and logos the email\n * may still show: the host's sender, the brand before, \"Your company\").\n * @returns {string}\n */\nexport function applyBrand(mjml, brand, { header = false, previous = {} } = {}) {\n const clean = cleanBrand(brand);\n if (!hasBrand(clean)) return mjml;\n let root = parseMjml(mjml);\n root = paint(root, paletteOf(clean), false);\n if (header) {\n root = rebrandHeader(root, clean, {\n names: /** @type {string[]} */ ((previous.names || []).filter(Boolean)),\n logos: /** @type {string[]} */ ((previous.logos || []).filter(Boolean)),\n });\n }\n return serializeMjml(root);\n}\n\n/**\n * A fragment (a block about to be inserted) in the brand's look.\n * @param {string} mjml @param {Brand|null|undefined} brand\n */\nexport function applyBrandToFragment(mjml, brand) {\n const clean = cleanBrand(brand);\n if (!mjml || !hasBrand(clean)) return mjml;\n const palette = paletteOf(clean);\n return parseMjmlFragment(mjml)\n .map((node) => serializeMjml(paint(node, palette, false), { pretty: false }))\n .join('');\n}\n\n/**\n * A ready-made email (the starter, a gallery template) in the brand's look,\n * its header and footer naming the brand rather than the host's sender or\n * the \"Your company\" placeholder.\n * @param {string} mjml\n * @param {Brand|null|undefined} brand\n * @param {{sender?: import('../templates/starter.js').StarterSender|null, t?: (key: string) => string}} [context]\n */\nexport function brandTemplate(mjml, brand, { sender = null, t = (key) => key } = {}) {\n return applyBrand(mjml, brand, {\n header: true,\n previous: { names: [sender?.name, t('Your company')], logos: [sender?.logoUrl] },\n });\n}\n","// @ts-check\n/**\n * Variables catalog — the merge tags a template can contain.\n *\n * The editor only inserts TAGS (`{{contact.firstname}}`); it never knows a\n * value. Rendering (server side) replaces them:\n *\n * preview, test → sample values (`sample`), the real address for a test;\n * sending → each recipient's values (`applyVariables`), or the merge\n * tags of an email provider (`mapVariables`).\n *\n * A missing value falls back to the tag's inline default\n * (`{{contact.firstname|there}}`), then to the variable's `fallback`, then to\n * an empty string — never to the raw tag.\n *\n * Two kinds of variables:\n * - SYSTEM variables, given by the application (`variables` prop / option),\n * read-only in the interface — this list is the default;\n * - CUSTOM variables, created by users in the builder and kept by the\n * storage (`listVariables` / `saveVariable` / `deleteVariable`), shared by\n * every template.\n *\n * The unsubscribe link is `{{ unsubscribe }}`: the sending pipeline replaces it\n * with the right link (see `applyServiceLinks`).\n *\n * Dependency-free: shared by the browser and the server.\n */\n\nimport { identity } from '../../i18n/index.js';\n\n/**\n * @typedef {{\n * key: string,\n * label: string,\n * group: string,\n * sample: string,\n * fallback?: string,\n * description?: string,\n * token?: string,\n * kind?: 'text'|'link',\n * custom?: boolean,\n * }} EmailVariable\n */\n\n/** @type {EmailVariable[]} */\nexport const DEFAULT_VARIABLES = [\n { key: 'contact.firstname', label: 'First name', group: 'Recipient', sample: 'Jane' },\n { key: 'contact.lastname', label: 'Last name', group: 'Recipient', sample: 'Doe' },\n { key: 'contact.email', label: 'Email address', group: 'Recipient', sample: 'jane.doe@example.com' },\n { key: 'company.name', label: 'Company', group: 'Recipient', sample: 'Example Inc.' },\n { key: 'campaign.name', label: 'Campaign name', group: 'Campaign', sample: 'Newsletter' },\n {\n key: 'unsubscribe',\n label: 'Unsubscribe link',\n group: 'Links',\n sample: '',\n token: '{{ unsubscribe }}',\n kind: 'link',\n },\n];\n\n/** Group of custom variables created without a group. */\nexport const CUSTOM_GROUP = 'Custom';\n\n/** Keys reserved by the sending pipeline. */\nconst RESERVED_KEYS = new Set(['unsubscribe', 'mirror']);\n\nexport const VARIABLE_LIMITS = { key: 60, label: 80, group: 40, sample: 200, fallback: 200, description: 200 };\n\nconst KEY_SHAPE = /^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$/;\n\n/** Tag inserted by the editor. @param {EmailVariable} variable */\nexport function tokenOf(variable) {\n return variable.token || `{{${variable.key}}}`;\n}\n\n/**\n * @param {string} key\n * @param {EmailVariable[]} [variables]\n */\nexport function findVariable(key, variables = DEFAULT_VARIABLES) {\n return variables.find((v) => v.key === key) || null;\n}\n\n/**\n * A key proposed from a label: \"Order number\" → `order_number`,\n * \"Numéro de commande\" → `numero_de_commande`.\n *\n * @param {string} label\n */\nexport function keyFromLabel(label) {\n const key = String(label || '')\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .replace(/^[^a-z]+/, '')\n .slice(0, VARIABLE_LIMITS.key)\n .replace(/_+$/, '');\n return key || 'variable';\n}\n\n/**\n * Checks an application-provided catalog (keys must be unique and safe to\n * embed in a tag).\n *\n * @param {EmailVariable[]} variables\n * @returns {EmailVariable[]}\n */\nexport function defineVariables(variables) {\n const seen = new Set();\n for (const variable of variables) {\n if (!variable || !KEY_SHAPE.test(variable.key || '')) {\n throw new Error(`variables: invalid key \"${variable?.key}\"`);\n }\n if (seen.has(variable.key)) throw new Error(`variables: duplicate key \"${variable.key}\"`);\n seen.add(variable.key);\n }\n return variables;\n}\n\nconst clean = (value, max) => String(value ?? '').replace(/\\s+/g, ' ').trim().slice(0, max);\n\n/**\n * Validates and normalizes a CUSTOM variable typed by a user.\n * Returns `{ variable }` or `{ errors: { field: message } }`.\n *\n * @param {Partial<EmailVariable>} input\n * @param {{\n * existing?: EmailVariable[],\n * editing?: string|null,\n * t?: import('../../i18n/index.js').Translate,\n * }} [options] `editing`: key of the variable being edited (its key cannot change)\n */\nexport function validateCustomVariable(input, { existing = [], editing = null, t = identity } = {}) {\n /** @type {Record<string, string>} */\n const errors = {};\n const label = clean(input?.label, VARIABLE_LIMITS.label);\n const key = editing || String(input?.key || '').trim();\n if (!label) errors.label = t('Name is required');\n if (!editing) {\n if (!key) errors.key = t('Key is required');\n else if (key.length > VARIABLE_LIMITS.key || !KEY_SHAPE.test(key)) {\n errors.key = t('Lowercase letters, digits, \"_\" and \".\" only, starting with a letter');\n } else if (RESERVED_KEYS.has(key) || existing.some((v) => v.key === key)) {\n errors.key = t('This key is already used');\n }\n }\n if (Object.keys(errors).length) return { errors };\n return {\n variable: {\n key,\n label,\n group: clean(input?.group, VARIABLE_LIMITS.group) || CUSTOM_GROUP,\n sample: clean(input?.sample, VARIABLE_LIMITS.sample),\n fallback: clean(input?.fallback, VARIABLE_LIMITS.fallback),\n description: clean(input?.description, VARIABLE_LIMITS.description),\n custom: true,\n },\n };\n}\n\n/**\n * System variables first, then custom ones (a custom variable never replaces\n * a system one).\n *\n * @param {EmailVariable[]} system\n * @param {EmailVariable[]} custom\n */\nexport function mergeVariables(system, custom = []) {\n const keys = new Set(system.map((v) => v.key));\n return [...system, ...custom.filter((v) => !keys.has(v.key)).map((v) => ({ ...v, custom: true }))];\n}\n","// @ts-check\n/**\n * Service links and uploaded images in a rendered email.\n *\n * `{{ unsubscribe }}` → the unsubscribe link. For a preview or a test it is\n * an INERT link: it can be displayed and clicked but\n * unsubscribes nobody — least of all the person\n * proofreading their test;\n * `{{ mirror }}` → \"view online\" link of some providers, `#` here.\n *\n * UPLOADED IMAGES: a template keeps a RELATIVE path (`/email-assets/<key>`),\n * made absolute at render time with the public URL of the environment that\n * sends. An absolute URL frozen at save time would point to whichever\n * environment saved the template.\n *\n * An uploaded image path is any root-relative path ending with an asset key:\n * 32 hex characters of the content hash and a known extension. The prefix is\n * the application's choice (`assetsPath` option of the server handler).\n */\n\nconst UNSUBSCRIBE = /\\{\\{\\s*unsubscribe\\s*\\}\\}|\\[UNSUBSCRIBE\\]/gi;\nconst MIRROR = /\\{\\{\\s*mirror\\s*\\}\\}/gi;\n\n/** Default public path of uploaded images. */\nexport const DEFAULT_ASSETS_PATH = '/email-assets/';\n\nconst ASSET_PATH_PATTERN = /^\\/(?:[A-Za-z0-9._~-]+\\/)*[a-f0-9]{32}\\.(png|jpg|gif)$/;\nconst RELATIVE_ASSET = /(^|[\"'(=\\s])(\\/(?:[A-Za-z0-9._~-]+\\/)*[a-f0-9]{32}\\.(?:png|jpg|gif))(?=[\"')\\s>]|$)/g;\n\n/** Is this the path of an uploaded image? @param {string} value */\nexport function isAssetPath(value) {\n const path = String(value || '').trim();\n return ASSET_PATH_PATTERN.test(path) && !path.split('/').some((part) => part === '..' || part === '.');\n}\n\n/**\n * Makes uploaded image paths absolute. Without a base URL the HTML is\n * returned as is (preview inside the application, same origin).\n *\n * @param {string} html\n * @param {string|null|undefined} baseUrl\n */\nexport function absoluteAssetUrls(html, baseUrl) {\n const root = String(baseUrl || '').replace(/\\/+$/, '');\n if (!root) return String(html || '');\n return String(html || '').replace(RELATIVE_ASSET, (match, before, path) =>\n isAssetPath(path) ? `${before}${root}${path}` : match\n );\n}\n\nfunction escapeAttribute(text) {\n return String(text ?? '')\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n\n/**\n * @param {string} html\n * @param {{unsubscribeUrl: string}} links\n */\nexport function applyServiceLinks(html, { unsubscribeUrl }) {\n return String(html || '')\n .replace(UNSUBSCRIBE, () => escapeAttribute(unsubscribeUrl))\n .replace(MIRROR, '#');\n}\n"],"mappings":"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","names":["prop","clean"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
IMAGE_TYPES,
|
|
3
3
|
imageUploadError,
|
|
4
4
|
readDocument
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QPAZLCPO.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_ASSETS_PATH,
|
|
8
8
|
DEFAULT_VARIABLES,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
identity,
|
|
11
11
|
translatorFor,
|
|
12
12
|
validateCustomVariable
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-JI36FNWE.js";
|
|
14
14
|
|
|
15
15
|
// src/ui/context.jsx
|
|
16
16
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -892,4 +892,4 @@ export {
|
|
|
892
892
|
createHttpStorage,
|
|
893
893
|
createMemoryStorage
|
|
894
894
|
};
|
|
895
|
-
//# sourceMappingURL=chunk-
|
|
895
|
+
//# sourceMappingURL=chunk-JU3OUO6X.js.map
|
|
@@ -10,16 +10,17 @@ import {
|
|
|
10
10
|
useBrand,
|
|
11
11
|
useLayoutWidth,
|
|
12
12
|
withThumbnailImages
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-XEFBPP5Y.js";
|
|
14
14
|
import {
|
|
15
15
|
BRAND_NOTES,
|
|
16
|
+
countImagesWithoutPicture,
|
|
16
17
|
logoWidthFor
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-4U7L2NYQ.js";
|
|
18
19
|
import {
|
|
19
20
|
cleanRichText,
|
|
20
21
|
escapeText,
|
|
21
22
|
textOfHtml
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-SAIW46IC.js";
|
|
23
24
|
import {
|
|
24
25
|
Button,
|
|
25
26
|
Dialog,
|
|
@@ -55,24 +56,25 @@ import {
|
|
|
55
56
|
useSearchShortcut,
|
|
56
57
|
useT,
|
|
57
58
|
useUi
|
|
58
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-JU3OUO6X.js";
|
|
59
60
|
import {
|
|
60
61
|
EMAIL_BUILDER_VERSION,
|
|
61
62
|
EMAIL_DEVICES,
|
|
62
63
|
createDocument,
|
|
63
64
|
imageUploadError,
|
|
64
65
|
readDocument
|
|
65
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-QPAZLCPO.js";
|
|
66
67
|
import {
|
|
67
68
|
LONGHAND_SPACING,
|
|
69
|
+
MJML_DEFAULTS,
|
|
68
70
|
formatSpacing,
|
|
69
71
|
parseSpacing,
|
|
70
72
|
toAttributes,
|
|
71
73
|
toValues
|
|
72
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-OWVXKMLI.js";
|
|
73
75
|
import {
|
|
74
76
|
isSafeUrl
|
|
75
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-W424KPCR.js";
|
|
76
78
|
import {
|
|
77
79
|
CUSTOM_GROUP,
|
|
78
80
|
DEFAULT_BUTTON_URL,
|
|
@@ -121,7 +123,7 @@ import {
|
|
|
121
123
|
tokenOf,
|
|
122
124
|
translatorFor,
|
|
123
125
|
validateCustomVariable
|
|
124
|
-
} from "./chunk-
|
|
126
|
+
} from "./chunk-JI36FNWE.js";
|
|
125
127
|
|
|
126
128
|
// src/core/events.js
|
|
127
129
|
function createEmitter({ onError } = {}) {
|
|
@@ -154,62 +156,6 @@ function createEmitter({ onError } = {}) {
|
|
|
154
156
|
};
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
// src/core/mjmlDefaults.js
|
|
158
|
-
var MJML_DEFAULTS = {
|
|
159
|
-
"mj-body": { width: "600px" },
|
|
160
|
-
"mj-section": {
|
|
161
|
-
"background-repeat": "repeat",
|
|
162
|
-
"background-size": "auto",
|
|
163
|
-
"background-position": "top center",
|
|
164
|
-
padding: "20px 0",
|
|
165
|
-
"text-align": "center"
|
|
166
|
-
},
|
|
167
|
-
"mj-wrapper": { "background-repeat": "repeat", "background-size": "auto", "background-position": "top center", padding: "20px 0" },
|
|
168
|
-
"mj-column": { "vertical-align": "top" },
|
|
169
|
-
"mj-text": {
|
|
170
|
-
align: "left",
|
|
171
|
-
color: "#000000",
|
|
172
|
-
"font-family": "Ubuntu, Helvetica, Arial, sans-serif",
|
|
173
|
-
"font-size": "13px",
|
|
174
|
-
"line-height": "1",
|
|
175
|
-
padding: "10px 25px"
|
|
176
|
-
},
|
|
177
|
-
"mj-image": { align: "center", height: "auto", padding: "10px 25px" },
|
|
178
|
-
"mj-button": {
|
|
179
|
-
align: "center",
|
|
180
|
-
"background-color": "#414141",
|
|
181
|
-
"border-radius": "3px",
|
|
182
|
-
color: "#ffffff",
|
|
183
|
-
"font-family": "Ubuntu, Helvetica, Arial, sans-serif",
|
|
184
|
-
"font-size": "13px",
|
|
185
|
-
"font-weight": "normal",
|
|
186
|
-
"inner-padding": "10px 25px",
|
|
187
|
-
"line-height": "120%",
|
|
188
|
-
padding: "10px 25px"
|
|
189
|
-
},
|
|
190
|
-
"mj-divider": { "border-color": "#000000", "border-style": "solid", "border-width": "4px", padding: "10px 25px", width: "100%", align: "center" },
|
|
191
|
-
"mj-spacer": { height: "20px" },
|
|
192
|
-
"mj-social": {
|
|
193
|
-
align: "center",
|
|
194
|
-
"border-radius": "3px",
|
|
195
|
-
"icon-size": "20px",
|
|
196
|
-
mode: "horizontal",
|
|
197
|
-
padding: "10px 25px"
|
|
198
|
-
},
|
|
199
|
-
"mj-social-element": { "border-radius": "3px", padding: "4px" },
|
|
200
|
-
"mj-navbar": { align: "center" },
|
|
201
|
-
"mj-navbar-link": {
|
|
202
|
-
color: "#000000",
|
|
203
|
-
"font-family": "Ubuntu, Helvetica, Arial, sans-serif",
|
|
204
|
-
"font-size": "13px",
|
|
205
|
-
"font-weight": "normal",
|
|
206
|
-
"line-height": "22px",
|
|
207
|
-
padding: "15px 10px",
|
|
208
|
-
"text-transform": "uppercase"
|
|
209
|
-
},
|
|
210
|
-
"mj-hero": { "background-color": "#ffffff", padding: "0px" }
|
|
211
|
-
};
|
|
212
|
-
|
|
213
159
|
// src/core/EmailEditor.js
|
|
214
160
|
function resolveBlock(block, t = identity) {
|
|
215
161
|
const text = (value) => typeof value === "function" ? String(value(t)) : value;
|
|
@@ -5274,11 +5220,19 @@ function Builder({
|
|
|
5274
5220
|
if (!current.id) clearLocalDraft();
|
|
5275
5221
|
latest.current.onSaved?.(result.template);
|
|
5276
5222
|
const warnings = result.warnings || [];
|
|
5223
|
+
const withoutPicture = auto ? 0 : countImagesWithoutPicture(mjml6);
|
|
5277
5224
|
if (warnings.length) {
|
|
5278
5225
|
notify(
|
|
5279
5226
|
"warning",
|
|
5280
5227
|
t("Saved, with {count} MJML warning(s): {message}", { count: warnings.length, message: warnings[0].message })
|
|
5281
5228
|
);
|
|
5229
|
+
} else if (withoutPicture) {
|
|
5230
|
+
notify(
|
|
5231
|
+
"warning",
|
|
5232
|
+
t("Saved. {count} image(s) have no picture yet: they are left out of the email until you add one.", {
|
|
5233
|
+
count: withoutPicture
|
|
5234
|
+
})
|
|
5235
|
+
);
|
|
5282
5236
|
} else if (!auto) {
|
|
5283
5237
|
notify(
|
|
5284
5238
|
"success",
|
|
@@ -5632,4 +5586,4 @@ export {
|
|
|
5632
5586
|
EmailBuilder,
|
|
5633
5587
|
EmailBuilderBoundary
|
|
5634
5588
|
};
|
|
5635
|
-
//# sourceMappingURL=chunk-
|
|
5589
|
+
//# sourceMappingURL=chunk-NC2YDZSP.js.map
|