@kontakto/email-template-editor 2.0.0 → 2.2.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/README.md CHANGED
@@ -59,6 +59,9 @@ function MyApp() {
59
59
  | `renameTemplate` | function | - | Renames a template: `(id, newSlug) => void \| Promise<void>` |
60
60
  | `setTemplateKind` | function | - | Promotes/demotes a row between template and sample: `(id, kind) => void \| Promise<void>`. When omitted, promote/demote menu items are hidden. |
61
61
  | `saveAs` | function | - | Saves template with a new name: `(name, payload: SavePayload) => Promise<{ id, slug }>` |
62
+ | `uploadImage` | function | - | Uploads a single image file: `(file: File) => Promise<UploadedImage>`. Enables the Upload button on the Image inspector, drag-and-drop on the canvas, and paste-image-to-insert. When omitted, all upload UI is hidden and URL paste remains the only way to set an image. |
63
+ | `loadImages` | function | - | Lists previously uploaded images for the library picker: `() => Promise<LibraryImage[]>`. Enables the "Library" button on the Image inspector. When omitted, the library button is hidden. |
64
+ | `deleteImage` | function | - | Deletes an image from the library by URL: `(url: string) => Promise<void>`. When omitted, the per-row delete button in the library is hidden. |
62
65
 
63
66
  `TemplateListItem` is the lean list-endpoint shape (no `editor_config`):
64
67
 
@@ -91,12 +94,19 @@ Email subject and template variables are stored on the `EmailLayout` block's dat
91
94
  type EmailLayoutData = {
92
95
  // ...style fields
93
96
  subject?: string;
94
- variables?: Array<{ name: string; description?: string }>;
97
+ variables?: Array<{ name: string; description?: string; sampleValue?: string }>;
95
98
  };
96
99
  ```
97
100
 
98
101
  The editor renders a subject input above the canvas (always visible, supports `{{variable}}` syntax) and a Variables tab in the right inspector panel for declaring variables. Both persist via the standard save flow — consumers who previously stored `subject` in a separate DB column can read it from the saved `editor_config` instead.
99
102
 
103
+ The Variables tab supports Handlebars-aware management:
104
+
105
+ - **Add/rename/delete**. Names follow Handlebars identifier rules (`[A-Za-z_][A-Za-z0-9_]*`, max 64 chars, reserved words rejected). Renaming a declared variable rewrites all `{{oldName}}` and `{{oldName.*}}` tokens in the subject and in text/heading/button/html blocks — including inside block helpers (`{{#if}}`, `{{#each}}`, `{{#unless}}`, `{{#with}}`).
106
+ - **Usage indicators.** Each row shows how many times the variable is referenced, or "Unused in body" if the declared name never appears. Tokens found in the body that aren't declared surface at the top of the panel with a one-click "add as variable" action.
107
+ - **Insert at cursor.** Focus a text/heading/button/html editor or the subject input, then click the Insert button next to a variable to splice `{{name}}` at the caret.
108
+ - **Sample values.** Each row has an optional `sampleValue` field that travels with the template (persisted on `editor_config.root.data.variables[].sampleValue`). In Preview mode, `{{name}}` and `{{name.*}}` tokens in the subject and in text/heading/button/html blocks render with the sample value substituted in; block helpers (`{{#if}}`, `{{#each}}`, …) are stripped so their content renders inline, but the control flow is not evaluated. Edit mode always shows the raw tokens.
109
+
100
110
  #### Save payload
101
111
 
102
112
  `onSave` and `saveAs` receive the same `SavePayload`. The editor renders body HTML and plain text on every save so consumers don't ship the renderer themselves:
@@ -113,6 +123,41 @@ type SavePayload = {
113
123
 
114
124
  The `renderToStaticMarkup` and `renderToText` utilities are also exposed publicly for consumers that need to re-render outside the save flow (e.g. batch jobs).
115
125
 
126
+ #### Image upload and library (BYO backend)
127
+
128
+ The editor delegates image storage to the consumer through three optional callbacks. When omitted, the corresponding UI is hidden and URL paste remains the fallback.
129
+
130
+ ```ts
131
+ type UploadedImage = {
132
+ url: string;
133
+ width?: number;
134
+ height?: number;
135
+ alt?: string;
136
+ };
137
+
138
+ type LibraryImage = UploadedImage & {
139
+ thumbnailUrl?: string;
140
+ uploadedAt?: string;
141
+ };
142
+ ```
143
+
144
+ - **`uploadImage(file)`** — receives a single `File`, uploads it (S3, R2, Bunny, presigned PUT, etc.), and returns the public `url` plus optional intrinsic dimensions and alt text. Wires up: the Upload button in the Image inspector, drag-and-drop of an image file onto the canvas, and paste-image-from-clipboard.
145
+ - **`loadImages()`** — returns the consumer's image list for the "Library" picker dialog (grid + filter by alt/URL).
146
+ - **`deleteImage(url)`** — removes an image from the library; surfaces a delete button on hover in the picker.
147
+
148
+ Reference upload handler:
149
+
150
+ ```ts
151
+ const uploadImage = async (file: File) => {
152
+ const form = new FormData();
153
+ form.append('file', file);
154
+ const res = await fetch('/api/images', { method: 'POST', body: form });
155
+ return res.json(); // { url, width, height }
156
+ };
157
+ ```
158
+
159
+ Newly uploaded images get their `width` / `height` set on the resulting Image block — important for Outlook, which needs explicit dimensions to lay the email out before images load.
160
+
116
161
  | `theme` | object | theme.ts | Custom theme for the EmailEditor, must be a Material UI theme object |
117
162
 
118
163
  #### Imperative API