best-email-builder 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +186 -0
- package/README.md +309 -12
- package/dist/builder.js +44 -0
- package/dist/builder.js.map +1 -0
- package/dist/chunk-AT73Y7JA.js +1427 -0
- package/dist/chunk-AT73Y7JA.js.map +1 -0
- package/dist/chunk-HHLOE6OZ.js +1682 -0
- package/dist/chunk-HHLOE6OZ.js.map +1 -0
- package/dist/chunk-JNHFZXDL.js +1776 -0
- package/dist/chunk-JNHFZXDL.js.map +1 -0
- package/dist/chunk-MY6WPDT4.js +6110 -0
- package/dist/chunk-MY6WPDT4.js.map +1 -0
- package/dist/chunk-NAEGU6PR.js +538 -0
- package/dist/chunk-NAEGU6PR.js.map +1 -0
- package/dist/chunk-TGDZ4NFI.js +114 -0
- package/dist/chunk-TGDZ4NFI.js.map +1 -0
- package/dist/index.js +124 -4866
- package/dist/index.js.map +1 -1
- package/dist/library.js +31 -0
- package/dist/library.js.map +1 -0
- package/dist/server.js +554 -210
- package/dist/server.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/blocks/shared.d.ts +20 -0
- package/dist/types/builder.d.ts +8 -0
- package/dist/types/core/EmailEditor.d.ts +72 -19
- package/dist/types/core/constants.d.ts +1 -1
- package/dist/types/core/mjmlDefaults.d.ts +13 -0
- package/dist/types/core/scrollbars.d.ts +13 -0
- package/dist/types/defaultEditor.d.ts +11 -0
- package/dist/types/engine/NativeAdapter.d.ts +28 -0
- package/dist/types/engine/canvas/CanvasView.d.ts +88 -0
- package/dist/types/engine/canvas/canvasStyles.d.ts +3 -0
- package/dist/types/engine/canvas/icons.d.ts +14 -0
- package/dist/types/engine/canvas/morph.d.ts +21 -0
- package/dist/types/engine/canvasMjml.d.ts +30 -0
- package/dist/types/engine/mjml.d.ts +31 -0
- package/dist/types/engine/model.d.ts +130 -0
- package/dist/types/engine/placement.d.ts +69 -0
- package/dist/types/engine/richText.d.ts +15 -0
- package/dist/types/engine/unwrapModule.d.ts +29 -0
- package/dist/types/features/brand/BrandButton.d.ts +4 -0
- package/dist/types/features/brand/brand.d.ts +108 -0
- package/dist/types/features/brand/extract.d.ts +61 -0
- package/dist/types/features/brand/index.d.ts +2 -0
- package/dist/types/features/brand/messages.d.ts +13 -0
- package/dist/types/features/brand/useBrand.d.ts +22 -0
- package/dist/types/features/generate/GenerateButton.d.ts +3 -0
- package/dist/types/features/generate/RewriteText.d.ts +5 -0
- package/dist/types/features/generate/SubjectIdeas.d.ts +16 -0
- package/dist/types/features/generate/chip.d.ts +1 -0
- package/dist/types/features/generate/claude.d.ts +21 -0
- package/dist/types/features/generate/draft.d.ts +122 -0
- package/dist/types/features/generate/generator.d.ts +118 -0
- package/dist/types/features/generate/index.d.ts +2 -0
- package/dist/types/features/generate/prompt.d.ts +76 -0
- package/dist/types/features/generate/purposes.d.ts +48 -0
- package/dist/types/features/generate/rewrite.d.ts +83 -0
- package/dist/types/features/generate/subjects.d.ts +83 -0
- package/dist/types/features/preview/PreviewButton.d.ts +1 -1
- package/dist/types/features/preview/darkMode.d.ts +48 -0
- package/dist/types/features/templates/gallery.d.ts +7 -0
- package/dist/types/index.d.ts +14 -10
- package/dist/types/library.d.ts +5 -0
- package/dist/types/server/brand.d.ts +32 -0
- package/dist/types/server/brandImport.d.ts +93 -0
- package/dist/types/server/compileMjml.d.ts +2 -0
- package/dist/types/server/handler.d.ts +25 -0
- package/dist/types/server/index.d.ts +11 -0
- package/dist/types/storage/client.d.ts +79 -3
- package/dist/types/ui/EmailStudio.d.ts +42 -0
- package/dist/types/ui/fields.d.ts +2 -1
- package/dist/types/ui/library/TemplateLibrary.d.ts +41 -0
- package/dist/types/ui/primitives/button.d.ts +2 -2
- package/dist/types/ui/primitives/color-picker.d.ts +2 -1
- package/dist/types/ui/useLayoutWidth.d.ts +1 -1
- package/package.json +27 -4
- package/dist/chunk-MTALS5GC.js +0 -747
- package/dist/chunk-MTALS5GC.js.map +0 -1
- package/dist/types/adapters/grapesjs/GrapesJsAdapter.d.ts +0 -33
- package/dist/types/adapters/grapesjs/engineConfig.d.ts +0 -162
- package/dist/types/adapters/grapesjs/unwrapModule.d.ts +0 -18
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attributes of one start tag, names in lower case.
|
|
3
|
+
* @param {string} tag `<img src="…" class=logo>`
|
|
4
|
+
* @returns {Record<string, string>}
|
|
5
|
+
*/
|
|
6
|
+
export function tagAttributes(tag: string): Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* What a home page says about its brand.
|
|
9
|
+
* @param {string} html
|
|
10
|
+
* @param {string} pageUrl the address it was read from (after redirects)
|
|
11
|
+
* @returns {PageReading}
|
|
12
|
+
*/
|
|
13
|
+
export function readPage(html: string, pageUrl: string): PageReading;
|
|
14
|
+
/**
|
|
15
|
+
* The web-safe stack nearest to a CSS font-family.
|
|
16
|
+
* @param {string} family
|
|
17
|
+
* @returns {string|undefined}
|
|
18
|
+
*/
|
|
19
|
+
export function nearestFont(family: string): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Main color, headings color and font of a site, from its CSS.
|
|
22
|
+
* @param {{css: string, themeColor?: string}} input
|
|
23
|
+
* @returns {{primary?: string, heading?: string, font?: string}}
|
|
24
|
+
*/
|
|
25
|
+
export function brandFromCss({ css, themeColor }: {
|
|
26
|
+
css: string;
|
|
27
|
+
themeColor?: string;
|
|
28
|
+
}): {
|
|
29
|
+
primary?: string;
|
|
30
|
+
heading?: string;
|
|
31
|
+
font?: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Width and height of a PNG, GIF or JPEG, from its header; null otherwise.
|
|
35
|
+
* @param {Uint8Array} bytes
|
|
36
|
+
* @returns {{width: number, height: number}|null}
|
|
37
|
+
*/
|
|
38
|
+
export function imageSize(bytes: Uint8Array): {
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
} | null;
|
|
42
|
+
/**
|
|
43
|
+
* The width a logo is shown at in an email: a wordmark wider, a square mark
|
|
44
|
+
* smaller; never wider than the image itself (a blurry logo is worse than a
|
|
45
|
+
* small one). Retina images (twice the size shown) are common: halved first.
|
|
46
|
+
* @param {{width: number, height: number}} size
|
|
47
|
+
*/
|
|
48
|
+
export function logoWidthFor({ width, height }: {
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
}): number;
|
|
52
|
+
export type PageReading = {
|
|
53
|
+
name: string;
|
|
54
|
+
logos: {
|
|
55
|
+
url: string;
|
|
56
|
+
from: "schema" | "img" | "icon";
|
|
57
|
+
}[];
|
|
58
|
+
stylesheets: string[];
|
|
59
|
+
css: string;
|
|
60
|
+
themeColor: string;
|
|
61
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the user is told about reading a brand from a website: why it failed
|
|
3
|
+
* (the server's answer) and what it could not find (notes under the field).
|
|
4
|
+
* English source strings, translated where they are shown.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Why a website could not be read: [HTTP status, message], by
|
|
8
|
+
* BrandImportError code (src/server/brandImport.js).
|
|
9
|
+
* @type {Record<string, [number, string]>}
|
|
10
|
+
*/
|
|
11
|
+
export const BRAND_IMPORT_ERRORS: Record<string, [number, string]>;
|
|
12
|
+
/** What could not be read, by note code. @type {Record<string, string>} */
|
|
13
|
+
export const BRAND_NOTES: Record<string, string>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brand of a storage: `loaded` resolves once it is known ({} when the
|
|
3
|
+
* storage keeps none, or could not be read — the builder then uses its own look).
|
|
4
|
+
* @param {any} storage
|
|
5
|
+
* @returns {BrandEntry}
|
|
6
|
+
*/
|
|
7
|
+
export function brandOf(storage: any): BrandEntry;
|
|
8
|
+
/**
|
|
9
|
+
* @param {any} storage
|
|
10
|
+
* @returns {{brand: Brand, setBrand: (brand: Brand) => void}}
|
|
11
|
+
* `setBrand`: tells every screen of this storage about a brand just saved.
|
|
12
|
+
*/
|
|
13
|
+
export function useBrand(storage: any): {
|
|
14
|
+
brand: Brand;
|
|
15
|
+
setBrand: (brand: Brand) => void;
|
|
16
|
+
};
|
|
17
|
+
export type Brand = import("./brand.js").Brand;
|
|
18
|
+
export type BrandEntry = {
|
|
19
|
+
value: Brand;
|
|
20
|
+
loaded: Promise<Brand>;
|
|
21
|
+
listeners: Set<(brand: Brand) => void>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SUGGEST SUBJECTS — under the subject of the email.
|
|
3
|
+
*
|
|
4
|
+
* One click: three other subject lines, drawn from the words of the email.
|
|
5
|
+
* They are a list to pick from, the user's own subject first: a click puts
|
|
6
|
+
* one in the field, another click another one, the first row brings the
|
|
7
|
+
* user's subject back. "More ideas" asks for three new ones; "Done" closes
|
|
8
|
+
* the list and keeps the subject in the field.
|
|
9
|
+
*/
|
|
10
|
+
export default function SubjectIdeas({ editor, meta, onSubject, suggest, variables }: {
|
|
11
|
+
editor: any;
|
|
12
|
+
meta: any;
|
|
13
|
+
onSubject: any;
|
|
14
|
+
suggest: any;
|
|
15
|
+
variables?: null | undefined;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function chipClassName(selected?: boolean): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {{
|
|
3
|
+
* client: any,
|
|
4
|
+
* model?: string,
|
|
5
|
+
* effort?: 'low'|'medium'|'high'|'xhigh'|'max',
|
|
6
|
+
* rewriteEffort?: 'low'|'medium'|'high'|'xhigh'|'max',
|
|
7
|
+
* maxTokens?: number,
|
|
8
|
+
* }} options
|
|
9
|
+
* `effort`: how much the model thinks before writing an email — `low`
|
|
10
|
+
* answers faster, `high` takes more care. `rewriteEffort`: the same for
|
|
11
|
+
* the light jobs the user waits on — one text rewritten, subject ideas.
|
|
12
|
+
* @returns {import('./generator.js').EmailModel}
|
|
13
|
+
*/
|
|
14
|
+
export function claudeEmailModel({ client, model, effort, rewriteEffort, maxTokens, }?: {
|
|
15
|
+
client: any;
|
|
16
|
+
model?: string;
|
|
17
|
+
effort?: "low" | "medium" | "high" | "xhigh" | "max";
|
|
18
|
+
rewriteEffort?: "low" | "medium" | "high" | "xhigh" | "max";
|
|
19
|
+
maxTokens?: number;
|
|
20
|
+
}): import("./generator.js").EmailModel;
|
|
21
|
+
export const DEFAULT_CLAUDE_MODEL: "claude-opus-5-5";
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The email in a model's answer.
|
|
3
|
+
* @param {string} text
|
|
4
|
+
* @returns {EmailDraft}
|
|
5
|
+
*/
|
|
6
|
+
export function parseDraft(text: string): EmailDraft;
|
|
7
|
+
/**
|
|
8
|
+
* The MJML of a generated email, rewritten as the builder writes it.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} mjml
|
|
11
|
+
* @param {{variables?: {key: string}[], keepImages?: string[]}} [options]
|
|
12
|
+
* `variables`: the catalog (`unsubscribe` is always known);
|
|
13
|
+
* `keepImages`: image sources kept as they are (the sender's logo).
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeGeneratedMjml(mjml: string, { variables, keepImages }?: {
|
|
16
|
+
variables?: {
|
|
17
|
+
key: string;
|
|
18
|
+
}[];
|
|
19
|
+
keepImages?: string[];
|
|
20
|
+
}): string;
|
|
21
|
+
/**
|
|
22
|
+
* What still keeps the email from being used as it is — in English, written
|
|
23
|
+
* for the model that will correct it.
|
|
24
|
+
*
|
|
25
|
+
* @param {EmailDraft} draft
|
|
26
|
+
* @param {{kind?: string, compileWarnings?: {message: string}[]}} [options]
|
|
27
|
+
* @returns {{blocking: string[], advice: string[]}}
|
|
28
|
+
* `blocking`: the builder must not open it (rejected by the validator);
|
|
29
|
+
* `advice`: it opens, but is not what was asked.
|
|
30
|
+
*/
|
|
31
|
+
export function reviewDraft(draft: EmailDraft, { kind, compileWarnings }?: {
|
|
32
|
+
kind?: string;
|
|
33
|
+
compileWarnings?: {
|
|
34
|
+
message: string;
|
|
35
|
+
}[];
|
|
36
|
+
}): {
|
|
37
|
+
blocking: string[];
|
|
38
|
+
advice: string[];
|
|
39
|
+
};
|
|
40
|
+
export namespace GENERATION_ERRORS {
|
|
41
|
+
namespace NOTHING_TO_GENERATE {
|
|
42
|
+
let status: number;
|
|
43
|
+
let message: string;
|
|
44
|
+
}
|
|
45
|
+
namespace REFUSED {
|
|
46
|
+
let status_1: number;
|
|
47
|
+
export { status_1 as status };
|
|
48
|
+
let message_1: string;
|
|
49
|
+
export { message_1 as message };
|
|
50
|
+
}
|
|
51
|
+
namespace TRUNCATED {
|
|
52
|
+
let status_2: number;
|
|
53
|
+
export { status_2 as status };
|
|
54
|
+
let message_2: string;
|
|
55
|
+
export { message_2 as message };
|
|
56
|
+
}
|
|
57
|
+
namespace INVALID_ANSWER {
|
|
58
|
+
let status_3: number;
|
|
59
|
+
export { status_3 as status };
|
|
60
|
+
let message_3: string;
|
|
61
|
+
export { message_3 as message };
|
|
62
|
+
}
|
|
63
|
+
namespace INVALID_OUTPUT {
|
|
64
|
+
let status_4: number;
|
|
65
|
+
export { status_4 as status };
|
|
66
|
+
let message_4: string;
|
|
67
|
+
export { message_4 as message };
|
|
68
|
+
}
|
|
69
|
+
namespace NOTHING_TO_REWRITE {
|
|
70
|
+
let status_5: number;
|
|
71
|
+
export { status_5 as status };
|
|
72
|
+
let message_5: string;
|
|
73
|
+
export { message_5 as message };
|
|
74
|
+
}
|
|
75
|
+
namespace REWRITE_REFUSED {
|
|
76
|
+
let status_6: number;
|
|
77
|
+
export { status_6 as status };
|
|
78
|
+
let message_6: string;
|
|
79
|
+
export { message_6 as message };
|
|
80
|
+
}
|
|
81
|
+
namespace REWRITE_UNUSABLE {
|
|
82
|
+
let status_7: number;
|
|
83
|
+
export { status_7 as status };
|
|
84
|
+
let message_7: string;
|
|
85
|
+
export { message_7 as message };
|
|
86
|
+
}
|
|
87
|
+
namespace NOTHING_TO_SUGGEST {
|
|
88
|
+
let status_8: number;
|
|
89
|
+
export { status_8 as status };
|
|
90
|
+
let message_8: string;
|
|
91
|
+
export { message_8 as message };
|
|
92
|
+
}
|
|
93
|
+
namespace SUBJECTS_REFUSED {
|
|
94
|
+
let status_9: number;
|
|
95
|
+
export { status_9 as status };
|
|
96
|
+
let message_9: string;
|
|
97
|
+
export { message_9 as message };
|
|
98
|
+
}
|
|
99
|
+
namespace SUBJECTS_UNUSABLE {
|
|
100
|
+
let status_10: number;
|
|
101
|
+
export { status_10 as status };
|
|
102
|
+
let message_10: string;
|
|
103
|
+
export { message_10 as message };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class EmailGenerationError extends Error {
|
|
107
|
+
/**
|
|
108
|
+
* @param {keyof typeof GENERATION_ERRORS} code
|
|
109
|
+
* @param {string} [detail] what went wrong, for logs and for the model
|
|
110
|
+
*/
|
|
111
|
+
constructor(code: keyof typeof GENERATION_ERRORS, detail?: string);
|
|
112
|
+
code: "NOTHING_TO_GENERATE" | "REFUSED" | "TRUNCATED" | "INVALID_ANSWER" | "INVALID_OUTPUT" | "NOTHING_TO_REWRITE" | "REWRITE_REFUSED" | "REWRITE_UNUSABLE" | "NOTHING_TO_SUGGEST" | "SUBJECTS_REFUSED" | "SUBJECTS_UNUSABLE";
|
|
113
|
+
status: number;
|
|
114
|
+
detail: string;
|
|
115
|
+
}
|
|
116
|
+
export type EmailNode = import("../../engine/model.js").EmailNode;
|
|
117
|
+
export type EmailDraft = {
|
|
118
|
+
name: string;
|
|
119
|
+
subject: string;
|
|
120
|
+
preheader: string;
|
|
121
|
+
mjml: string;
|
|
122
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The input as the generator uses it: bounded strings, a known purpose or
|
|
3
|
+
* none, an https logo or none, the brand's colors and font or none.
|
|
4
|
+
* @param {GenerateInput} input
|
|
5
|
+
*/
|
|
6
|
+
export function cleanGenerateInput(input: GenerateInput): {
|
|
7
|
+
purpose: import("./purposes.js").EmailPurpose | null;
|
|
8
|
+
details: string;
|
|
9
|
+
language: string;
|
|
10
|
+
sender: {
|
|
11
|
+
name: string;
|
|
12
|
+
address: string;
|
|
13
|
+
logoUrl: string;
|
|
14
|
+
};
|
|
15
|
+
brand: {
|
|
16
|
+
font?: string | undefined;
|
|
17
|
+
heading?: string | undefined;
|
|
18
|
+
primary?: string | undefined;
|
|
19
|
+
};
|
|
20
|
+
variables: {
|
|
21
|
+
key: string;
|
|
22
|
+
label: string;
|
|
23
|
+
}[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* @param {{
|
|
27
|
+
* model: EmailModel,
|
|
28
|
+
* maxAttempts?: number,
|
|
29
|
+
* compile?: (mjml: string) => Promise<{warnings?: {message: string}[]}>,
|
|
30
|
+
* }} options
|
|
31
|
+
* `compile`: compiles the MJML and returns MJML's own warnings, fed back to
|
|
32
|
+
* the model (the server passes `compileMjml`).
|
|
33
|
+
*/
|
|
34
|
+
export function createEmailGenerator({ model, maxAttempts, compile }?: {
|
|
35
|
+
model: EmailModel;
|
|
36
|
+
maxAttempts?: number;
|
|
37
|
+
compile?: (mjml: string) => Promise<{
|
|
38
|
+
warnings?: {
|
|
39
|
+
message: string;
|
|
40
|
+
}[];
|
|
41
|
+
}>;
|
|
42
|
+
}): {
|
|
43
|
+
generate: (input: GenerateInput, { signal }?: {
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}) => Promise<GeneratedEmail>;
|
|
46
|
+
rewrite: (input: RewriteInput, options?: {
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
}) => Promise<{
|
|
49
|
+
text: string;
|
|
50
|
+
attempts: number;
|
|
51
|
+
}>;
|
|
52
|
+
suggestSubjects: (input: SubjectsInput, options?: {
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
}) => Promise<{
|
|
55
|
+
subjects: string[];
|
|
56
|
+
attempts: number;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* @typedef {{role: 'user'|'assistant', content: string}} ModelMessage
|
|
61
|
+
* @typedef {(request: {system: string, messages: ModelMessage[], schema: object, signal?: AbortSignal, task?: 'generate'|'rewrite'|'subjects'}) => Promise<string>} EmailModel
|
|
62
|
+
* `task`: a whole email, one text rewritten or subject ideas (lighter jobs:
|
|
63
|
+
* a model may answer them with less effort).
|
|
64
|
+
*
|
|
65
|
+
* @typedef {{
|
|
66
|
+
* purpose?: string|null,
|
|
67
|
+
* details?: string,
|
|
68
|
+
* language?: string,
|
|
69
|
+
* sender?: {name?: string, address?: string, logoUrl?: string},
|
|
70
|
+
* brand?: {primary?: string, heading?: string, font?: string},
|
|
71
|
+
* variables?: {key: string, label?: string}[],
|
|
72
|
+
* }} GenerateInput
|
|
73
|
+
* `brand`: the colors and font the email is written in (features/brand).
|
|
74
|
+
*
|
|
75
|
+
* @typedef {import('./draft.js').EmailDraft & {warnings: string[], attempts: number}} GeneratedEmail
|
|
76
|
+
*/
|
|
77
|
+
export const MAX_DETAILS_LENGTH: 2000;
|
|
78
|
+
export type ModelMessage = {
|
|
79
|
+
role: "user" | "assistant";
|
|
80
|
+
content: string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* `task`: a whole email, one text rewritten or subject ideas (lighter jobs:
|
|
84
|
+
* a model may answer them with less effort).
|
|
85
|
+
*/
|
|
86
|
+
export type EmailModel = (request: {
|
|
87
|
+
system: string;
|
|
88
|
+
messages: ModelMessage[];
|
|
89
|
+
schema: object;
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
task?: "generate" | "rewrite" | "subjects";
|
|
92
|
+
}) => Promise<string>;
|
|
93
|
+
/**
|
|
94
|
+
* `brand`: the colors and font the email is written in (features/brand).
|
|
95
|
+
*/
|
|
96
|
+
export type GenerateInput = {
|
|
97
|
+
purpose?: string | null;
|
|
98
|
+
details?: string;
|
|
99
|
+
language?: string;
|
|
100
|
+
sender?: {
|
|
101
|
+
name?: string;
|
|
102
|
+
address?: string;
|
|
103
|
+
logoUrl?: string;
|
|
104
|
+
};
|
|
105
|
+
brand?: {
|
|
106
|
+
primary?: string;
|
|
107
|
+
heading?: string;
|
|
108
|
+
font?: string;
|
|
109
|
+
};
|
|
110
|
+
variables?: {
|
|
111
|
+
key: string;
|
|
112
|
+
label?: string;
|
|
113
|
+
}[];
|
|
114
|
+
};
|
|
115
|
+
export type GeneratedEmail = import("./draft.js").EmailDraft & {
|
|
116
|
+
warnings: string[];
|
|
117
|
+
attempts: number;
|
|
118
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* English name of a language code ("fr" → "French"), the code itself when
|
|
3
|
+
* the runtime does not know it.
|
|
4
|
+
* @param {string} code
|
|
5
|
+
*/
|
|
6
|
+
export function languageName(code: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* The request message.
|
|
9
|
+
*
|
|
10
|
+
* @param {{
|
|
11
|
+
* purpose?: EmailPurpose|null,
|
|
12
|
+
* details?: string,
|
|
13
|
+
* language?: string,
|
|
14
|
+
* sender?: PromptSender,
|
|
15
|
+
* brand?: {primary?: string, heading?: string, font?: string},
|
|
16
|
+
* variables?: PromptVariable[],
|
|
17
|
+
* }} input
|
|
18
|
+
*/
|
|
19
|
+
export function buildUserPrompt({ purpose, details, language, sender, brand, variables }: {
|
|
20
|
+
purpose?: EmailPurpose | null;
|
|
21
|
+
details?: string;
|
|
22
|
+
language?: string;
|
|
23
|
+
sender?: PromptSender;
|
|
24
|
+
brand?: {
|
|
25
|
+
primary?: string;
|
|
26
|
+
heading?: string;
|
|
27
|
+
font?: string;
|
|
28
|
+
};
|
|
29
|
+
variables?: PromptVariable[];
|
|
30
|
+
}): string;
|
|
31
|
+
/**
|
|
32
|
+
* The message that asks the model to correct its email.
|
|
33
|
+
* @param {string[]} problems
|
|
34
|
+
*/
|
|
35
|
+
export function buildRepairPrompt(problems: string[]): string;
|
|
36
|
+
export namespace EMAIL_DRAFT_SCHEMA {
|
|
37
|
+
let type: string;
|
|
38
|
+
namespace properties {
|
|
39
|
+
namespace name {
|
|
40
|
+
let type_1: string;
|
|
41
|
+
export { type_1 as type };
|
|
42
|
+
export let description: string;
|
|
43
|
+
}
|
|
44
|
+
namespace subject {
|
|
45
|
+
let type_2: string;
|
|
46
|
+
export { type_2 as type };
|
|
47
|
+
let description_1: string;
|
|
48
|
+
export { description_1 as description };
|
|
49
|
+
}
|
|
50
|
+
namespace preheader {
|
|
51
|
+
let type_3: string;
|
|
52
|
+
export { type_3 as type };
|
|
53
|
+
let description_2: string;
|
|
54
|
+
export { description_2 as description };
|
|
55
|
+
}
|
|
56
|
+
namespace mjml {
|
|
57
|
+
let type_4: string;
|
|
58
|
+
export { type_4 as type };
|
|
59
|
+
let description_3: string;
|
|
60
|
+
export { description_3 as description };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let required: string[];
|
|
64
|
+
let additionalProperties: boolean;
|
|
65
|
+
}
|
|
66
|
+
export const SYSTEM_PROMPT: string;
|
|
67
|
+
export type EmailPurpose = import("./purposes.js").EmailPurpose;
|
|
68
|
+
export type PromptVariable = {
|
|
69
|
+
key: string;
|
|
70
|
+
label?: string;
|
|
71
|
+
};
|
|
72
|
+
export type PromptSender = {
|
|
73
|
+
name?: string;
|
|
74
|
+
address?: string;
|
|
75
|
+
logoUrl?: string;
|
|
76
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** @param {string|null|undefined} id */
|
|
2
|
+
export function purposeById(id: string | null | undefined): EmailPurpose | null;
|
|
3
|
+
/** Purposes grouped by category, in category order. */
|
|
4
|
+
export function purposesByCategory(purposes?: EmailPurpose[]): {
|
|
5
|
+
purposes: EmailPurpose[];
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}[];
|
|
9
|
+
/**
|
|
10
|
+
* WHAT AN EMAIL IS FOR — the purposes offered by "Generate".
|
|
11
|
+
*
|
|
12
|
+
* Each purpose carries a short BRIEF for the model: the sections and the
|
|
13
|
+
* information such an email needs (a password reset needs the link's expiry
|
|
14
|
+
* and a "not you?" line; a sale needs the offer, its end date and a code).
|
|
15
|
+
* The brief is the part of the prompt that changes with the purpose; the
|
|
16
|
+
* design and MJML rules stay the same (see `prompt.js`).
|
|
17
|
+
*
|
|
18
|
+
* `kind` sets the footer:
|
|
19
|
+
* marketing sent in bulk: an unsubscribe link is required (CAN-SPAM, GDPR);
|
|
20
|
+
* transactional triggered by the recipient's action: a discreet link to the
|
|
21
|
+
* email preferences;
|
|
22
|
+
* security account security: no unsubscribe link at all, a line says
|
|
23
|
+
* why the message was sent.
|
|
24
|
+
*
|
|
25
|
+
* Plain data: labels are English keys translated by the interface, briefs
|
|
26
|
+
* stay in English (they are written for the model, not shown).
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {'marketing'|'transactional'|'security'} PurposeKind
|
|
30
|
+
* @typedef {{id: string, label: string}} PurposeCategory
|
|
31
|
+
* @typedef {{id: string, label: string, category: string, kind: PurposeKind, brief: string}} EmailPurpose
|
|
32
|
+
*/
|
|
33
|
+
/** @type {PurposeCategory[]} */
|
|
34
|
+
export const PURPOSE_CATEGORIES: PurposeCategory[];
|
|
35
|
+
/** @type {EmailPurpose[]} */
|
|
36
|
+
export const EMAIL_PURPOSES: EmailPurpose[];
|
|
37
|
+
export type PurposeKind = "marketing" | "transactional" | "security";
|
|
38
|
+
export type PurposeCategory = {
|
|
39
|
+
id: string;
|
|
40
|
+
label: string;
|
|
41
|
+
};
|
|
42
|
+
export type EmailPurpose = {
|
|
43
|
+
id: string;
|
|
44
|
+
label: string;
|
|
45
|
+
category: string;
|
|
46
|
+
kind: PurposeKind;
|
|
47
|
+
brief: string;
|
|
48
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {{
|
|
3
|
+
* action: string,
|
|
4
|
+
* text: string,
|
|
5
|
+
* format?: 'html'|'plain',
|
|
6
|
+
* targetLanguage?: string,
|
|
7
|
+
* instruction?: string,
|
|
8
|
+
* }} RewriteInput
|
|
9
|
+
* `action`: an id of REWRITE_ACTIONS, `translate` (with `targetLanguage`)
|
|
10
|
+
* or `custom` (with `instruction`).
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The request as the rewriter uses it, or an error when there is nothing to do.
|
|
14
|
+
* @param {RewriteInput} input
|
|
15
|
+
*/
|
|
16
|
+
export function cleanRewriteInput(input: RewriteInput): {
|
|
17
|
+
format: "html" | "plain";
|
|
18
|
+
text: string;
|
|
19
|
+
change: string;
|
|
20
|
+
};
|
|
21
|
+
/** @param {{format: 'html'|'plain', text: string, change: string}} request */
|
|
22
|
+
export function buildRewritePrompt({ format, text, change }: {
|
|
23
|
+
format: "html" | "plain";
|
|
24
|
+
text: string;
|
|
25
|
+
change: string;
|
|
26
|
+
}): string;
|
|
27
|
+
/** The text in a model's answer: the JSON field, or the bare answer. @param {string} answer */
|
|
28
|
+
export function parseRewrite(answer: string): any;
|
|
29
|
+
/**
|
|
30
|
+
* What is wrong with a rewritten text, for the model (empty: usable).
|
|
31
|
+
* @param {string} original @param {string} rewritten @param {'html'|'plain'} format
|
|
32
|
+
*/
|
|
33
|
+
export function reviewRewrite(original: string, rewritten: string, format: "html" | "plain"): string[];
|
|
34
|
+
/**
|
|
35
|
+
* @param {{model: import('./generator.js').EmailModel, maxAttempts?: number}} options
|
|
36
|
+
* @returns {(input: RewriteInput, options?: {signal?: AbortSignal}) => Promise<{text: string, attempts: number}>}
|
|
37
|
+
*/
|
|
38
|
+
export function createRewriter({ model, maxAttempts }: {
|
|
39
|
+
model: import("./generator.js").EmailModel;
|
|
40
|
+
maxAttempts?: number;
|
|
41
|
+
}): (input: RewriteInput, options?: {
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
}) => Promise<{
|
|
44
|
+
text: string;
|
|
45
|
+
attempts: number;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* The actions offered as buttons, in order. `translate` takes a language,
|
|
49
|
+
* a custom change (`custom`) takes the user's words.
|
|
50
|
+
*/
|
|
51
|
+
export const REWRITE_ACTIONS: {
|
|
52
|
+
id: string;
|
|
53
|
+
label: string;
|
|
54
|
+
change: string;
|
|
55
|
+
}[];
|
|
56
|
+
/** Languages offered by "Translate", shown under their own name. */
|
|
57
|
+
export const REWRITE_LANGUAGES: string[];
|
|
58
|
+
export const MAX_REWRITE_TEXT: 6000;
|
|
59
|
+
export const MAX_REWRITE_INSTRUCTION: 300;
|
|
60
|
+
export namespace REWRITE_SCHEMA {
|
|
61
|
+
let type: string;
|
|
62
|
+
namespace properties {
|
|
63
|
+
namespace text {
|
|
64
|
+
let type_1: string;
|
|
65
|
+
export { type_1 as type };
|
|
66
|
+
export let description: string;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
let required: string[];
|
|
70
|
+
let additionalProperties: boolean;
|
|
71
|
+
}
|
|
72
|
+
export const REWRITE_SYSTEM_PROMPT: "You are an email copywriter. You rewrite ONE piece of text taken from an email, as asked, and answer with the rewritten text only, in the JSON field \"text\".\n\nThe text is one of:\n- HTML, the content of a text block: keep the same kind of markup. Allowed tags: <p>, <br>, <b>, <strong>, <i>, <em>, <u>, <s>, <span style=\"...\">, <a href=\"...\">, <ul>, <ol>, <li>, <sup>, <sub>. No other tag, no class, no new style. Keep bold, italic and links on the words they belong to.\n- plain text, the label of a button: answer plain text without HTML, a short call to action of a few words.\n\nAlways:\n- Keep every link: each <a href=\"...\"> of the text stays, with its href unchanged; the words of the link may change. Add no link.\n- Keep every merge tag in double braces exactly as written, e.g. {{contact.firstname}} or {{ unsubscribe }}. In {{key|fallback}} only the fallback after \"|\" may change (translated, for instance). Add no merge tag.\n- Keep [placeholders in square brackets]: the user replaces them later. Translate their words when translating.\n- Invent no facts: no new price, date, number, name, guarantee or promise.\n- Write in the language of the text, unless asked to translate.\n- No preamble, no comment, no quotation marks around the text.";
|
|
73
|
+
/**
|
|
74
|
+
* `action`: an id of REWRITE_ACTIONS, `translate` (with `targetLanguage`)
|
|
75
|
+
* or `custom` (with `instruction`).
|
|
76
|
+
*/
|
|
77
|
+
export type RewriteInput = {
|
|
78
|
+
action: string;
|
|
79
|
+
text: string;
|
|
80
|
+
format?: "html" | "plain";
|
|
81
|
+
targetLanguage?: string;
|
|
82
|
+
instruction?: string;
|
|
83
|
+
};
|