@one-million-lines/email-builder 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/core/plugins.d.ts +3 -1
- package/dist/core/types.d.ts +26 -0
- package/dist/core/validation.d.ts +4 -0
- package/dist/email-builder.cjs +6 -6
- package/dist/email-builder.cjs.map +1 -1
- package/dist/email-builder.js +2320 -2189
- package/dist/email-builder.js.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/modules/helpers.d.ts +28 -1
- package/dist/plugins/mergeTags/state.d.ts +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,57 @@ All notable changes to this package are documented here.
|
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org/) and the
|
|
5
5
|
[Keep a Changelog](https://keepachangelog.com/) format.
|
|
6
6
|
|
|
7
|
+
## [0.4.0] — 2026-09-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Special link types** — links can now be tagged with a semantic `linkType`
|
|
12
|
+
(`"unsubscribe"`, `"view_in_browser"`, `"manage_preferences"`, `"user_profile"`).
|
|
13
|
+
The rendered HTML carries both a `data-link-type` attribute on the anchor element
|
|
14
|
+
**and** a placeholder href value (e.g. `{{unsubscribe_url}}`), giving backend
|
|
15
|
+
processors two independent ways to locate and replace per-recipient URLs.
|
|
16
|
+
- New `SpecialLinkType` union type and `SPECIAL_LINK_PLACEHOLDERS` map exported
|
|
17
|
+
from `core/types`.
|
|
18
|
+
- `linkType` field added to `ButtonElement` and `ImageElement` (top-level), and
|
|
19
|
+
to `TextElement.style` (alongside the existing `link`).
|
|
20
|
+
- `text()`, `button()`, and `muted()` helpers accept a `linkType` option.
|
|
21
|
+
- New `footerLinks(links, opts?)` helper creates a text element with multiple
|
|
22
|
+
inline `<a>` tags, each carrying `data-link-type` — ideal for footer lines
|
|
23
|
+
that combine "Unsubscribe · View in browser".
|
|
24
|
+
- All built-in footer modules updated to use `footerLinks()` and proper
|
|
25
|
+
`linkType` values instead of plain text.
|
|
26
|
+
- Right sidebar: **Link role** dropdown added below the Link URL field for
|
|
27
|
+
Text, Image, and Button elements. Selecting a role auto-fills the placeholder
|
|
28
|
+
URL.
|
|
29
|
+
- `safeUrl()` updated to pass through `{{...}}` placeholder URLs without
|
|
30
|
+
stripping them.
|
|
31
|
+
- Validation schema updated to accept `linkType` on image and button elements.
|
|
32
|
+
|
|
33
|
+
- **Merge tags / personalisation tokens** — dynamic content placeholders can
|
|
34
|
+
now be configured and inserted directly from the editor sidebar.
|
|
35
|
+
- New `MergeTag` interface: `{ attribute: string; title: string }`.
|
|
36
|
+
- New `mergeTags` prop on `<EmailBuilder>` (React) and `createEmailBuilder`
|
|
37
|
+
(vanilla) accepts an array of merge tag definitions.
|
|
38
|
+
- New `builder.registerMergeTags(tags)` method on `BuilderHandle` for plugin
|
|
39
|
+
authors.
|
|
40
|
+
- New `useMergeTagsStore` reactive store (`src/plugins/mergeTags/state.ts`).
|
|
41
|
+
- Right sidebar: **Insert merge tag** dropdown appears in the Text element
|
|
42
|
+
panel when merge tags are configured. Selecting one appends
|
|
43
|
+
`{attribute}` to the text content.
|
|
44
|
+
- `MergeTag` exported from the public API.
|
|
45
|
+
|
|
46
|
+
- **Template & block authoring guide** (`src/templates/TEMPLATE_GUIDE.md`) —
|
|
47
|
+
comprehensive, AI-agent-ready reference documenting:
|
|
48
|
+
- The full `EmailDocument` JSON schema
|
|
49
|
+
- All element types and their style options
|
|
50
|
+
- Module structure, categories, and naming conventions
|
|
51
|
+
- Theme tokens and how to reference them
|
|
52
|
+
- All helper functions with options tables
|
|
53
|
+
- Special link types and merge tags
|
|
54
|
+
- Step-by-step instructions for creating new templates and module definitions
|
|
55
|
+
- A complete template code example
|
|
56
|
+
- A commit checklist and AI agent prompt template
|
|
57
|
+
|
|
7
58
|
## [Unreleased]
|
|
8
59
|
|
|
9
60
|
### Added
|
package/dist/core/plugins.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ModuleDefinition } from '../modules/registry';
|
|
2
|
-
import { Theme } from './types';
|
|
2
|
+
import { Theme, MergeTag } from './types';
|
|
3
3
|
import { AIProvider } from './aiActions';
|
|
4
4
|
export interface AssetProvider {
|
|
5
5
|
upload: (file: File) => Promise<{
|
|
@@ -49,6 +49,8 @@ export interface BuilderHandle {
|
|
|
49
49
|
registerProductProvider: (provider: ProductProvider) => void;
|
|
50
50
|
registerVoucherProvider: (provider: VoucherProvider) => void;
|
|
51
51
|
setAIProvider: (provider: AIProvider) => void;
|
|
52
|
+
/** Configure the list of merge tags available in the text element sidebar. */
|
|
53
|
+
registerMergeTags: (tags: MergeTag[]) => void;
|
|
52
54
|
}
|
|
53
55
|
export type PluginType = "modules" | "themes" | "asset-provider" | "product-provider" | "voucher-provider" | "ai-provider";
|
|
54
56
|
export interface Plugin {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
export type ElementType = "text" | "image" | "button" | "spacer" | "divider" | "productGrid";
|
|
2
|
+
/**
|
|
3
|
+
* Marks a link as a well-known system link. Backend processors replace the
|
|
4
|
+
* placeholder href (e.g. `{{unsubscribe_url}}`) with a real per-recipient URL.
|
|
5
|
+
* The rendered HTML also carries a `data-link-type` attribute so backends that
|
|
6
|
+
* parse HTML can find and replace these links without inspecting the JSON.
|
|
7
|
+
*/
|
|
8
|
+
export type SpecialLinkType = "unsubscribe" | "view_in_browser" | "manage_preferences" | "user_profile";
|
|
9
|
+
/** Placeholder href values automatically set when a SpecialLinkType is chosen. */
|
|
10
|
+
export declare const SPECIAL_LINK_PLACEHOLDERS: Record<SpecialLinkType, string>;
|
|
11
|
+
/**
|
|
12
|
+
* A merge tag / personalisation token. Configured via the `mergeTags` prop on
|
|
13
|
+
* `<EmailBuilder>` or via `builder.registerMergeTags(tags)`.
|
|
14
|
+
* When the user picks a tag, `{attribute}` is inserted into the text content.
|
|
15
|
+
*/
|
|
16
|
+
export interface MergeTag {
|
|
17
|
+
/** Dot-notation path used as the placeholder, e.g. `"user.firstname"`. */
|
|
18
|
+
attribute: string;
|
|
19
|
+
/** Human-readable label shown in the sidebar dropdown, e.g. `"First name"`. */
|
|
20
|
+
title: string;
|
|
21
|
+
}
|
|
2
22
|
export interface BaseStyle {
|
|
3
23
|
paddingTop?: number;
|
|
4
24
|
paddingBottom?: number;
|
|
@@ -28,6 +48,8 @@ export interface TextElement {
|
|
|
28
48
|
fontWeight?: number | string;
|
|
29
49
|
color?: string;
|
|
30
50
|
link?: string;
|
|
51
|
+
/** Marks the element link as a well-known system link (e.g. unsubscribe). */
|
|
52
|
+
linkType?: SpecialLinkType;
|
|
31
53
|
};
|
|
32
54
|
}
|
|
33
55
|
export interface ImageElement {
|
|
@@ -36,6 +58,8 @@ export interface ImageElement {
|
|
|
36
58
|
src: string;
|
|
37
59
|
alt?: string;
|
|
38
60
|
link?: string;
|
|
61
|
+
/** Marks the image link as a well-known system link. */
|
|
62
|
+
linkType?: SpecialLinkType;
|
|
39
63
|
style?: BaseStyle & {
|
|
40
64
|
width?: number;
|
|
41
65
|
height?: number;
|
|
@@ -46,6 +70,8 @@ export interface ButtonElement {
|
|
|
46
70
|
type: "button";
|
|
47
71
|
label: string;
|
|
48
72
|
link: string;
|
|
73
|
+
/** Marks the button link as a well-known system link. */
|
|
74
|
+
linkType?: SpecialLinkType;
|
|
49
75
|
style?: BaseStyle & {
|
|
50
76
|
backgroundColor?: string;
|
|
51
77
|
color?: string;
|
|
@@ -16,12 +16,14 @@ export declare const moduleSchema: z.ZodObject<{
|
|
|
16
16
|
src: z.ZodString;
|
|
17
17
|
alt: z.ZodOptional<z.ZodString>;
|
|
18
18
|
link: z.ZodOptional<z.ZodString>;
|
|
19
|
+
linkType: z.ZodOptional<z.ZodString>;
|
|
19
20
|
style: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodAny>]>>>;
|
|
20
21
|
}, z.core.$strip>, z.ZodObject<{
|
|
21
22
|
id: z.ZodString;
|
|
22
23
|
type: z.ZodLiteral<"button">;
|
|
23
24
|
label: z.ZodString;
|
|
24
25
|
link: z.ZodString;
|
|
26
|
+
linkType: z.ZodOptional<z.ZodString>;
|
|
25
27
|
style: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodAny>]>>>;
|
|
26
28
|
}, z.core.$strip>, z.ZodObject<{
|
|
27
29
|
id: z.ZodString;
|
|
@@ -104,12 +106,14 @@ export declare const documentSchema: z.ZodObject<{
|
|
|
104
106
|
src: z.ZodString;
|
|
105
107
|
alt: z.ZodOptional<z.ZodString>;
|
|
106
108
|
link: z.ZodOptional<z.ZodString>;
|
|
109
|
+
linkType: z.ZodOptional<z.ZodString>;
|
|
107
110
|
style: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodAny>]>>>;
|
|
108
111
|
}, z.core.$strip>, z.ZodObject<{
|
|
109
112
|
id: z.ZodString;
|
|
110
113
|
type: z.ZodLiteral<"button">;
|
|
111
114
|
label: z.ZodString;
|
|
112
115
|
link: z.ZodString;
|
|
116
|
+
linkType: z.ZodOptional<z.ZodString>;
|
|
113
117
|
style: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodAny>]>>>;
|
|
114
118
|
}, z.core.$strip>, z.ZodObject<{
|
|
115
119
|
id: z.ZodString;
|