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.
Files changed (82) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/README.md +309 -12
  3. package/dist/builder.js +44 -0
  4. package/dist/builder.js.map +1 -0
  5. package/dist/chunk-AT73Y7JA.js +1427 -0
  6. package/dist/chunk-AT73Y7JA.js.map +1 -0
  7. package/dist/chunk-HHLOE6OZ.js +1682 -0
  8. package/dist/chunk-HHLOE6OZ.js.map +1 -0
  9. package/dist/chunk-JNHFZXDL.js +1776 -0
  10. package/dist/chunk-JNHFZXDL.js.map +1 -0
  11. package/dist/chunk-MY6WPDT4.js +6110 -0
  12. package/dist/chunk-MY6WPDT4.js.map +1 -0
  13. package/dist/chunk-NAEGU6PR.js +538 -0
  14. package/dist/chunk-NAEGU6PR.js.map +1 -0
  15. package/dist/chunk-TGDZ4NFI.js +114 -0
  16. package/dist/chunk-TGDZ4NFI.js.map +1 -0
  17. package/dist/index.js +124 -4866
  18. package/dist/index.js.map +1 -1
  19. package/dist/library.js +31 -0
  20. package/dist/library.js.map +1 -0
  21. package/dist/server.js +554 -210
  22. package/dist/server.js.map +1 -1
  23. package/dist/styles.css +1 -1
  24. package/dist/types/blocks/shared.d.ts +20 -0
  25. package/dist/types/builder.d.ts +8 -0
  26. package/dist/types/core/EmailEditor.d.ts +72 -19
  27. package/dist/types/core/constants.d.ts +1 -1
  28. package/dist/types/core/mjmlDefaults.d.ts +13 -0
  29. package/dist/types/core/scrollbars.d.ts +13 -0
  30. package/dist/types/defaultEditor.d.ts +11 -0
  31. package/dist/types/engine/NativeAdapter.d.ts +28 -0
  32. package/dist/types/engine/canvas/CanvasView.d.ts +88 -0
  33. package/dist/types/engine/canvas/canvasStyles.d.ts +3 -0
  34. package/dist/types/engine/canvas/icons.d.ts +14 -0
  35. package/dist/types/engine/canvas/morph.d.ts +21 -0
  36. package/dist/types/engine/canvasMjml.d.ts +30 -0
  37. package/dist/types/engine/mjml.d.ts +31 -0
  38. package/dist/types/engine/model.d.ts +130 -0
  39. package/dist/types/engine/placement.d.ts +69 -0
  40. package/dist/types/engine/richText.d.ts +15 -0
  41. package/dist/types/engine/unwrapModule.d.ts +29 -0
  42. package/dist/types/features/brand/BrandButton.d.ts +4 -0
  43. package/dist/types/features/brand/brand.d.ts +108 -0
  44. package/dist/types/features/brand/extract.d.ts +61 -0
  45. package/dist/types/features/brand/index.d.ts +2 -0
  46. package/dist/types/features/brand/messages.d.ts +13 -0
  47. package/dist/types/features/brand/useBrand.d.ts +22 -0
  48. package/dist/types/features/generate/GenerateButton.d.ts +3 -0
  49. package/dist/types/features/generate/RewriteText.d.ts +5 -0
  50. package/dist/types/features/generate/SubjectIdeas.d.ts +16 -0
  51. package/dist/types/features/generate/chip.d.ts +1 -0
  52. package/dist/types/features/generate/claude.d.ts +21 -0
  53. package/dist/types/features/generate/draft.d.ts +122 -0
  54. package/dist/types/features/generate/generator.d.ts +118 -0
  55. package/dist/types/features/generate/index.d.ts +2 -0
  56. package/dist/types/features/generate/prompt.d.ts +76 -0
  57. package/dist/types/features/generate/purposes.d.ts +48 -0
  58. package/dist/types/features/generate/rewrite.d.ts +83 -0
  59. package/dist/types/features/generate/subjects.d.ts +83 -0
  60. package/dist/types/features/preview/PreviewButton.d.ts +1 -1
  61. package/dist/types/features/preview/darkMode.d.ts +48 -0
  62. package/dist/types/features/templates/gallery.d.ts +7 -0
  63. package/dist/types/index.d.ts +14 -10
  64. package/dist/types/library.d.ts +5 -0
  65. package/dist/types/server/brand.d.ts +32 -0
  66. package/dist/types/server/brandImport.d.ts +93 -0
  67. package/dist/types/server/compileMjml.d.ts +2 -0
  68. package/dist/types/server/handler.d.ts +25 -0
  69. package/dist/types/server/index.d.ts +11 -0
  70. package/dist/types/storage/client.d.ts +79 -3
  71. package/dist/types/ui/EmailStudio.d.ts +42 -0
  72. package/dist/types/ui/fields.d.ts +2 -1
  73. package/dist/types/ui/library/TemplateLibrary.d.ts +41 -0
  74. package/dist/types/ui/primitives/button.d.ts +2 -2
  75. package/dist/types/ui/primitives/color-picker.d.ts +2 -1
  76. package/dist/types/ui/useLayoutWidth.d.ts +1 -1
  77. package/package.json +27 -4
  78. package/dist/chunk-MTALS5GC.js +0 -747
  79. package/dist/chunk-MTALS5GC.js.map +0 -1
  80. package/dist/types/adapters/grapesjs/GrapesJsAdapter.d.ts +0 -33
  81. package/dist/types/adapters/grapesjs/engineConfig.d.ts +0 -162
  82. 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,2 @@
1
+ /** @type {import('../../core/EmailEditor.js').EmailBuilderFeature} */
2
+ export const brandFeature: import("../../core/EmailEditor.js").EmailBuilderFeature;
@@ -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,3 @@
1
+ export default function GenerateButton({ context }: {
2
+ context: any;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ export default function RewriteText({ editor, selection, rewrite }: {
2
+ editor: any;
3
+ selection: any;
4
+ rewrite: any;
5
+ }): import("react").JSX.Element;
@@ -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,2 @@
1
+ /** @type {import('../../core/EmailEditor.js').EmailBuilderFeature} */
2
+ export const generateFeature: import("../../core/EmailEditor.js").EmailBuilderFeature;
@@ -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
+ };