best-email-builder 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +3 -0
  3. package/dist/ai-server.js +3 -3
  4. package/dist/ai.js +6 -6
  5. package/dist/builder.js +9 -10
  6. package/dist/{chunk-7G7C6G3A.js → chunk-47WQSNER.js} +42 -4
  7. package/dist/chunk-47WQSNER.js.map +1 -0
  8. package/dist/{chunk-M4R53NYT.js → chunk-7HDOSA5G.js} +550 -147
  9. package/dist/chunk-7HDOSA5G.js.map +1 -0
  10. package/dist/{chunk-MBZI6NVP.js → chunk-A2MEYOR3.js} +271 -37
  11. package/dist/chunk-A2MEYOR3.js.map +1 -0
  12. package/dist/{chunk-XDCGYHLF.js → chunk-ACAQ2QTU.js} +82 -10
  13. package/dist/chunk-ACAQ2QTU.js.map +1 -0
  14. package/dist/chunk-COZXOBR3.js +294 -0
  15. package/dist/chunk-COZXOBR3.js.map +1 -0
  16. package/dist/{chunk-OLTD3S3N.js → chunk-NV6IV4CU.js} +2 -2
  17. package/dist/{chunk-KTTVBNOU.js → chunk-ONYPTZFK.js} +2 -2
  18. package/dist/{chunk-VQRVBQIB.js → chunk-PFGTD5CS.js} +2 -2
  19. package/dist/{chunk-24E4GOHU.js → chunk-PYD5FOL2.js} +3 -9
  20. package/dist/chunk-PYD5FOL2.js.map +1 -0
  21. package/dist/chunk-SQJ3TKEL.js +855 -0
  22. package/dist/chunk-SQJ3TKEL.js.map +1 -0
  23. package/dist/{chunk-A6VTFUSW.js → chunk-YQGPBUZ3.js} +2 -2
  24. package/dist/index.js +26 -21
  25. package/dist/index.js.map +1 -1
  26. package/dist/library.js +12 -10
  27. package/dist/server.js +174 -12
  28. package/dist/server.js.map +1 -1
  29. package/dist/styles.css +1 -1
  30. package/dist/types/core/constants.d.ts +1 -1
  31. package/dist/types/engine/NativeAdapter.d.ts +1 -1
  32. package/dist/types/features/tags/tags.d.ts +55 -0
  33. package/dist/types/features/templates/gallery.d.ts +4 -14
  34. package/dist/types/features/templates/thumbnails.d.ts +13 -0
  35. package/dist/types/index.d.ts +1 -0
  36. package/dist/types/library.d.ts +2 -1
  37. package/dist/types/server/handler.d.ts +1 -0
  38. package/dist/types/server/index.d.ts +2 -0
  39. package/dist/types/server/tags.d.ts +64 -0
  40. package/dist/types/storage/client.d.ts +33 -4
  41. package/dist/types/ui/EmailStudio.d.ts +8 -8
  42. package/dist/types/ui/library/TagBadge.d.ts +19 -0
  43. package/dist/types/ui/library/TagsDialog.d.ts +43 -0
  44. package/dist/types/ui/library/TemplateLibrary.d.ts +6 -14
  45. package/dist/types/ui/primitives/misc.d.ts +9 -0
  46. package/package.json +1 -1
  47. package/dist/chunk-24E4GOHU.js.map +0 -1
  48. package/dist/chunk-7G7C6G3A.js.map +0 -1
  49. package/dist/chunk-M4R53NYT.js.map +0 -1
  50. package/dist/chunk-MBZI6NVP.js.map +0 -1
  51. package/dist/chunk-SCQKWAXR.js +0 -527
  52. package/dist/chunk-SCQKWAXR.js.map +0 -1
  53. package/dist/chunk-TUA6E3BR.js +0 -632
  54. package/dist/chunk-TUA6E3BR.js.map +0 -1
  55. package/dist/chunk-XDCGYHLF.js.map +0 -1
  56. /package/dist/{chunk-OLTD3S3N.js.map → chunk-NV6IV4CU.js.map} +0 -0
  57. /package/dist/{chunk-KTTVBNOU.js.map → chunk-ONYPTZFK.js.map} +0 -0
  58. /package/dist/{chunk-VQRVBQIB.js.map → chunk-PFGTD5CS.js.map} +0 -0
  59. /package/dist/{chunk-A6VTFUSW.js.map → chunk-YQGPBUZ3.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,61 @@ All notable changes are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.11.0] — 2026-10-19
10
+
11
+ The library loses its bases and gains tags, which also filter it; the
12
+ editor's *Templates* dialog gains a search and tag filters. This breaks
13
+ code that passed `sender` or `templates` to `TemplateLibrary`, or read
14
+ `onCreate`'s argument (see *Changed* and *Removed*).
15
+
16
+ ### Added
17
+ - **Tags** on templates, in the library: up to three per template
18
+ (`MAX_TEMPLATE_TAGS`), shown in the list before the template's date, and
19
+ found by the search. The *Tags* line of the details opens a dialog to tick
20
+ them and to create, rename, recolor or delete the workspace's tags (a name
21
+ of 24 characters at most, unique case aside, one of the twelve
22
+ `TAG_COLORS`). Each change is saved at once; a deleted tag leaves every
23
+ template; a copy keeps its template's tags.
24
+ - The storage methods `listTags()`, `saveTag(tag, { create })`,
25
+ `deleteTag(id)` and `setTemplateTags(id, tagIds)`, in the memory storage
26
+ and in `createHttpStorage({ tags: true })`. `list()` and `load()` give each
27
+ template's `tags` (ids).
28
+ - On the server: the `tagStore` option (`createMemoryTagRepository()` is the
29
+ reference), the routes `GET`/`POST {base}/tags`, `PUT`/`DELETE
30
+ {base}/tags/:id` and `PUT {base}/templates/:id/tags`, `createTagService`.
31
+ `validateTag`, `TAG_COLORS`, `TAG_LIMITS`, `MAX_TEMPLATE_TAGS` and
32
+ `MAX_TAGS` are exported (`validateTag`, `TAG_COLORS` and
33
+ `MAX_TEMPLATE_TAGS` from the browser entries too).
34
+ - `TemplateLibrary`'s `deleteDisabled`: a sentence shown instead of deleting,
35
+ *Delete* staying in place, dimmed. For a public demo.
36
+ - `createMemoryStorage({ compile })`: the templates keep their compiled HTML,
37
+ as on the server, so the library shows *Copy HTML* and *Download*.
38
+ - Tag filters under the library's search: the tags some template carries,
39
+ one or several (a template with any of them), or *All*; with the search,
40
+ both apply.
41
+ - The editor's *Templates* dialog has a search (names, descriptions, tags)
42
+ and the bases' tags as filters: one or several (a base with any of them),
43
+ or *All*. A gallery entry takes `tags`, translated like its name.
44
+
45
+ ### Changed
46
+ - `TemplateLibrary` lists only the templates; a new one starts from a base
47
+ with the editor's *Templates* button. `onCreate()` takes no argument.
48
+ - `EmailStudio` passes `sender` and `templates` to the editor only, which
49
+ keeps its *Templates* button.
50
+ - The live demo opens on five templates of the gallery, three of them
51
+ tagged; its *Delete* is turned off.
52
+ - The *Templates* dialog's cards all have the same height: a name or a
53
+ description too long fades out at the card's edge, and the thumbnails show
54
+ no scrollbar. Only the cards scroll; the search and the tags stay.
55
+
56
+ ### Removed
57
+ - The *Builder* badge of every row of the library: the same on every
58
+ template, it only looked like a tag. The details still give the origin.
59
+ - The *Bases* section of the library, its *Start from this base* button, and
60
+ `TemplateLibrary`'s `sender` and `templates` props.
61
+ - `TEMPLATE_GALLERY` and `buildGallery` from `best-email-builder/library`
62
+ (they stay in `best-email-builder/builder` and `best-email-builder`).
63
+
9
64
  ## [0.10.0] — 2026-10-19
10
65
 
11
66
  The AI no longer writes whole emails: it checks and writes their texts.
package/README.md CHANGED
@@ -76,6 +76,9 @@ once and given away.
76
76
  server, or in the page with each user's own key.
77
77
 
78
78
  **Your app**
79
+ - A template library: search, favorites, up to three colored tags per
80
+ template that also filter the list, and *Edit*, *Duplicate*, *Copy HTML*,
81
+ *Download*, *Delete*.
79
82
  - Merge-tag variables, including ones your users create.
80
83
  - Your brand read from your website: logo, colors and font.
81
84
  - One Fetch-API handler for Next.js, Hono, Bun, Deno, Cloudflare Workers and Express.
package/dist/ai-server.js CHANGED
@@ -26,9 +26,9 @@ import {
26
26
  reviewFix,
27
27
  reviewRewrite,
28
28
  reviewSubjects
29
- } from "./chunk-24E4GOHU.js";
30
- import "./chunk-VQRVBQIB.js";
31
- import "./chunk-MBZI6NVP.js";
29
+ } from "./chunk-PYD5FOL2.js";
30
+ import "./chunk-PFGTD5CS.js";
31
+ import "./chunk-A2MEYOR3.js";
32
32
 
33
33
  // src/ai/agentEndpoint.js
34
34
  var TASKS = ["rewrite", "subjects"];
package/dist/ai.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cleanRichText
3
- } from "./chunk-KTTVBNOU.js";
3
+ } from "./chunk-ONYPTZFK.js";
4
4
  import {
5
5
  Button,
6
6
  Dialog,
@@ -19,8 +19,8 @@ import {
19
19
  cn,
20
20
  createHttpCaller,
21
21
  useUi
22
- } from "./chunk-XDCGYHLF.js";
23
- import "./chunk-7G7C6G3A.js";
22
+ } from "./chunk-ACAQ2QTU.js";
23
+ import "./chunk-47WQSNER.js";
24
24
  import {
25
25
  AGENT_ERROR_CODES,
26
26
  AGENT_PROTOCOL,
@@ -43,9 +43,9 @@ import {
43
43
  providerEmailModel,
44
44
  resolveProvider,
45
45
  visibleText
46
- } from "./chunk-24E4GOHU.js";
47
- import "./chunk-VQRVBQIB.js";
48
- import "./chunk-MBZI6NVP.js";
46
+ } from "./chunk-PYD5FOL2.js";
47
+ import "./chunk-PFGTD5CS.js";
48
+ import "./chunk-A2MEYOR3.js";
49
49
 
50
50
  // src/ai/keys.js
51
51
  var STORAGE_PREFIX = "best-email-builder:ai-key:";
package/dist/builder.js CHANGED
@@ -1,25 +1,24 @@
1
1
  import {
2
2
  EmailBuilder,
3
- EmailBuilderBoundary
4
- } from "./chunk-M4R53NYT.js";
5
- import {
3
+ EmailBuilderBoundary,
6
4
  STARTER_MJML,
7
5
  TEMPLATE_GALLERY,
8
6
  buildGallery,
9
7
  createStarterMjml
10
- } from "./chunk-TUA6E3BR.js";
11
- import "./chunk-A6VTFUSW.js";
8
+ } from "./chunk-7HDOSA5G.js";
9
+ import "./chunk-COZXOBR3.js";
10
+ import "./chunk-YQGPBUZ3.js";
12
11
  import "./chunk-UFKCLU2D.js";
13
- import "./chunk-KTTVBNOU.js";
12
+ import "./chunk-ONYPTZFK.js";
14
13
  import {
15
14
  StorageError,
16
15
  createHttpStorage,
17
16
  createMemoryStorage
18
- } from "./chunk-XDCGYHLF.js";
17
+ } from "./chunk-ACAQ2QTU.js";
19
18
  import {
20
19
  EMAIL_BUILDER_VERSION
21
- } from "./chunk-7G7C6G3A.js";
22
- import "./chunk-VQRVBQIB.js";
20
+ } from "./chunk-47WQSNER.js";
21
+ import "./chunk-PFGTD5CS.js";
23
22
  import {
24
23
  CUSTOM_GROUP,
25
24
  DEFAULT_VARIABLES,
@@ -28,7 +27,7 @@ import {
28
27
  defineVariables,
29
28
  mergeVariables,
30
29
  translatorFor
31
- } from "./chunk-MBZI6NVP.js";
30
+ } from "./chunk-A2MEYOR3.js";
32
31
  export {
33
32
  CUSTOM_GROUP,
34
33
  DEFAULT_VARIABLES,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  identity
3
- } from "./chunk-MBZI6NVP.js";
3
+ } from "./chunk-A2MEYOR3.js";
4
4
 
5
5
  // src/core/constants.js
6
- var EMAIL_BUILDER_VERSION = "0.10.0";
6
+ var EMAIL_BUILDER_VERSION = "0.11.0";
7
7
  var EMAIL_DOCUMENT_SCHEMA_VERSION = 1;
8
8
  var EMAIL_DEVICES = ["desktop", "mobile"];
9
9
  var EMAIL_MOBILE_WIDTH = 375;
@@ -758,6 +758,37 @@ function brandTemplate(mjml, brand, { sender = null, t = (key) => key } = {}) {
758
758
  });
759
759
  }
760
760
 
761
+ // src/features/tags/tags.js
762
+ var MAX_TEMPLATE_TAGS = 3;
763
+ var MAX_TAGS = 100;
764
+ var TAG_LIMITS = { label: 24 };
765
+ var TAG_COLORS = ["gray", "red", "orange", "amber", "lime", "green", "teal", "cyan", "blue", "violet", "fuchsia", "pink"];
766
+ var sameName = (a, b) => a.localeCompare(b, void 0, { sensitivity: "accent" }) === 0;
767
+ function validateTag(input, { existing = [], editing = null, t = identity } = {}) {
768
+ const errors = {};
769
+ const label = String(input?.label ?? "").replace(/\s+/g, " ").trim();
770
+ const color = String(input?.color ?? "");
771
+ if (!label) errors.label = t("Name is required");
772
+ else if (label.length > TAG_LIMITS.label) errors.label = t("{max} characters at most", { max: TAG_LIMITS.label });
773
+ else if (existing.some((tag) => String(tag.id) !== String(editing) && sameName(tag.label, label))) {
774
+ errors.label = t("A tag with this name already exists");
775
+ }
776
+ if (!TAG_COLORS.includes(color)) errors.color = t("Choose a color");
777
+ if (Object.keys(errors).length) return { errors };
778
+ return { tag: { label, color } };
779
+ }
780
+ function checkTemplateTags(ids, catalog, { t = identity } = {}) {
781
+ if (!Array.isArray(ids)) return { error: { code: "INVALID_TAGS", message: t("A list of tags is expected") } };
782
+ const wanted = new Set(ids.map(String));
783
+ if (wanted.size > MAX_TEMPLATE_TAGS) {
784
+ return { error: { code: "TOO_MANY_TAGS", message: t("{max} tags at most per template", { max: MAX_TEMPLATE_TAGS }) } };
785
+ }
786
+ const known = new Set(catalog.map((tag) => String(tag.id)));
787
+ if ([...wanted].some((id) => !known.has(id))) return { error: { code: "UNKNOWN_TAG", message: t("Tag not found") } };
788
+ return { ids: catalog.map((tag) => String(tag.id)).filter((id) => wanted.has(id)) };
789
+ }
790
+ var sortTags = (tags) => [...tags].sort((a, b) => a.label.localeCompare(b.label));
791
+
761
792
  // src/validation/images.js
762
793
  var MAX_IMAGE_BYTES = 2 * 1024 * 1024;
763
794
  var IMAGE_TYPES = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif" };
@@ -832,6 +863,13 @@ export {
832
863
  IMAGE_TYPES,
833
864
  ASSET_KEY_PATTERN,
834
865
  detectImage,
835
- imageUploadError
866
+ imageUploadError,
867
+ MAX_TEMPLATE_TAGS,
868
+ MAX_TAGS,
869
+ TAG_LIMITS,
870
+ TAG_COLORS,
871
+ validateTag,
872
+ checkTemplateTags,
873
+ sortTags
836
874
  };
837
- //# sourceMappingURL=chunk-7G7C6G3A.js.map
875
+ //# sourceMappingURL=chunk-47WQSNER.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/constants.js","../src/core/document.js","../src/engine/model.js","../src/engine/mjml.js","../src/blocks/shared.js","../src/features/brand/brand.js","../src/features/tags/tags.js","../src/validation/images.js"],"sourcesContent":["// @ts-check\n/**\n * Email builder constants.\n *\n * `EMAIL_BUILDER_VERSION` versions THIS package (UI, blocks, rendering);\n * `EMAIL_DOCUMENT_SCHEMA_VERSION` versions the stored JSON envelope (the\n * project JSON). Both are written with every template, so a document read by\n * a newer version knows whether it must be migrated (see `core/document.js`).\n */\n\nexport const EMAIL_BUILDER_VERSION = '0.11.0';\n\nexport const EMAIL_DOCUMENT_SCHEMA_VERSION = 1;\n\n/** @typedef {'desktop'|'mobile'} EmailDevice */\n\n/** @type {EmailDevice[]} */\nexport const EMAIL_DEVICES = ['desktop', 'mobile'];\n\n/** Content column width — the email client standard. */\nexport const EMAIL_CONTENT_WIDTH = 600;\n\n/** Mobile preview frame width (standard iPhone, the common worst case). */\nexport const EMAIL_MOBILE_WIDTH = 375;\n","// @ts-check\n/**\n * DOCUMENT envelope — what gets stored (the project JSON).\n *\n * HTML is only a compilation output; what gets edited is the engine's\n * project. It is not stored bare but wrapped with:\n *\n * schemaVersion the shape of THIS envelope (migrations, see MIGRATIONS);\n * engine the engine that can read `project` back;\n * engineVersion its version at write time;\n * builderVersion the builder version that wrote it;\n * project the engine data, opaque to the rest of the code.\n *\n * A document from another engine, a future schema or a malformed one is\n * REJECTED on read rather than half-loaded: a template silently emptied and\n * then saved again would be lost.\n *\n * Dependency-free module: testable outside the browser.\n */\n\nimport { EMAIL_BUILDER_VERSION, EMAIL_DOCUMENT_SCHEMA_VERSION } from './constants.js';\n\n/**\n * @typedef {{\n * schemaVersion: number,\n * engine: string,\n * engineVersion: string,\n * builderVersion: string,\n * project: Record<string, any>,\n * }} EmailDocument\n */\n\n/**\n * Envelope migrations, from N to N+1. Empty while only one schema exists; the\n * first entry will be `1: (doc) => ({ ...doc, schemaVersion: 2 })`.\n *\n * @type {Record<number, (document: any) => any>}\n */\nconst MIGRATIONS = {};\n\nexport class EmailDocumentError extends Error {\n /** @param {string} message */\n constructor(message) {\n super(message);\n this.name = 'EmailDocumentError';\n this.status = 422;\n }\n}\n\nfunction isPlainObject(value) {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * @param {{engine: string, engineVersion: string, project: object}} parts\n * @returns {EmailDocument}\n */\nexport function createDocument({ engine, engineVersion, project }) {\n if (!engine) throw new EmailDocumentError('Document: missing engine');\n if (!isPlainObject(project)) throw new EmailDocumentError('Document: invalid engine project');\n return {\n schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,\n engine,\n engineVersion: String(engineVersion || ''),\n builderVersion: EMAIL_BUILDER_VERSION,\n project: /** @type {Record<string, any>} */ (project),\n };\n}\n\n/**\n * Reads a document (object or JSON string), migrates it to the current schema\n * and validates it.\n *\n * @param {unknown} raw\n * @returns {EmailDocument}\n * @throws {EmailDocumentError}\n */\nexport function readDocument(raw) {\n let value = raw;\n if (typeof value === 'string') {\n try {\n value = JSON.parse(value);\n } catch {\n throw new EmailDocumentError('Unreadable document: invalid JSON');\n }\n }\n if (!isPlainObject(value)) throw new EmailDocumentError('Unreadable document: object expected');\n\n /** @type {any} */\n let document = { ...(/** @type {object} */ (value)) };\n const version = Number(document.schemaVersion);\n if (!Number.isInteger(version) || version < 1) {\n throw new EmailDocumentError('Unreadable document: missing schema version');\n }\n if (version > EMAIL_DOCUMENT_SCHEMA_VERSION) {\n throw new EmailDocumentError(\n `Document written by a newer version of the builder (schema ${version}) — ` +\n 'reload the page'\n );\n }\n for (let v = version; v < EMAIL_DOCUMENT_SCHEMA_VERSION; v += 1) {\n const migrate = MIGRATIONS[v];\n if (!migrate) throw new EmailDocumentError(`Document: no migration from schema ${v}`);\n document = migrate(document);\n }\n\n if (typeof document.engine !== 'string' || !document.engine) {\n throw new EmailDocumentError('Unreadable document: missing engine');\n }\n if (!isPlainObject(document.project)) {\n throw new EmailDocumentError('Unreadable document: missing engine project');\n }\n\n return {\n schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,\n engine: document.engine,\n engineVersion: String(document.engineVersion || ''),\n builderVersion: String(document.builderVersion || ''),\n project: document.project,\n };\n}\n\n/** @param {EmailDocument} document */\nexport function serializeDocument(document) {\n return JSON.stringify(readDocument(document));\n}\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 (`&amp;` → `&`) 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, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\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 and block,\n * 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, an imported 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(/&nbsp;/g, ' ')\n .replace(/&amp;/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 * TAGS — colored labels on templates, to sort a library at a glance.\n *\n * The workspace keeps one list of tags, shared by every user like the custom\n * variables; a template carries at most `MAX_TEMPLATE_TAGS` of them, by id.\n * A tag's name is unique (case aside) and its color is one of `TAG_COLORS`,\n * so every tag reads well in the light and the dark theme.\n *\n * The same rules run in the browser (the tags dialog, the memory storage) and\n * on the server (`best-email-builder/server`).\n */\n\nimport { identity } from '../../i18n/index.js';\n\n/** @typedef {{id: string, label: string, color: string}} TemplateTag */\n\nexport const MAX_TEMPLATE_TAGS = 3;\n/** Tags in a workspace. */\nexport const MAX_TAGS = 100;\nexport const TAG_LIMITS = { label: 24 };\nexport const TAG_COLORS = ['gray', 'red', 'orange', 'amber', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'fuchsia', 'pink'];\n\nconst sameName = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0;\n\n/**\n * @param {any} input `{label, color}`\n * @param {{existing?: TemplateTag[], editing?: string|null, t?: import('../../i18n/index.js').Translate}} [options]\n * `editing`: the id of the tag being edited (its own name is not a duplicate)\n * @returns {{tag: {label: string, color: string}, errors?: undefined} | {tag?: undefined, errors: Record<string, string>}}\n */\nexport function validateTag(input, { existing = [], editing = null, t = identity } = {}) {\n /** @type {Record<string, string>} */\n const errors = {};\n const label = String(input?.label ?? '').replace(/\\s+/g, ' ').trim();\n const color = String(input?.color ?? '');\n if (!label) errors.label = t('Name is required');\n else if (label.length > TAG_LIMITS.label) errors.label = t('{max} characters at most', { max: TAG_LIMITS.label });\n else if (existing.some((tag) => String(tag.id) !== String(editing) && sameName(tag.label, label))) {\n errors.label = t('A tag with this name already exists');\n }\n if (!TAG_COLORS.includes(color)) errors.color = t('Choose a color');\n if (Object.keys(errors).length) return { errors };\n return { tag: { label, color } };\n}\n\n/**\n * The tags a template may carry: known ids, each once, `MAX_TEMPLATE_TAGS`\n * at most, in the list's order.\n *\n * @param {unknown} ids\n * @param {TemplateTag[]} catalog\n * @param {{t?: import('../../i18n/index.js').Translate}} [options]\n * @returns {{ids: string[], error?: undefined} | {ids?: undefined, error: {code: string, message: string}}}\n */\nexport function checkTemplateTags(ids, catalog, { t = identity } = {}) {\n if (!Array.isArray(ids)) return { error: { code: 'INVALID_TAGS', message: t('A list of tags is expected') } };\n const wanted = new Set(ids.map(String));\n if (wanted.size > MAX_TEMPLATE_TAGS) {\n return { error: { code: 'TOO_MANY_TAGS', message: t('{max} tags at most per template', { max: MAX_TEMPLATE_TAGS }) } };\n }\n const known = new Set(catalog.map((tag) => String(tag.id)));\n if ([...wanted].some((id) => !known.has(id))) return { error: { code: 'UNKNOWN_TAG', message: t('Tag not found') } };\n return { ids: catalog.map((tag) => String(tag.id)).filter((id) => wanted.has(id)) };\n}\n\n/** The list's order: by name. @param {TemplateTag[]} tags */\nexport const sortTags = (tags) => [...tags].sort((a, b) => a.label.localeCompare(b.label));\n","// @ts-check\n/**\n * Images UPLOADED in the builder — one rule shared by the browser (immediate\n * rejection, storage/client.js) and the server (storage/server/assetFile.js).\n *\n * PNG, JPEG, GIF: the three formats every email client displays. WebP and SVG\n * do not display in Outlook, and an SVG can carry script.\n * The server reads the type from the file BYTES; the browser, from `file.type`.\n */\n\nimport { identity } from '../i18n/index.js';\n\n/** Maximum image size: beyond it, the email becomes slow to open. */\nexport const MAX_IMAGE_BYTES = 2 * 1024 * 1024;\n\n/** Accepted types → extension of the public key. */\nexport const IMAGE_TYPES = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/gif': 'gif' };\n\n/** Public key: 32 hex characters of the hash + extension. */\nexport const ASSET_KEY_PATTERN = /^[a-f0-9]{32}\\.(png|jpg|gif)$/;\n\nconst SIGNATURES = [\n { type: 'image/png', bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] },\n { type: 'image/jpeg', bytes: [0xff, 0xd8, 0xff] },\n { type: 'image/gif', bytes: [0x47, 0x49, 0x46, 0x38] },\n];\n\n/**\n * Image type detected from the file signature, or `null`.\n * @param {Uint8Array} bytes\n * @returns {{type: string, ext: string}|null}\n */\nexport function detectImage(bytes) {\n if (!bytes || bytes.length < 8) return null;\n const found = SIGNATURES.find((s) => s.bytes.every((b, i) => bytes[i] === b));\n return found ? { type: found.type, ext: IMAGE_TYPES[found.type] } : null;\n}\n\n/**\n * Immediate rejection of a file the server would refuse, or `null`.\n * @param {{type?: string, size?: number}} file\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function imageUploadError(file, { t = identity } = {}) {\n if (!(String(file?.type || '') in IMAGE_TYPES)) return t('Format rejected: PNG, JPEG or GIF expected');\n if (Number(file?.size || 0) > MAX_IMAGE_BYTES) {\n return t('Image too large: {max} MB maximum', { max: MAX_IMAGE_BYTES / (1024 * 1024) });\n }\n return null;\n}\n"],"mappings":"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","names":["prop"]}