@seliseblocks/mailcraft 0.1.0 → 0.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/DOCS.md +240 -12
- package/README.md +11 -3
- package/README.md.txt +134 -0
- package/dist/mailcraft-editor.bundle.js +51 -39
- package/dist/mailcraft-editor.bundle.js.map +3 -3
- package/examples/templates/order-confirmed.html +80 -80
- package/examples/vanilla.html +242 -23
- package/package.json +7 -2
- package/src/core/assets.js +10 -15
- package/src/core/binder.js +120 -118
- package/src/core/blocks.js +14 -1
- package/src/core/css-cascade.js +117 -117
- package/src/core/editor-core.js +1530 -1492
- package/src/core/export.js +142 -55
- package/src/core/i18n/ar.js +219 -177
- package/src/core/i18n/bg.js +196 -152
- package/src/core/i18n/bn.js +218 -176
- package/src/core/i18n/ca.js +196 -152
- package/src/core/i18n/cs.js +196 -152
- package/src/core/i18n/da.js +196 -152
- package/src/core/i18n/de-CH.js +196 -152
- package/src/core/i18n/de.js +196 -152
- package/src/core/i18n/dz.js +221 -179
- package/src/core/i18n/el.js +196 -152
- package/src/core/i18n/en.js +3 -10
- package/src/core/i18n/es.js +196 -152
- package/src/core/i18n/et.js +196 -152
- package/src/core/i18n/fi.js +196 -152
- package/src/core/i18n/fr.js +196 -152
- package/src/core/i18n/hr.js +196 -152
- package/src/core/i18n/hu.js +196 -152
- package/src/core/i18n/index.js +83 -83
- package/src/core/i18n/it.js +196 -152
- package/src/core/i18n/lt.js +196 -152
- package/src/core/i18n/lv.js +196 -152
- package/src/core/i18n/nb.js +196 -152
- package/src/core/i18n/nl.js +196 -152
- package/src/core/i18n/pl.js +196 -152
- package/src/core/i18n/pt.js +196 -152
- package/src/core/i18n/ro.js +196 -152
- package/src/core/i18n/ru.js +196 -152
- package/src/core/i18n/sk.js +196 -152
- package/src/core/i18n/sl.js +196 -152
- package/src/core/i18n/sv.js +196 -152
- package/src/core/i18n/tables.js +50 -50
- package/src/core/i18n/tr.js +196 -152
- package/src/core/i18n/uk.js +196 -152
- package/src/core/icons.js +237 -235
- package/src/core/ids.js +1 -1
- package/src/core/import-html.js +1025 -959
- package/src/core/layout-style.js +100 -100
- package/src/core/parse.js +10 -10
- package/src/core/placeholder.js +15 -15
- package/src/core/sanitize.js +141 -141
- package/src/core/storage-limits.js +184 -184
- package/src/core/storage.js +85 -85
- package/src/core/theme.js +1 -1
- package/src/core/variables.js +11 -11
- package/src/index.js +9 -9
- package/src/mailcraft-editor.js +26 -14
- package/src/render/block-body.js +49 -6
- package/src/render/canvas.js +31 -2
- package/src/render/fields.js +602 -588
- package/src/render/focus-preserve.js +158 -158
- package/src/render/rte.js +241 -212
- package/src/render/screenshot.js +132 -132
- package/src/render/story.js +415 -415
- package/src/render/style.js +8 -0
- package/types/index.d.ts +419 -0
package/src/render/style.js
CHANGED
|
@@ -109,6 +109,14 @@ export const STYLE = `
|
|
|
109
109
|
.mc-block-el { outline: 1px solid transparent; outline-offset: 1px; transition: outline-color 0.12s; }
|
|
110
110
|
.mc-block-el.is-selected { outline: 2px solid var(--ed-accent-sheet); }
|
|
111
111
|
.mc-block-el:not(.is-selected):hover { outline: 1px dashed var(--ed-accent-sheet); }
|
|
112
|
+
/*
|
|
113
|
+
* The other half of the WebKit fix in render/canvas.js: a block is a
|
|
114
|
+
* draggable element, and Safari would rather drag the text inside it than
|
|
115
|
+
* let the caret select it. Taking the editable copy out of the drag path,
|
|
116
|
+
* and saying outright that it is selectable, keeps double-click-to-select
|
|
117
|
+
* and press-and-sweep working there. Harmless everywhere else.
|
|
118
|
+
*/
|
|
119
|
+
[contenteditable="true"] { -webkit-user-drag: none; -webkit-user-select: text; user-select: text; }
|
|
112
120
|
/*
|
|
113
121
|
* The grab strip runs the row's whole left edge (see render/canvas.js); the
|
|
114
122
|
* visible part is a round badge straddling the selection border. It takes
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written type declarations for `@seliseblocks/mailcraft`.
|
|
3
|
+
*
|
|
4
|
+
* The runtime is plain JavaScript; these types describe the public contract
|
|
5
|
+
* documented in DOCS.md. Anything DOCS.md calls internal (`EditorCore`,
|
|
6
|
+
* `renderDoc`, the document object) is typed loosely on purpose — its shape
|
|
7
|
+
* is free to change between versions.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// i18n
|
|
12
|
+
|
|
13
|
+
/** A message table: i18n keys to translated strings. `EN` holds every key. */
|
|
14
|
+
export type MessageTable = Record<string, string>;
|
|
15
|
+
|
|
16
|
+
export interface LocaleInfo {
|
|
17
|
+
tag: string;
|
|
18
|
+
name: string;
|
|
19
|
+
rtl?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The 31 shipped locale tags. */
|
|
23
|
+
export type LocaleTag =
|
|
24
|
+
| 'en' | 'ar' | 'bg' | 'bn' | 'ca' | 'cs' | 'da' | 'de' | 'de-CH' | 'dz'
|
|
25
|
+
| 'el' | 'es' | 'et' | 'fi' | 'fr' | 'hr' | 'hu' | 'it' | 'lt' | 'lv'
|
|
26
|
+
| 'nb' | 'nl' | 'pl' | 'pt' | 'ro' | 'ru' | 'sk' | 'sl' | 'sv' | 'tr' | 'uk';
|
|
27
|
+
|
|
28
|
+
/** Every locale that ships — metadata only, no message tables. */
|
|
29
|
+
export const LOCALES: readonly LocaleInfo[];
|
|
30
|
+
|
|
31
|
+
/** Every shipped message table, keyed by locale tag. Importing this pulls all translations in. */
|
|
32
|
+
export const LOCALE_TABLES: Record<LocaleTag, MessageTable>;
|
|
33
|
+
|
|
34
|
+
/** The English table — the fallback for every key a custom table leaves out. */
|
|
35
|
+
export const EN: MessageTable;
|
|
36
|
+
|
|
37
|
+
/** Every message key the editor ever asks for. */
|
|
38
|
+
export const MESSAGE_KEYS: readonly string[];
|
|
39
|
+
|
|
40
|
+
/** Resolves a key against `overrides`, then `EN`, then the key itself; interpolates `{name}` params. */
|
|
41
|
+
export function createTranslator(
|
|
42
|
+
overrides?: MessageTable | null,
|
|
43
|
+
): (key: string, params?: Record<string, string | number>) => string;
|
|
44
|
+
|
|
45
|
+
/** Merges a locale over a base — the documented way to build a `.messages` value. */
|
|
46
|
+
export function defineMessages(base: MessageTable, overrides?: MessageTable): MessageTable;
|
|
47
|
+
|
|
48
|
+
/** Keys in `base` (default: `EN`) that `locale` does not translate, sorted. */
|
|
49
|
+
export function missingKeys(locale: MessageTable, base?: MessageTable): string[];
|
|
50
|
+
|
|
51
|
+
/** Whether a locale tag is written right-to-left. Metadata — `dir` is what flips the layout. */
|
|
52
|
+
export function isRtl(tag: string): boolean;
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Storage: the file-library contract the host implements
|
|
56
|
+
|
|
57
|
+
export interface Asset {
|
|
58
|
+
/** Stable id. With a provider this is the backend's file id — `remove()` gets it back verbatim. */
|
|
59
|
+
id: string;
|
|
60
|
+
/** Display/file name. */
|
|
61
|
+
name: string;
|
|
62
|
+
/** Resolvable image URL. Must outlive the send: an email renders it long after the editor closed. */
|
|
63
|
+
url: string;
|
|
64
|
+
/** Folder display name. */
|
|
65
|
+
folder: string;
|
|
66
|
+
/** Backend folder id, when the provider has one. */
|
|
67
|
+
folderId?: string;
|
|
68
|
+
/** Pixel width (0 when unknown). */
|
|
69
|
+
w: number;
|
|
70
|
+
/** Pixel height (0 when unknown). */
|
|
71
|
+
ht: number;
|
|
72
|
+
/** Bytes. */
|
|
73
|
+
size: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface StorageLimits {
|
|
77
|
+
/** Allowed MIME types, e.g. `['image/jpeg', 'image/png', 'image/gif']`. Required. */
|
|
78
|
+
accept: string[];
|
|
79
|
+
/** Per-file byte ceiling. Required. */
|
|
80
|
+
maxBytes: number;
|
|
81
|
+
maxWidth?: number;
|
|
82
|
+
maxHeight?: number;
|
|
83
|
+
maxFilesPerDrop?: number;
|
|
84
|
+
/** SVG is refused even when listed in `accept` unless this is also true. */
|
|
85
|
+
allowSvg?: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface StorageListQuery {
|
|
89
|
+
folderId: string;
|
|
90
|
+
/** Opaque — whatever the provider returned last, handed back to fetch the next page. */
|
|
91
|
+
cursor: string | null;
|
|
92
|
+
query: string;
|
|
93
|
+
/** Aborted when a newer listing supersedes this one. */
|
|
94
|
+
signal?: AbortSignal | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface StorageListResult {
|
|
98
|
+
items: Asset[];
|
|
99
|
+
/** `null` when there are no more pages. */
|
|
100
|
+
cursor: string | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface StorageUploadInfo {
|
|
104
|
+
folderId: string;
|
|
105
|
+
/** Pixel dimensions the validation pass already measured client-side. */
|
|
106
|
+
width: number;
|
|
107
|
+
height: number;
|
|
108
|
+
signal?: AbortSignal | null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface StorageProvider {
|
|
112
|
+
/** One page of assets. */
|
|
113
|
+
list(q: StorageListQuery): Promise<StorageListResult>;
|
|
114
|
+
/** Stores one already-validated file and resolves to the asset that represents it. */
|
|
115
|
+
upload(file: File, info: StorageUploadInfo): Promise<Asset>;
|
|
116
|
+
/** Selectable folders. Omit for a flat library. */
|
|
117
|
+
folders?(): Promise<Array<{ id: string; name: string }>>;
|
|
118
|
+
/** Deletes. Without it the library's DEL only drops the tile from view. */
|
|
119
|
+
remove?(asset: Asset): Promise<void>;
|
|
120
|
+
/** Provider-declared ceilings. `editor.storageLimits` wins over these, per key. */
|
|
121
|
+
limits?: StorageLimits;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The synthetic "all files" folder id (the empty string). */
|
|
125
|
+
export const ALL_FOLDER_ID: '';
|
|
126
|
+
|
|
127
|
+
/** Coerces whatever a provider returned into the library's asset shape; `probe` fills in what the client already measured. */
|
|
128
|
+
export function normalizeAsset(
|
|
129
|
+
raw: unknown,
|
|
130
|
+
probe?: { name?: string; w?: number; ht?: number; size?: number } | null,
|
|
131
|
+
): Asset;
|
|
132
|
+
|
|
133
|
+
/** `hostLimits` over `providerLimits`, per key. `null` when neither is set. */
|
|
134
|
+
export function resolveLimits(
|
|
135
|
+
hostLimits?: Partial<StorageLimits> | null,
|
|
136
|
+
providerLimits?: Partial<StorageLimits> | null,
|
|
137
|
+
): StorageLimits | null;
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Upload validation, reusable outside the editor
|
|
141
|
+
|
|
142
|
+
export interface AcceptedFile {
|
|
143
|
+
file: File;
|
|
144
|
+
/** Sanitized name — see `sanitizeName`. */
|
|
145
|
+
name: string;
|
|
146
|
+
/** Byte-sniffed MIME type, never `file.type`. */
|
|
147
|
+
type: string;
|
|
148
|
+
size: number;
|
|
149
|
+
w: number;
|
|
150
|
+
ht: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface RejectedFile {
|
|
154
|
+
name: string;
|
|
155
|
+
/** An i18n key (`storage.err*`) naming the reason, translated by the same table as the chrome. */
|
|
156
|
+
key: string;
|
|
157
|
+
params: Record<string, string | number>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Splits a file list into what may be uploaded and what may not, against the given limits. */
|
|
161
|
+
export function validateFiles(
|
|
162
|
+
list: ArrayLike<File> | Iterable<File> | null | undefined,
|
|
163
|
+
limits?: Partial<StorageLimits> | null,
|
|
164
|
+
): Promise<{ accepted: AcceptedFile[]; rejected: RejectedFile[] }>;
|
|
165
|
+
|
|
166
|
+
/** Strips path syntax and control characters from a filename and caps its length. */
|
|
167
|
+
export function sanitizeName(name: string): string;
|
|
168
|
+
|
|
169
|
+
/** An `accept` attribute for the file picker, so the OS dialog greys out what validation would refuse. */
|
|
170
|
+
export function acceptAttribute(limits?: Partial<StorageLimits> | null): string;
|
|
171
|
+
|
|
172
|
+
/** The i18n key naming what a limits object is missing, or `null` when it is usable. */
|
|
173
|
+
export function limitsProblem(limits: unknown): string | null;
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Editor options shared by the attribute/property surface and createEditor
|
|
177
|
+
|
|
178
|
+
export type ToolbarPart =
|
|
179
|
+
| 'logo' | 'status' | 'device' | 'undo' | 'redo'
|
|
180
|
+
| 'theme' | 'ai' | 'code' | 'preview' | 'export';
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The `toolbar` property: `false` for no bar, or an object where only the
|
|
184
|
+
* keys set to `false` do anything — unlisted parts stay on. (The attribute
|
|
185
|
+
* form is a string: `none`, `all`, or a comma list of the parts to keep.)
|
|
186
|
+
*/
|
|
187
|
+
export type ToolbarOption = boolean | string | Partial<Record<ToolbarPart, boolean>>;
|
|
188
|
+
|
|
189
|
+
/** The `footer` property: `false` to remove the strip, a string to replace the line, or a config object. */
|
|
190
|
+
export type FooterOption =
|
|
191
|
+
| boolean
|
|
192
|
+
| string
|
|
193
|
+
| { text?: string; href?: string; target?: string; show?: boolean };
|
|
194
|
+
|
|
195
|
+
/** One `async (prompt) => text` function — the whole AI seam. */
|
|
196
|
+
export type AiProvider = (prompt: string) => string | Promise<string>;
|
|
197
|
+
|
|
198
|
+
/** Social-icon override. Falls back to the built-in icon when unset, throwing, or returning a non-node. */
|
|
199
|
+
export type IconProvider = (
|
|
200
|
+
platformKey: string,
|
|
201
|
+
ctx: { label: string; size: number; color: string },
|
|
202
|
+
) => Node;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The editor's internal document. NOT a public contract — the shape is free
|
|
206
|
+
* to change between versions. Store `exportHtml()` instead; this type exists
|
|
207
|
+
* so the `change` event and the internal accessors have something to name.
|
|
208
|
+
*/
|
|
209
|
+
export type EmailDocument = Record<string, any>;
|
|
210
|
+
|
|
211
|
+
/** What `loadTemplate` accepts: `html` (through the importer), a `doc`, or a `build()` that makes one per use. */
|
|
212
|
+
export interface Template {
|
|
213
|
+
name?: string;
|
|
214
|
+
html?: string;
|
|
215
|
+
doc?: EmailDocument;
|
|
216
|
+
build?: () => EmailDocument;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// The element
|
|
221
|
+
|
|
222
|
+
export interface MailCraftEditorEventMap extends HTMLElementEventMap {
|
|
223
|
+
/** The internal document — for dirty-tracking, not persistence. */
|
|
224
|
+
change: CustomEvent<EmailDocument>;
|
|
225
|
+
/** The exported HTML string. */
|
|
226
|
+
export: CustomEvent<string>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The `<mailcraft-editor>` custom element. Importing the package registers it.
|
|
231
|
+
*/
|
|
232
|
+
export class MailCraftEditor extends HTMLElement {
|
|
233
|
+
/** Merge variables. Reads back as an array; accepts a comma-separated string or an array. */
|
|
234
|
+
get variables(): string[];
|
|
235
|
+
set variables(value: string | string[]);
|
|
236
|
+
|
|
237
|
+
/** `false` for no bar, `{ part: false }` to drop parts. Reads back whatever was set (or the attribute string). */
|
|
238
|
+
get toolbar(): ToolbarOption;
|
|
239
|
+
set toolbar(value: ToolbarOption);
|
|
240
|
+
|
|
241
|
+
/** `false` to remove the strip, a string to replace it, or `{ text, href, target, show }`. */
|
|
242
|
+
get footer(): FooterOption;
|
|
243
|
+
set footer(value: FooterOption);
|
|
244
|
+
|
|
245
|
+
/** `inherit` or a CSS font-family stack. Editor chrome only. */
|
|
246
|
+
uiFont: string;
|
|
247
|
+
|
|
248
|
+
/** A CSS color, `var(--token)`, or `inherit`. One color repaints the editor chrome. */
|
|
249
|
+
accent: string;
|
|
250
|
+
|
|
251
|
+
/** Per-key overrides for the UI strings; `EN` lists every key. */
|
|
252
|
+
messages: MessageTable | null;
|
|
253
|
+
|
|
254
|
+
aiProvider: AiProvider | null;
|
|
255
|
+
iconProvider: IconProvider | null;
|
|
256
|
+
|
|
257
|
+
/** `null` drops back to the empty local library. */
|
|
258
|
+
storageProvider: StorageProvider | null;
|
|
259
|
+
|
|
260
|
+
/** Merged over `storageProvider.limits` per key, this side winning. */
|
|
261
|
+
storageLimits: StorageLimits | null;
|
|
262
|
+
|
|
263
|
+
/** Send-ready email HTML — valid input to the importer, so saving the export is saving the work. */
|
|
264
|
+
exportHtml(): string;
|
|
265
|
+
|
|
266
|
+
/** Parses email HTML back onto the canvas. Returns the number of rows produced. */
|
|
267
|
+
importHtml(html: string): number;
|
|
268
|
+
|
|
269
|
+
/** Applies a template as a normal undoable edit. The input is never mutated. */
|
|
270
|
+
loadTemplate(tpl: Template): void;
|
|
271
|
+
|
|
272
|
+
undo(): void;
|
|
273
|
+
redo(): void;
|
|
274
|
+
|
|
275
|
+
/** The full template as a PNG. */
|
|
276
|
+
screenshotPng(): Promise<Blob>;
|
|
277
|
+
|
|
278
|
+
/** Opens the story-style screenshot viewer. */
|
|
279
|
+
previewScreenshot(): void;
|
|
280
|
+
|
|
281
|
+
/** Saves a screenshot — captures first if no blob is passed. */
|
|
282
|
+
downloadScreenshot(blob?: Blob): Promise<void>;
|
|
283
|
+
|
|
284
|
+
/** Copies a screenshot to the clipboard — captures first if no blob is passed. */
|
|
285
|
+
copyScreenshot(blob?: Blob): Promise<void>;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* NOT public API: the internal document, kept for undo, autosave and tests.
|
|
289
|
+
* Its shape is free to change between versions — store `exportHtml()` instead.
|
|
290
|
+
*/
|
|
291
|
+
getContent(): EmailDocument;
|
|
292
|
+
/** NOT public API — see `getContent`. */
|
|
293
|
+
setContent(doc: EmailDocument): void;
|
|
294
|
+
|
|
295
|
+
/** The internal engine. Its shape is free to change between versions. */
|
|
296
|
+
readonly core: EditorCore;
|
|
297
|
+
|
|
298
|
+
addEventListener<K extends keyof MailCraftEditorEventMap>(
|
|
299
|
+
type: K,
|
|
300
|
+
listener: (this: MailCraftEditor, ev: MailCraftEditorEventMap[K]) => any,
|
|
301
|
+
options?: boolean | AddEventListenerOptions,
|
|
302
|
+
): void;
|
|
303
|
+
addEventListener(
|
|
304
|
+
type: string,
|
|
305
|
+
listener: EventListenerOrEventListenerObject,
|
|
306
|
+
options?: boolean | AddEventListenerOptions,
|
|
307
|
+
): void;
|
|
308
|
+
removeEventListener<K extends keyof MailCraftEditorEventMap>(
|
|
309
|
+
type: K,
|
|
310
|
+
listener: (this: MailCraftEditor, ev: MailCraftEditorEventMap[K]) => any,
|
|
311
|
+
options?: boolean | EventListenerOptions,
|
|
312
|
+
): void;
|
|
313
|
+
removeEventListener(
|
|
314
|
+
type: string,
|
|
315
|
+
listener: EventListenerOrEventListenerObject,
|
|
316
|
+
options?: boolean | EventListenerOptions,
|
|
317
|
+
): void;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// createEditor — mount into a container
|
|
322
|
+
|
|
323
|
+
export interface CreateEditorOptions {
|
|
324
|
+
/** Initial email HTML, applied through the importer as an undoable edit. */
|
|
325
|
+
html?: string;
|
|
326
|
+
/** Template name for that content — only read alongside `html`. */
|
|
327
|
+
name?: string;
|
|
328
|
+
variables?: string | string[];
|
|
329
|
+
/** One of the 31 shipped tags. */
|
|
330
|
+
locale?: string;
|
|
331
|
+
/** Defaults from `locale`. */
|
|
332
|
+
dir?: 'ltr' | 'rtl';
|
|
333
|
+
theme?: 'light' | 'dark';
|
|
334
|
+
uiFont?: string;
|
|
335
|
+
accent?: string;
|
|
336
|
+
toolbar?: ToolbarOption;
|
|
337
|
+
footer?: FooterOption;
|
|
338
|
+
storageProvider?: StorageProvider | null;
|
|
339
|
+
storageLimits?: StorageLimits | null;
|
|
340
|
+
aiProvider?: AiProvider | null;
|
|
341
|
+
iconProvider?: IconProvider | null;
|
|
342
|
+
messages?: MessageTable | null;
|
|
343
|
+
/** Sets the container's height; a number is treated as px. Omit it and your CSS decides. */
|
|
344
|
+
height?: string | number;
|
|
345
|
+
/** Empty the container first (default: append). */
|
|
346
|
+
replace?: boolean;
|
|
347
|
+
onChange?(doc: EmailDocument): void;
|
|
348
|
+
onExport?(html: string): void;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface EditorHandle {
|
|
352
|
+
/** The underlying custom element — everything the wrapper does not forward. */
|
|
353
|
+
element: MailCraftEditor;
|
|
354
|
+
/** Removes the editor and detaches the listeners this call attached. */
|
|
355
|
+
destroy(): void;
|
|
356
|
+
exportHtml(): string;
|
|
357
|
+
importHtml(html: string): number;
|
|
358
|
+
loadTemplate(tpl: Template): void;
|
|
359
|
+
undo(): void;
|
|
360
|
+
redo(): void;
|
|
361
|
+
screenshotPng(): Promise<Blob>;
|
|
362
|
+
previewScreenshot(): void;
|
|
363
|
+
downloadScreenshot(blob?: Blob): Promise<void>;
|
|
364
|
+
copyScreenshot(blob?: Blob): Promise<void>;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Creates a `<mailcraft-editor>` inside `target` (a CSS selector or element). Throws when nothing matches. */
|
|
368
|
+
export function createEditor(target: string | Element, options?: CreateEditorOptions): EditorHandle;
|
|
369
|
+
|
|
370
|
+
/** True once the custom element is registered — importing the package is what registers it. */
|
|
371
|
+
export function isReady(): boolean;
|
|
372
|
+
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// Internals, exported for building your own UI on top.
|
|
375
|
+
// Typed loosely on purpose: their shapes are free to change between versions.
|
|
376
|
+
|
|
377
|
+
export class EditorCore {
|
|
378
|
+
constructor(options?: {
|
|
379
|
+
variables?: string | string[];
|
|
380
|
+
aiProvider?: AiProvider | null;
|
|
381
|
+
iconProvider?: IconProvider | null;
|
|
382
|
+
messages?: MessageTable | null;
|
|
383
|
+
storageProvider?: StorageProvider | null;
|
|
384
|
+
storageLimits?: StorageLimits | null;
|
|
385
|
+
});
|
|
386
|
+
state: Record<string, any>;
|
|
387
|
+
[key: string]: any;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Renders the document tree: `live = true` is the editable canvas, `false` the static preview/export tree. */
|
|
391
|
+
export function renderDoc(core: EditorCore, live?: boolean): HTMLElement;
|
|
392
|
+
|
|
393
|
+
export interface BlockDef {
|
|
394
|
+
type: string;
|
|
395
|
+
code: string;
|
|
396
|
+
label: string;
|
|
397
|
+
hint: string;
|
|
398
|
+
make(): Record<string, any>;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** The 20 content-block definitions. */
|
|
402
|
+
export const BLOCKS: BlockDef[];
|
|
403
|
+
|
|
404
|
+
/** Compound presets — `build()` returns full row(s) built from ordinary blocks. */
|
|
405
|
+
export const GROUPS: Record<string, { label: string; icon: string; build(): Array<Record<string, any>> }>;
|
|
406
|
+
|
|
407
|
+
/** The section layouts (column span presets). */
|
|
408
|
+
export const LAYOUTS: Array<{ spans: number[]; label: string }>;
|
|
409
|
+
|
|
410
|
+
/** The palette order: `t` names a block type, `g` a group. */
|
|
411
|
+
export const PALETTE: Array<{ t?: string; g?: string }>;
|
|
412
|
+
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
|
|
415
|
+
declare global {
|
|
416
|
+
interface HTMLElementTagNameMap {
|
|
417
|
+
'mailcraft-editor': MailCraftEditor;
|
|
418
|
+
}
|
|
419
|
+
}
|