@react-email/editor 0.0.0-experimental.15 → 0.0.0-experimental.17
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/dist/index.cjs +1222 -419
- package/dist/index.d.cts +334 -160
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +335 -161
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1202 -424
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -5
package/dist/index.cjs
CHANGED
|
@@ -25,11 +25,22 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
|
|
27
27
|
//#endregion
|
|
28
|
-
let _tiptap_core = require("@tiptap/core");
|
|
29
|
-
let _tiptap_starter_kit = require("@tiptap/starter-kit");
|
|
30
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
31
28
|
let _react_email_components = require("@react-email/components");
|
|
32
29
|
_react_email_components = __toESM(_react_email_components);
|
|
30
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
31
|
+
let _tiptap_core = require("@tiptap/core");
|
|
32
|
+
let _tiptap_extensions = require("@tiptap/extensions");
|
|
33
|
+
let _tiptap_react = require("@tiptap/react");
|
|
34
|
+
let react = require("react");
|
|
35
|
+
react = __toESM(react);
|
|
36
|
+
let _tiptap_starter_kit = require("@tiptap/starter-kit");
|
|
37
|
+
_tiptap_starter_kit = __toESM(_tiptap_starter_kit);
|
|
38
|
+
let _tiptap_extension_blockquote = require("@tiptap/extension-blockquote");
|
|
39
|
+
_tiptap_extension_blockquote = __toESM(_tiptap_extension_blockquote);
|
|
40
|
+
let _tiptap_extension_bullet_list = require("@tiptap/extension-bullet-list");
|
|
41
|
+
_tiptap_extension_bullet_list = __toESM(_tiptap_extension_bullet_list);
|
|
42
|
+
let _tiptap_extension_code = require("@tiptap/extension-code");
|
|
43
|
+
_tiptap_extension_code = __toESM(_tiptap_extension_code);
|
|
33
44
|
let _tiptap_extension_code_block = require("@tiptap/extension-code-block");
|
|
34
45
|
_tiptap_extension_code_block = __toESM(_tiptap_extension_code_block);
|
|
35
46
|
let _tiptap_pm_state = require("@tiptap/pm/state");
|
|
@@ -37,12 +48,27 @@ let _tiptap_pm_view = require("@tiptap/pm/view");
|
|
|
37
48
|
let hast_util_from_html = require("hast-util-from-html");
|
|
38
49
|
let prismjs = require("prismjs");
|
|
39
50
|
prismjs = __toESM(prismjs);
|
|
51
|
+
let _tiptap_extension_horizontal_rule = require("@tiptap/extension-horizontal-rule");
|
|
52
|
+
_tiptap_extension_horizontal_rule = __toESM(_tiptap_extension_horizontal_rule);
|
|
53
|
+
let _tiptap_extension_hard_break = require("@tiptap/extension-hard-break");
|
|
54
|
+
_tiptap_extension_hard_break = __toESM(_tiptap_extension_hard_break);
|
|
55
|
+
let _tiptap_extension_heading = require("@tiptap/extension-heading");
|
|
56
|
+
let _tiptap_extension_italic = require("@tiptap/extension-italic");
|
|
57
|
+
_tiptap_extension_italic = __toESM(_tiptap_extension_italic);
|
|
58
|
+
let _tiptap_extension_link = require("@tiptap/extension-link");
|
|
59
|
+
_tiptap_extension_link = __toESM(_tiptap_extension_link);
|
|
60
|
+
let _tiptap_extension_list_item = require("@tiptap/extension-list-item");
|
|
61
|
+
_tiptap_extension_list_item = __toESM(_tiptap_extension_list_item);
|
|
62
|
+
let _tiptap_extension_ordered_list = require("@tiptap/extension-ordered-list");
|
|
63
|
+
_tiptap_extension_ordered_list = __toESM(_tiptap_extension_ordered_list);
|
|
64
|
+
let _tiptap_extension_paragraph = require("@tiptap/extension-paragraph");
|
|
65
|
+
_tiptap_extension_paragraph = __toESM(_tiptap_extension_paragraph);
|
|
40
66
|
let _tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
|
|
41
67
|
_tiptap_extension_placeholder = __toESM(_tiptap_extension_placeholder);
|
|
42
|
-
let
|
|
68
|
+
let _tiptap_extension_strike = require("@tiptap/extension-strike");
|
|
69
|
+
_tiptap_extension_strike = __toESM(_tiptap_extension_strike);
|
|
70
|
+
let _tiptap_html = require("@tiptap/html");
|
|
43
71
|
let lucide_react = require("lucide-react");
|
|
44
|
-
let react = require("react");
|
|
45
|
-
react = __toESM(react);
|
|
46
72
|
let _radix_ui_react_popover = require("@radix-ui/react-popover");
|
|
47
73
|
_radix_ui_react_popover = __toESM(_radix_ui_react_popover);
|
|
48
74
|
let _tiptap_react_menus = require("@tiptap/react/menus");
|
|
@@ -51,37 +77,6 @@ _tiptap_suggestion = __toESM(_tiptap_suggestion);
|
|
|
51
77
|
let tippy_js = require("tippy.js");
|
|
52
78
|
tippy_js = __toESM(tippy_js);
|
|
53
79
|
|
|
54
|
-
//#region src/core/email-node.ts
|
|
55
|
-
var EmailNode = class EmailNode extends _tiptap_core.Node {
|
|
56
|
-
constructor(config) {
|
|
57
|
-
super(config);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Create a new Node instance
|
|
61
|
-
* @param config - Node configuration object or a function that returns a configuration object
|
|
62
|
-
*/
|
|
63
|
-
static create(config) {
|
|
64
|
-
return new EmailNode(typeof config === "function" ? config() : config);
|
|
65
|
-
}
|
|
66
|
-
static from(node, renderToReactEmail) {
|
|
67
|
-
const customNode = EmailNode.create({});
|
|
68
|
-
Object.assign(customNode, { ...node });
|
|
69
|
-
customNode.config = {
|
|
70
|
-
...node.config,
|
|
71
|
-
renderToReactEmail
|
|
72
|
-
};
|
|
73
|
-
return customNode;
|
|
74
|
-
}
|
|
75
|
-
configure(options) {
|
|
76
|
-
return super.configure(options);
|
|
77
|
-
}
|
|
78
|
-
extend(extendedConfig) {
|
|
79
|
-
const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
|
|
80
|
-
return super.extend(resolvedConfig);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
//#endregion
|
|
85
80
|
//#region src/core/event-bus.ts
|
|
86
81
|
const EVENT_PREFIX = "@react-email/editor:";
|
|
87
82
|
var EditorEventBus = class {
|
|
@@ -123,139 +118,6 @@ var EditorEventBus = class {
|
|
|
123
118
|
};
|
|
124
119
|
const editorEventBus = new EditorEventBus();
|
|
125
120
|
|
|
126
|
-
//#endregion
|
|
127
|
-
//#region src/extensions/alignment-attribute.tsx
|
|
128
|
-
const AlignmentAttribute = _tiptap_core.Extension.create({
|
|
129
|
-
name: "alignmentAttribute",
|
|
130
|
-
addOptions() {
|
|
131
|
-
return {
|
|
132
|
-
types: [],
|
|
133
|
-
alignments: [
|
|
134
|
-
"left",
|
|
135
|
-
"center",
|
|
136
|
-
"right",
|
|
137
|
-
"justify"
|
|
138
|
-
]
|
|
139
|
-
};
|
|
140
|
-
},
|
|
141
|
-
addGlobalAttributes() {
|
|
142
|
-
return [{
|
|
143
|
-
types: this.options.types,
|
|
144
|
-
attributes: { alignment: {
|
|
145
|
-
parseHTML: (element) => {
|
|
146
|
-
const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
|
|
147
|
-
if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
|
|
148
|
-
return null;
|
|
149
|
-
},
|
|
150
|
-
renderHTML: (attributes) => {
|
|
151
|
-
if (attributes.alignment === "left") return {};
|
|
152
|
-
return { alignment: attributes.alignment };
|
|
153
|
-
}
|
|
154
|
-
} }
|
|
155
|
-
}];
|
|
156
|
-
},
|
|
157
|
-
addCommands() {
|
|
158
|
-
return { setAlignment: (alignment) => ({ commands }) => {
|
|
159
|
-
if (!this.options.alignments.includes(alignment)) return false;
|
|
160
|
-
return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
|
|
161
|
-
} };
|
|
162
|
-
},
|
|
163
|
-
addKeyboardShortcuts() {
|
|
164
|
-
return {
|
|
165
|
-
Enter: () => {
|
|
166
|
-
const { from } = this.editor.state.selection;
|
|
167
|
-
const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
|
|
168
|
-
if (currentAlignment) requestAnimationFrame(() => {
|
|
169
|
-
this.editor.commands.setAlignment(currentAlignment);
|
|
170
|
-
});
|
|
171
|
-
return false;
|
|
172
|
-
},
|
|
173
|
-
"Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
|
|
174
|
-
"Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
|
|
175
|
-
"Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
|
|
176
|
-
"Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
//#endregion
|
|
182
|
-
//#region src/utils/attribute-helpers.ts
|
|
183
|
-
/**
|
|
184
|
-
* Creates TipTap attribute definitions for a list of HTML attributes.
|
|
185
|
-
* Each attribute will have the same pattern:
|
|
186
|
-
* - default: null
|
|
187
|
-
* - parseHTML: extracts the attribute from the element
|
|
188
|
-
* - renderHTML: conditionally renders the attribute if it has a value
|
|
189
|
-
*
|
|
190
|
-
* @param attributeNames - Array of HTML attribute names to create definitions for
|
|
191
|
-
* @returns Object with TipTap attribute definitions
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* const attrs = createStandardAttributes(['class', 'id', 'title']);
|
|
195
|
-
* // Returns:
|
|
196
|
-
* // {
|
|
197
|
-
* // class: {
|
|
198
|
-
* // default: null,
|
|
199
|
-
* // parseHTML: (element) => element.getAttribute('class'),
|
|
200
|
-
* // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
|
|
201
|
-
* // },
|
|
202
|
-
* // ...
|
|
203
|
-
* // }
|
|
204
|
-
*/
|
|
205
|
-
function createStandardAttributes(attributeNames) {
|
|
206
|
-
return Object.fromEntries(attributeNames.map((attr) => [attr, {
|
|
207
|
-
default: null,
|
|
208
|
-
parseHTML: (element) => element.getAttribute(attr),
|
|
209
|
-
renderHTML: (attributes) => {
|
|
210
|
-
if (!attributes[attr]) return {};
|
|
211
|
-
return { [attr]: attributes[attr] };
|
|
212
|
-
}
|
|
213
|
-
}]));
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Common HTML attributes used across multiple extensions.
|
|
217
|
-
* These preserve attributes during HTML import and editing for better
|
|
218
|
-
* fidelity when importing existing email templates.
|
|
219
|
-
*/
|
|
220
|
-
const COMMON_HTML_ATTRIBUTES = [
|
|
221
|
-
"id",
|
|
222
|
-
"class",
|
|
223
|
-
"title",
|
|
224
|
-
"lang",
|
|
225
|
-
"dir",
|
|
226
|
-
"data-id"
|
|
227
|
-
];
|
|
228
|
-
/**
|
|
229
|
-
* Layout-specific HTML attributes used for positioning and sizing.
|
|
230
|
-
*/
|
|
231
|
-
const LAYOUT_ATTRIBUTES = [
|
|
232
|
-
"align",
|
|
233
|
-
"width",
|
|
234
|
-
"height"
|
|
235
|
-
];
|
|
236
|
-
/**
|
|
237
|
-
* Table-specific HTML attributes used for table layout and styling.
|
|
238
|
-
*/
|
|
239
|
-
const TABLE_ATTRIBUTES = [
|
|
240
|
-
"border",
|
|
241
|
-
"cellpadding",
|
|
242
|
-
"cellspacing"
|
|
243
|
-
];
|
|
244
|
-
/**
|
|
245
|
-
* Table cell-specific HTML attributes.
|
|
246
|
-
*/
|
|
247
|
-
const TABLE_CELL_ATTRIBUTES = [
|
|
248
|
-
"valign",
|
|
249
|
-
"bgcolor",
|
|
250
|
-
"colspan",
|
|
251
|
-
"rowspan"
|
|
252
|
-
];
|
|
253
|
-
/**
|
|
254
|
-
* Table header cell-specific HTML attributes.
|
|
255
|
-
* These are additional attributes that only apply to <th> elements.
|
|
256
|
-
*/
|
|
257
|
-
const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
|
|
258
|
-
|
|
259
121
|
//#endregion
|
|
260
122
|
//#region src/utils/styles.ts
|
|
261
123
|
const WHITE_SPACE_REGEX = /\s+/;
|
|
@@ -412,18 +274,354 @@ function convertBorderValue(value) {
|
|
|
412
274
|
* shorthand properties (margin, padding) to longhand before merging.
|
|
413
275
|
* This prevents shorthand properties from overriding specific longhand properties.
|
|
414
276
|
*
|
|
415
|
-
* @param resetStyles - Base reset styles that may contain shorthand properties
|
|
416
|
-
* @param inlineStyles - Inline styles that should override reset styles
|
|
417
|
-
* @returns Merged styles with inline styles taking precedence
|
|
277
|
+
* @param resetStyles - Base reset styles that may contain shorthand properties
|
|
278
|
+
* @param inlineStyles - Inline styles that should override reset styles
|
|
279
|
+
* @returns Merged styles with inline styles taking precedence
|
|
280
|
+
*/
|
|
281
|
+
function resolveConflictingStyles(resetStyles, inlineStyles) {
|
|
282
|
+
const expandedResetStyles = expandShorthandProperties(resetStyles);
|
|
283
|
+
const expandedInlineStyles = expandShorthandProperties(inlineStyles);
|
|
284
|
+
return {
|
|
285
|
+
...expandedResetStyles,
|
|
286
|
+
...expandedInlineStyles
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/core/serializer/default-base-template.tsx
|
|
292
|
+
function DefaultBaseTemplate({ children, previewText }) {
|
|
293
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_email_components.Html, { children: [
|
|
294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_email_components.Head, { children: [
|
|
295
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
296
|
+
content: "width=device-width",
|
|
297
|
+
name: "viewport"
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
300
|
+
content: "IE=edge",
|
|
301
|
+
httpEquiv: "X-UA-Compatible"
|
|
302
|
+
}),
|
|
303
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", { name: "x-apple-disable-message-reformatting" }),
|
|
304
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
305
|
+
content: "telephone=no,address=no,email=no,date=no,url=no",
|
|
306
|
+
name: "format-detection"
|
|
307
|
+
})
|
|
308
|
+
] }),
|
|
309
|
+
previewText && previewText !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Preview, { children: previewText }),
|
|
310
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Body, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
|
|
311
|
+
width: "100%",
|
|
312
|
+
align: "center",
|
|
313
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
|
|
314
|
+
style: { width: "100%" },
|
|
315
|
+
children
|
|
316
|
+
})
|
|
317
|
+
}) })
|
|
318
|
+
] });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/core/serializer/email-mark.ts
|
|
323
|
+
var EmailMark = class EmailMark extends _tiptap_core.Mark {
|
|
324
|
+
constructor(config) {
|
|
325
|
+
super(config);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Create a new Mark instance
|
|
329
|
+
* @param config - Mark configuration object or a function that returns a configuration object
|
|
330
|
+
*/
|
|
331
|
+
static create(config) {
|
|
332
|
+
return new EmailMark(typeof config === "function" ? config() : config);
|
|
333
|
+
}
|
|
334
|
+
static from(mark, renderToReactEmail) {
|
|
335
|
+
const customMark = EmailMark.create({});
|
|
336
|
+
Object.assign(customMark, { ...mark });
|
|
337
|
+
customMark.config = {
|
|
338
|
+
...mark.config,
|
|
339
|
+
renderToReactEmail
|
|
340
|
+
};
|
|
341
|
+
return customMark;
|
|
342
|
+
}
|
|
343
|
+
configure(options) {
|
|
344
|
+
return super.configure(options);
|
|
345
|
+
}
|
|
346
|
+
extend(extendedConfig) {
|
|
347
|
+
const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
|
|
348
|
+
return super.extend(resolvedConfig);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region src/core/serializer/email-node.ts
|
|
354
|
+
var EmailNode = class EmailNode extends _tiptap_core.Node {
|
|
355
|
+
constructor(config) {
|
|
356
|
+
super(config);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Create a new Node instance
|
|
360
|
+
* @param config - Node configuration object or a function that returns a configuration object
|
|
361
|
+
*/
|
|
362
|
+
static create(config) {
|
|
363
|
+
return new EmailNode(typeof config === "function" ? config() : config);
|
|
364
|
+
}
|
|
365
|
+
static from(node, renderToReactEmail) {
|
|
366
|
+
const customNode = EmailNode.create({});
|
|
367
|
+
Object.assign(customNode, { ...node });
|
|
368
|
+
customNode.config = {
|
|
369
|
+
...node.config,
|
|
370
|
+
renderToReactEmail
|
|
371
|
+
};
|
|
372
|
+
return customNode;
|
|
373
|
+
}
|
|
374
|
+
configure(options) {
|
|
375
|
+
return super.configure(options);
|
|
376
|
+
}
|
|
377
|
+
extend(extendedConfig) {
|
|
378
|
+
const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
|
|
379
|
+
return super.extend(resolvedConfig);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/core/serializer/compose-react-email.tsx
|
|
385
|
+
const MARK_ORDER = {
|
|
386
|
+
preservedStyle: 0,
|
|
387
|
+
italic: 1,
|
|
388
|
+
strike: 2,
|
|
389
|
+
underline: 3,
|
|
390
|
+
link: 4,
|
|
391
|
+
bold: 5,
|
|
392
|
+
code: 6
|
|
393
|
+
};
|
|
394
|
+
const NODES_WITH_INCREMENTED_CHILD_DEPTH = new Set(["bulletList", "orderedList"]);
|
|
395
|
+
function getOrderedMarks(marks) {
|
|
396
|
+
if (!marks) return [];
|
|
397
|
+
return [...marks].sort((a, b) => (MARK_ORDER[a.type] ?? Number.MAX_SAFE_INTEGER) - (MARK_ORDER[b.type] ?? Number.MAX_SAFE_INTEGER));
|
|
398
|
+
}
|
|
399
|
+
const composeReactEmail = async ({ editor, preview }) => {
|
|
400
|
+
const data = editor.getJSON();
|
|
401
|
+
const extensions = editor.extensionManager.extensions;
|
|
402
|
+
const serializerPlugin = extensions.map((ext) => ext.options?.serializerPlugin).filter((p) => Boolean(p)).at(-1);
|
|
403
|
+
const emailNodeComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailNode).map((extension) => [extension.name, extension.config.renderToReactEmail]));
|
|
404
|
+
const emailMarkComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailMark).map((extension) => [extension.name, extension.config.renderToReactEmail]));
|
|
405
|
+
function renderMark(mark, node, children, depth) {
|
|
406
|
+
const markStyle = serializerPlugin?.getNodeStyles({
|
|
407
|
+
type: mark.type,
|
|
408
|
+
attrs: mark.attrs ?? {}
|
|
409
|
+
}, depth, editor) ?? {};
|
|
410
|
+
const markRenderer = emailMarkComponentRegistry[mark.type];
|
|
411
|
+
if (markRenderer) return markRenderer({
|
|
412
|
+
mark,
|
|
413
|
+
node,
|
|
414
|
+
style: markStyle,
|
|
415
|
+
children
|
|
416
|
+
});
|
|
417
|
+
return children;
|
|
418
|
+
}
|
|
419
|
+
function parseContent(content, depth = 0) {
|
|
420
|
+
if (!content) return;
|
|
421
|
+
return content.map((node, index) => {
|
|
422
|
+
const style = serializerPlugin?.getNodeStyles(node, depth, editor) ?? {};
|
|
423
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
424
|
+
if (node.type && emailNodeComponentRegistry[node.type]) {
|
|
425
|
+
const Component = emailNodeComponentRegistry[node.type];
|
|
426
|
+
const childDepth = NODES_WITH_INCREMENTED_CHILD_DEPTH.has(node.type) ? depth + 1 : depth;
|
|
427
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
|
|
428
|
+
node: node.type === "table" && inlineStyles.width && !node.attrs?.width ? {
|
|
429
|
+
...node,
|
|
430
|
+
attrs: {
|
|
431
|
+
...node.attrs,
|
|
432
|
+
width: inlineStyles.width
|
|
433
|
+
}
|
|
434
|
+
} : node,
|
|
435
|
+
style,
|
|
436
|
+
children: parseContent(node.content, childDepth)
|
|
437
|
+
}, index);
|
|
438
|
+
}
|
|
439
|
+
switch (node.type) {
|
|
440
|
+
case "text": {
|
|
441
|
+
let wrappedText = node.text;
|
|
442
|
+
getOrderedMarks(node.marks).forEach((mark) => {
|
|
443
|
+
wrappedText = renderMark(mark, node, wrappedText, depth);
|
|
444
|
+
});
|
|
445
|
+
const textAttributes = node.marks?.find((mark) => mark.type === "textStyle")?.attrs;
|
|
446
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
447
|
+
style: {
|
|
448
|
+
...textAttributes,
|
|
449
|
+
...style
|
|
450
|
+
},
|
|
451
|
+
children: wrappedText
|
|
452
|
+
}, index);
|
|
453
|
+
}
|
|
454
|
+
default: return null;
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
const unformattedHtml = await (0, _react_email_components.render)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(serializerPlugin?.BaseTemplate ?? DefaultBaseTemplate, {
|
|
459
|
+
previewText: preview,
|
|
460
|
+
editor,
|
|
461
|
+
children: parseContent(data.content)
|
|
462
|
+
}));
|
|
463
|
+
const [prettyHtml, text] = await Promise.all([(0, _react_email_components.pretty)(unformattedHtml), (0, _react_email_components.toPlainText)(unformattedHtml)]);
|
|
464
|
+
return {
|
|
465
|
+
html: prettyHtml,
|
|
466
|
+
text
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/extensions/alignment-attribute.tsx
|
|
472
|
+
const AlignmentAttribute = _tiptap_core.Extension.create({
|
|
473
|
+
name: "alignmentAttribute",
|
|
474
|
+
addOptions() {
|
|
475
|
+
return {
|
|
476
|
+
types: [],
|
|
477
|
+
alignments: [
|
|
478
|
+
"left",
|
|
479
|
+
"center",
|
|
480
|
+
"right",
|
|
481
|
+
"justify"
|
|
482
|
+
]
|
|
483
|
+
};
|
|
484
|
+
},
|
|
485
|
+
addGlobalAttributes() {
|
|
486
|
+
return [{
|
|
487
|
+
types: this.options.types,
|
|
488
|
+
attributes: { alignment: {
|
|
489
|
+
parseHTML: (element) => {
|
|
490
|
+
const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
|
|
491
|
+
if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
|
|
492
|
+
return null;
|
|
493
|
+
},
|
|
494
|
+
renderHTML: (attributes) => {
|
|
495
|
+
if (attributes.alignment === "left") return {};
|
|
496
|
+
return { alignment: attributes.alignment };
|
|
497
|
+
}
|
|
498
|
+
} }
|
|
499
|
+
}];
|
|
500
|
+
},
|
|
501
|
+
addCommands() {
|
|
502
|
+
return { setAlignment: (alignment) => ({ commands }) => {
|
|
503
|
+
if (!this.options.alignments.includes(alignment)) return false;
|
|
504
|
+
return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
|
|
505
|
+
} };
|
|
506
|
+
},
|
|
507
|
+
addKeyboardShortcuts() {
|
|
508
|
+
return {
|
|
509
|
+
Enter: () => {
|
|
510
|
+
const { from } = this.editor.state.selection;
|
|
511
|
+
const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
|
|
512
|
+
if (currentAlignment) requestAnimationFrame(() => {
|
|
513
|
+
this.editor.commands.setAlignment(currentAlignment);
|
|
514
|
+
});
|
|
515
|
+
return false;
|
|
516
|
+
},
|
|
517
|
+
"Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
|
|
518
|
+
"Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
|
|
519
|
+
"Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
|
|
520
|
+
"Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
//#endregion
|
|
526
|
+
//#region src/utils/get-text-alignment.ts
|
|
527
|
+
function getTextAlignment(alignment) {
|
|
528
|
+
switch (alignment) {
|
|
529
|
+
case "left": return { textAlign: "left" };
|
|
530
|
+
case "center": return { textAlign: "center" };
|
|
531
|
+
case "right": return { textAlign: "right" };
|
|
532
|
+
default: return {};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/extensions/blockquote.tsx
|
|
538
|
+
const Blockquote = EmailNode.from(_tiptap_extension_blockquote.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("blockquote", {
|
|
539
|
+
className: node.attrs?.class || void 0,
|
|
540
|
+
style: {
|
|
541
|
+
...style,
|
|
542
|
+
...inlineCssToJs(node.attrs?.style),
|
|
543
|
+
...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
|
|
544
|
+
},
|
|
545
|
+
children
|
|
546
|
+
}));
|
|
547
|
+
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region src/utils/attribute-helpers.ts
|
|
550
|
+
/**
|
|
551
|
+
* Creates TipTap attribute definitions for a list of HTML attributes.
|
|
552
|
+
* Each attribute will have the same pattern:
|
|
553
|
+
* - default: null
|
|
554
|
+
* - parseHTML: extracts the attribute from the element
|
|
555
|
+
* - renderHTML: conditionally renders the attribute if it has a value
|
|
556
|
+
*
|
|
557
|
+
* @param attributeNames - Array of HTML attribute names to create definitions for
|
|
558
|
+
* @returns Object with TipTap attribute definitions
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* const attrs = createStandardAttributes(['class', 'id', 'title']);
|
|
562
|
+
* // Returns:
|
|
563
|
+
* // {
|
|
564
|
+
* // class: {
|
|
565
|
+
* // default: null,
|
|
566
|
+
* // parseHTML: (element) => element.getAttribute('class'),
|
|
567
|
+
* // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
|
|
568
|
+
* // },
|
|
569
|
+
* // ...
|
|
570
|
+
* // }
|
|
418
571
|
*/
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
572
|
+
function createStandardAttributes(attributeNames) {
|
|
573
|
+
return Object.fromEntries(attributeNames.map((attr) => [attr, {
|
|
574
|
+
default: null,
|
|
575
|
+
parseHTML: (element) => element.getAttribute(attr),
|
|
576
|
+
renderHTML: (attributes) => {
|
|
577
|
+
if (!attributes[attr]) return {};
|
|
578
|
+
return { [attr]: attributes[attr] };
|
|
579
|
+
}
|
|
580
|
+
}]));
|
|
426
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Common HTML attributes used across multiple extensions.
|
|
584
|
+
* These preserve attributes during HTML import and editing for better
|
|
585
|
+
* fidelity when importing existing email templates.
|
|
586
|
+
*/
|
|
587
|
+
const COMMON_HTML_ATTRIBUTES = [
|
|
588
|
+
"id",
|
|
589
|
+
"class",
|
|
590
|
+
"title",
|
|
591
|
+
"lang",
|
|
592
|
+
"dir",
|
|
593
|
+
"data-id"
|
|
594
|
+
];
|
|
595
|
+
/**
|
|
596
|
+
* Layout-specific HTML attributes used for positioning and sizing.
|
|
597
|
+
*/
|
|
598
|
+
const LAYOUT_ATTRIBUTES = [
|
|
599
|
+
"align",
|
|
600
|
+
"width",
|
|
601
|
+
"height"
|
|
602
|
+
];
|
|
603
|
+
/**
|
|
604
|
+
* Table-specific HTML attributes used for table layout and styling.
|
|
605
|
+
*/
|
|
606
|
+
const TABLE_ATTRIBUTES = [
|
|
607
|
+
"border",
|
|
608
|
+
"cellpadding",
|
|
609
|
+
"cellspacing"
|
|
610
|
+
];
|
|
611
|
+
/**
|
|
612
|
+
* Table cell-specific HTML attributes.
|
|
613
|
+
*/
|
|
614
|
+
const TABLE_CELL_ATTRIBUTES = [
|
|
615
|
+
"valign",
|
|
616
|
+
"bgcolor",
|
|
617
|
+
"colspan",
|
|
618
|
+
"rowspan"
|
|
619
|
+
];
|
|
620
|
+
/**
|
|
621
|
+
* Table header cell-specific HTML attributes.
|
|
622
|
+
* These are additional attributes that only apply to <th> elements.
|
|
623
|
+
*/
|
|
624
|
+
const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
|
|
427
625
|
|
|
428
626
|
//#endregion
|
|
429
627
|
//#region src/extensions/body.tsx
|
|
@@ -471,7 +669,7 @@ const Body = EmailNode.create({
|
|
|
471
669
|
});
|
|
472
670
|
|
|
473
671
|
//#endregion
|
|
474
|
-
//#region src/extensions/bold.
|
|
672
|
+
//#region src/extensions/bold.tsx
|
|
475
673
|
/**
|
|
476
674
|
* Matches bold text via `**` as input.
|
|
477
675
|
*/
|
|
@@ -492,7 +690,7 @@ const underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
|
|
|
492
690
|
* This extension allows you to mark text as bold.
|
|
493
691
|
* @see https://tiptap.dev/api/marks/bold
|
|
494
692
|
*/
|
|
495
|
-
const Bold =
|
|
693
|
+
const Bold = EmailMark.create({
|
|
496
694
|
name: "bold",
|
|
497
695
|
addOptions() {
|
|
498
696
|
return { HTMLAttributes: {} };
|
|
@@ -517,6 +715,12 @@ const Bold = _tiptap_core.Mark.create({
|
|
|
517
715
|
0
|
|
518
716
|
];
|
|
519
717
|
},
|
|
718
|
+
renderToReactEmail({ children, style }) {
|
|
719
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
720
|
+
style,
|
|
721
|
+
children
|
|
722
|
+
});
|
|
723
|
+
},
|
|
520
724
|
addCommands() {
|
|
521
725
|
return {
|
|
522
726
|
setBold: () => ({ commands }) => {
|
|
@@ -556,6 +760,17 @@ const Bold = _tiptap_core.Mark.create({
|
|
|
556
760
|
}
|
|
557
761
|
});
|
|
558
762
|
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/extensions/bullet-list.tsx
|
|
765
|
+
const BulletList = EmailNode.from(_tiptap_extension_bullet_list.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
766
|
+
className: node.attrs?.class || void 0,
|
|
767
|
+
style: {
|
|
768
|
+
...style,
|
|
769
|
+
...inlineCssToJs(node.attrs?.style)
|
|
770
|
+
},
|
|
771
|
+
children
|
|
772
|
+
}));
|
|
773
|
+
|
|
559
774
|
//#endregion
|
|
560
775
|
//#region src/extensions/button.tsx
|
|
561
776
|
const Button = EmailNode.create({
|
|
@@ -677,6 +892,16 @@ const ClassAttribute = _tiptap_core.Extension.create({
|
|
|
677
892
|
}
|
|
678
893
|
});
|
|
679
894
|
|
|
895
|
+
//#endregion
|
|
896
|
+
//#region src/extensions/code.tsx
|
|
897
|
+
const Code = EmailMark.from(_tiptap_extension_code.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
898
|
+
style: {
|
|
899
|
+
...style,
|
|
900
|
+
...inlineCssToJs(node.attrs?.style)
|
|
901
|
+
},
|
|
902
|
+
children
|
|
903
|
+
}));
|
|
904
|
+
|
|
680
905
|
//#endregion
|
|
681
906
|
//#region src/utils/prism-utils.ts
|
|
682
907
|
const publicURL = "/styles/prism";
|
|
@@ -911,16 +1136,314 @@ const CodeBlockPrism = EmailNode.from(_tiptap_extension_code_block.default.exten
|
|
|
911
1136
|
});
|
|
912
1137
|
|
|
913
1138
|
//#endregion
|
|
914
|
-
//#region src/extensions/div.tsx
|
|
915
|
-
const Div = EmailNode.create({
|
|
916
|
-
name: "div",
|
|
917
|
-
group: "block",
|
|
918
|
-
content: "block+",
|
|
919
|
-
defining: true,
|
|
920
|
-
isolating: true,
|
|
1139
|
+
//#region src/extensions/div.tsx
|
|
1140
|
+
const Div = EmailNode.create({
|
|
1141
|
+
name: "div",
|
|
1142
|
+
group: "block",
|
|
1143
|
+
content: "block+",
|
|
1144
|
+
defining: true,
|
|
1145
|
+
isolating: true,
|
|
1146
|
+
parseHTML() {
|
|
1147
|
+
return [{
|
|
1148
|
+
tag: "div:not([data-type])",
|
|
1149
|
+
getAttrs: (node) => {
|
|
1150
|
+
if (typeof node === "string") return false;
|
|
1151
|
+
const element = node;
|
|
1152
|
+
const attrs = {};
|
|
1153
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
1154
|
+
attrs[attr.name] = attr.value;
|
|
1155
|
+
});
|
|
1156
|
+
return attrs;
|
|
1157
|
+
}
|
|
1158
|
+
}];
|
|
1159
|
+
},
|
|
1160
|
+
renderHTML({ HTMLAttributes }) {
|
|
1161
|
+
return [
|
|
1162
|
+
"div",
|
|
1163
|
+
(0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
|
|
1164
|
+
0
|
|
1165
|
+
];
|
|
1166
|
+
},
|
|
1167
|
+
addAttributes() {
|
|
1168
|
+
return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
|
|
1169
|
+
},
|
|
1170
|
+
renderToReactEmail({ children, node, style }) {
|
|
1171
|
+
const inlineStyles = inlineCssToJs(node.attrs?.style);
|
|
1172
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1173
|
+
className: node.attrs?.class || void 0,
|
|
1174
|
+
style: {
|
|
1175
|
+
...style,
|
|
1176
|
+
...inlineStyles
|
|
1177
|
+
},
|
|
1178
|
+
children
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
//#endregion
|
|
1184
|
+
//#region src/extensions/divider.tsx
|
|
1185
|
+
const Divider = EmailNode.from(_tiptap_extension_horizontal_rule.default.extend({
|
|
1186
|
+
addAttributes() {
|
|
1187
|
+
return { class: { default: "divider" } };
|
|
1188
|
+
},
|
|
1189
|
+
addInputRules() {
|
|
1190
|
+
return [new _tiptap_core.InputRule({
|
|
1191
|
+
find: /^(?:---|—-|___\s|\*\*\*\s)$/,
|
|
1192
|
+
handler: ({ state, range }) => {
|
|
1193
|
+
const attributes = {};
|
|
1194
|
+
const { tr } = state;
|
|
1195
|
+
const start = range.from;
|
|
1196
|
+
const end = range.to;
|
|
1197
|
+
tr.insert(start - 1, this.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
|
|
1198
|
+
}
|
|
1199
|
+
})];
|
|
1200
|
+
},
|
|
1201
|
+
addNodeView() {
|
|
1202
|
+
return (0, _tiptap_react.ReactNodeViewRenderer)((props) => {
|
|
1203
|
+
const node = props.node;
|
|
1204
|
+
const { class: className, ...rest } = node.attrs;
|
|
1205
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react.NodeViewWrapper, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Hr, {
|
|
1206
|
+
...rest,
|
|
1207
|
+
className: "node-hr",
|
|
1208
|
+
style: inlineCssToJs(node.attrs.style)
|
|
1209
|
+
}) });
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
}), ({ node, style }) => {
|
|
1213
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Hr, {
|
|
1214
|
+
className: node.attrs?.class || void 0,
|
|
1215
|
+
style: {
|
|
1216
|
+
...style,
|
|
1217
|
+
...inlineCssToJs(node.attrs?.style)
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
//#endregion
|
|
1223
|
+
//#region src/extensions/global-content.ts
|
|
1224
|
+
const GLOBAL_CONTENT_NODE_TYPE = "globalContent";
|
|
1225
|
+
let cachedGlobalPosition = null;
|
|
1226
|
+
function findGlobalContentPositions(doc) {
|
|
1227
|
+
const positions = [];
|
|
1228
|
+
doc.descendants((node, position) => {
|
|
1229
|
+
if (node.type.name === GLOBAL_CONTENT_NODE_TYPE) positions.push(position);
|
|
1230
|
+
});
|
|
1231
|
+
return positions;
|
|
1232
|
+
}
|
|
1233
|
+
function getCachedGlobalContentPosition(doc) {
|
|
1234
|
+
if (cachedGlobalPosition != null) try {
|
|
1235
|
+
if (doc.nodeAt(cachedGlobalPosition)?.type.name === GLOBAL_CONTENT_NODE_TYPE) return cachedGlobalPosition;
|
|
1236
|
+
} catch {
|
|
1237
|
+
cachedGlobalPosition = null;
|
|
1238
|
+
}
|
|
1239
|
+
cachedGlobalPosition = findGlobalContentPositions(doc)[0] ?? null;
|
|
1240
|
+
return cachedGlobalPosition;
|
|
1241
|
+
}
|
|
1242
|
+
function getGlobalContent(key, editor) {
|
|
1243
|
+
const position = getCachedGlobalContentPosition(editor.state.doc);
|
|
1244
|
+
if (cachedGlobalPosition == null) return null;
|
|
1245
|
+
return editor.state.doc.nodeAt(position)?.attrs.data[key] ?? null;
|
|
1246
|
+
}
|
|
1247
|
+
const GlobalContent = _tiptap_core.Node.create({
|
|
1248
|
+
name: GLOBAL_CONTENT_NODE_TYPE,
|
|
1249
|
+
addOptions() {
|
|
1250
|
+
return {
|
|
1251
|
+
key: GLOBAL_CONTENT_NODE_TYPE,
|
|
1252
|
+
data: {}
|
|
1253
|
+
};
|
|
1254
|
+
},
|
|
1255
|
+
group: "block",
|
|
1256
|
+
selectable: false,
|
|
1257
|
+
draggable: false,
|
|
1258
|
+
atom: true,
|
|
1259
|
+
addAttributes() {
|
|
1260
|
+
return { data: { default: this.options.data } };
|
|
1261
|
+
},
|
|
1262
|
+
parseHTML() {
|
|
1263
|
+
return [{ tag: `div[data-type="${this.name}"]` }];
|
|
1264
|
+
},
|
|
1265
|
+
renderHTML({ HTMLAttributes }) {
|
|
1266
|
+
return ["div", (0, _tiptap_core.mergeAttributes)(HTMLAttributes, {
|
|
1267
|
+
"data-type": this.name,
|
|
1268
|
+
style: "width: 100%; height: 1px; visibility: hidden;"
|
|
1269
|
+
})];
|
|
1270
|
+
},
|
|
1271
|
+
addCommands() {
|
|
1272
|
+
return { setGlobalContent: (key, value) => ({ tr, dispatch }) => {
|
|
1273
|
+
const ensureGlobalPosition = () => {
|
|
1274
|
+
const positions = findGlobalContentPositions(tr.doc);
|
|
1275
|
+
for (let i = positions.length - 1; i > 0; i--) tr.delete(positions[i], positions[i] + 1);
|
|
1276
|
+
const pos = positions[0] ?? -1;
|
|
1277
|
+
if (pos >= 0) cachedGlobalPosition = pos;
|
|
1278
|
+
else {
|
|
1279
|
+
cachedGlobalPosition = 0;
|
|
1280
|
+
tr.insert(0, this.type.create());
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
if (dispatch) {
|
|
1284
|
+
ensureGlobalPosition();
|
|
1285
|
+
if (cachedGlobalPosition == null) return false;
|
|
1286
|
+
tr.setNodeAttribute(cachedGlobalPosition, "data", {
|
|
1287
|
+
...tr.doc.nodeAt(cachedGlobalPosition)?.attrs.data,
|
|
1288
|
+
[key]: value
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
return true;
|
|
1292
|
+
} };
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
//#endregion
|
|
1297
|
+
//#region src/extensions/hard-break.tsx
|
|
1298
|
+
const HardBreak = EmailNode.from(_tiptap_extension_hard_break.default, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}));
|
|
1299
|
+
|
|
1300
|
+
//#endregion
|
|
1301
|
+
//#region src/extensions/heading.tsx
|
|
1302
|
+
const Heading = EmailNode.from(_tiptap_extension_heading.Heading.extend({ addNodeView() {
|
|
1303
|
+
return (0, _tiptap_react.ReactNodeViewRenderer)(({ node }) => {
|
|
1304
|
+
const level = node.attrs.level ?? 1;
|
|
1305
|
+
const { class: className, ...rest } = node.attrs;
|
|
1306
|
+
const attrs = {
|
|
1307
|
+
...rest,
|
|
1308
|
+
className: `node-h${level} ${className}`,
|
|
1309
|
+
style: inlineCssToJs(node.attrs.style)
|
|
1310
|
+
};
|
|
1311
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react.NodeViewWrapper, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Heading, {
|
|
1312
|
+
as: `h${level}`,
|
|
1313
|
+
...attrs,
|
|
1314
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react.NodeViewContent, {})
|
|
1315
|
+
}) });
|
|
1316
|
+
});
|
|
1317
|
+
} }), ({ children, node, style }) => {
|
|
1318
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Heading, {
|
|
1319
|
+
as: `h${node.attrs?.level ?? 1}`,
|
|
1320
|
+
className: node.attrs?.class || void 0,
|
|
1321
|
+
style: {
|
|
1322
|
+
...style,
|
|
1323
|
+
...inlineCssToJs(node.attrs?.style),
|
|
1324
|
+
...getTextAlignment(node.attrs?.align ?? node.attrs?.alignment)
|
|
1325
|
+
},
|
|
1326
|
+
children
|
|
1327
|
+
});
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
//#endregion
|
|
1331
|
+
//#region src/extensions/italic.tsx
|
|
1332
|
+
const Italic = EmailMark.from(_tiptap_extension_italic.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", {
|
|
1333
|
+
style,
|
|
1334
|
+
children
|
|
1335
|
+
}));
|
|
1336
|
+
|
|
1337
|
+
//#endregion
|
|
1338
|
+
//#region src/extensions/preserved-style.tsx
|
|
1339
|
+
const PreservedStyle = EmailMark.create({
|
|
1340
|
+
name: "preservedStyle",
|
|
1341
|
+
addAttributes() {
|
|
1342
|
+
return { style: {
|
|
1343
|
+
default: null,
|
|
1344
|
+
parseHTML: (element) => element.getAttribute("style"),
|
|
1345
|
+
renderHTML: (attributes) => {
|
|
1346
|
+
if (!attributes.style) return {};
|
|
1347
|
+
return { style: attributes.style };
|
|
1348
|
+
}
|
|
1349
|
+
} };
|
|
1350
|
+
},
|
|
1351
|
+
parseHTML() {
|
|
1352
|
+
return [{
|
|
1353
|
+
tag: "span[style]",
|
|
1354
|
+
getAttrs: (element) => {
|
|
1355
|
+
if (typeof element === "string") return false;
|
|
1356
|
+
const style = element.getAttribute("style");
|
|
1357
|
+
if (style && hasPreservableStyles(style)) return { style };
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
}];
|
|
1361
|
+
},
|
|
1362
|
+
renderHTML({ HTMLAttributes }) {
|
|
1363
|
+
return [
|
|
1364
|
+
"span",
|
|
1365
|
+
(0, _tiptap_core.mergeAttributes)(HTMLAttributes),
|
|
1366
|
+
0
|
|
1367
|
+
];
|
|
1368
|
+
},
|
|
1369
|
+
renderToReactEmail({ children, mark }) {
|
|
1370
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1371
|
+
style: mark.attrs?.style ? inlineCssToJs(mark.attrs.style) : void 0,
|
|
1372
|
+
children
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
const LINK_INDICATOR_STYLES = [
|
|
1377
|
+
"color",
|
|
1378
|
+
"text-decoration",
|
|
1379
|
+
"text-decoration-line",
|
|
1380
|
+
"text-decoration-color",
|
|
1381
|
+
"text-decoration-style"
|
|
1382
|
+
];
|
|
1383
|
+
function parseStyleString(styleString) {
|
|
1384
|
+
const temp = document.createElement("div");
|
|
1385
|
+
temp.style.cssText = styleString;
|
|
1386
|
+
return temp.style;
|
|
1387
|
+
}
|
|
1388
|
+
function hasBackground(style) {
|
|
1389
|
+
const bgColor = style.backgroundColor;
|
|
1390
|
+
const bg = style.background;
|
|
1391
|
+
if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") return true;
|
|
1392
|
+
if (bg && bg !== "transparent" && bg !== "none" && bg !== "rgba(0, 0, 0, 0)") return true;
|
|
1393
|
+
return false;
|
|
1394
|
+
}
|
|
1395
|
+
function hasPreservableStyles(styleString) {
|
|
1396
|
+
return processStylesForUnlink(styleString) !== null;
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Processes styles when unlinking:
|
|
1400
|
+
* - Has background (button-like): preserve all styles
|
|
1401
|
+
* - No background: strip link-indicator styles (color, text-decoration), keep the rest
|
|
1402
|
+
*/
|
|
1403
|
+
function processStylesForUnlink(styleString) {
|
|
1404
|
+
if (!styleString) return null;
|
|
1405
|
+
const style = parseStyleString(styleString);
|
|
1406
|
+
if (hasBackground(style)) return styleString;
|
|
1407
|
+
const filtered = [];
|
|
1408
|
+
for (let i = 0; i < style.length; i++) {
|
|
1409
|
+
const prop = style[i];
|
|
1410
|
+
if (LINK_INDICATOR_STYLES.includes(prop)) continue;
|
|
1411
|
+
const value = style.getPropertyValue(prop);
|
|
1412
|
+
if (value) filtered.push(`${prop}: ${value}`);
|
|
1413
|
+
}
|
|
1414
|
+
return filtered.length > 0 ? filtered.join("; ") : null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
//#endregion
|
|
1418
|
+
//#region src/extensions/link.tsx
|
|
1419
|
+
const Link = EmailMark.from(_tiptap_extension_link.default, ({ children, mark, style }) => {
|
|
1420
|
+
const linkMarkStyle = mark.attrs?.style ? inlineCssToJs(mark.attrs.style) : {};
|
|
1421
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Link, {
|
|
1422
|
+
href: mark.attrs?.href ?? void 0,
|
|
1423
|
+
rel: mark.attrs?.rel ?? void 0,
|
|
1424
|
+
style: {
|
|
1425
|
+
...style,
|
|
1426
|
+
...linkMarkStyle
|
|
1427
|
+
},
|
|
1428
|
+
target: mark.attrs?.target ?? void 0,
|
|
1429
|
+
...mark.attrs?.["ses:no-track"] ? { "ses:no-track": mark.attrs["ses:no-track"] } : {},
|
|
1430
|
+
children
|
|
1431
|
+
});
|
|
1432
|
+
}).extend({
|
|
921
1433
|
parseHTML() {
|
|
922
1434
|
return [{
|
|
923
|
-
tag: "
|
|
1435
|
+
tag: "a[target]:not([data-id=\"react-email-button\"])",
|
|
1436
|
+
getAttrs: (node) => {
|
|
1437
|
+
if (typeof node === "string") return false;
|
|
1438
|
+
const element = node;
|
|
1439
|
+
const attrs = {};
|
|
1440
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
1441
|
+
attrs[attr.name] = attr.value;
|
|
1442
|
+
});
|
|
1443
|
+
return attrs;
|
|
1444
|
+
}
|
|
1445
|
+
}, {
|
|
1446
|
+
tag: "a[href]:not([data-id=\"react-email-button\"])",
|
|
924
1447
|
getAttrs: (node) => {
|
|
925
1448
|
if (typeof node === "string") return false;
|
|
926
1449
|
const element = node;
|
|
@@ -932,29 +1455,51 @@ const Div = EmailNode.create({
|
|
|
932
1455
|
}
|
|
933
1456
|
}];
|
|
934
1457
|
},
|
|
935
|
-
renderHTML({ HTMLAttributes }) {
|
|
936
|
-
return [
|
|
937
|
-
"div",
|
|
938
|
-
(0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
|
|
939
|
-
0
|
|
940
|
-
];
|
|
941
|
-
},
|
|
942
1458
|
addAttributes() {
|
|
943
|
-
return {
|
|
1459
|
+
return {
|
|
1460
|
+
...this.parent?.(),
|
|
1461
|
+
"ses:no-track": {
|
|
1462
|
+
default: null,
|
|
1463
|
+
parseHTML: (element) => element.getAttribute("ses:no-track")
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
944
1466
|
},
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1467
|
+
addCommands() {
|
|
1468
|
+
return {
|
|
1469
|
+
...this.parent?.(),
|
|
1470
|
+
unsetLink: () => ({ state, chain }) => {
|
|
1471
|
+
const { from } = state.selection;
|
|
1472
|
+
const linkStyle = state.doc.resolve(from).marks().find((m) => m.type.name === "link")?.attrs?.style ?? null;
|
|
1473
|
+
const preservedStyle = processStylesForUnlink(linkStyle);
|
|
1474
|
+
const shouldRemoveUnderline = preservedStyle !== linkStyle;
|
|
1475
|
+
if (preservedStyle) {
|
|
1476
|
+
const cmd = chain().extendMarkRange("link").unsetMark("link").setMark("preservedStyle", { style: preservedStyle });
|
|
1477
|
+
return shouldRemoveUnderline ? cmd.unsetMark("underline").run() : cmd.run();
|
|
1478
|
+
}
|
|
1479
|
+
return chain().extendMarkRange("link").unsetMark("link").unsetMark("underline").run();
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
},
|
|
1483
|
+
addKeyboardShortcuts() {
|
|
1484
|
+
return { "Mod-k": () => {
|
|
1485
|
+
editorEventBus.dispatch("bubble-menu:add-link", void 0);
|
|
1486
|
+
return this.editor.chain().focus().toggleLink({ href: "" }).run();
|
|
1487
|
+
} };
|
|
955
1488
|
}
|
|
956
1489
|
});
|
|
957
1490
|
|
|
1491
|
+
//#endregion
|
|
1492
|
+
//#region src/extensions/list-item.tsx
|
|
1493
|
+
const ListItem = EmailNode.from(_tiptap_extension_list_item.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
1494
|
+
className: node.attrs?.class || void 0,
|
|
1495
|
+
style: {
|
|
1496
|
+
...style,
|
|
1497
|
+
...inlineCssToJs(node.attrs?.style),
|
|
1498
|
+
...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
|
|
1499
|
+
},
|
|
1500
|
+
children
|
|
1501
|
+
}));
|
|
1502
|
+
|
|
958
1503
|
//#endregion
|
|
959
1504
|
//#region src/extensions/max-nesting.ts
|
|
960
1505
|
const MaxNesting = _tiptap_core.Extension.create({
|
|
@@ -1030,6 +1575,33 @@ const MaxNesting = _tiptap_core.Extension.create({
|
|
|
1030
1575
|
}
|
|
1031
1576
|
});
|
|
1032
1577
|
|
|
1578
|
+
//#endregion
|
|
1579
|
+
//#region src/extensions/ordered-list.tsx
|
|
1580
|
+
const OrderedList = EmailNode.from(_tiptap_extension_ordered_list.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
|
|
1581
|
+
className: node.attrs?.class || void 0,
|
|
1582
|
+
start: node.attrs?.start,
|
|
1583
|
+
style: {
|
|
1584
|
+
...style,
|
|
1585
|
+
...inlineCssToJs(node.attrs?.style)
|
|
1586
|
+
},
|
|
1587
|
+
children
|
|
1588
|
+
}));
|
|
1589
|
+
|
|
1590
|
+
//#endregion
|
|
1591
|
+
//#region src/extensions/paragraph.tsx
|
|
1592
|
+
const Paragraph = EmailNode.from(_tiptap_extension_paragraph.default, ({ children, node, style }) => {
|
|
1593
|
+
const isEmpty = !node.content || node.content.length === 0;
|
|
1594
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1595
|
+
className: node.attrs?.class || void 0,
|
|
1596
|
+
style: {
|
|
1597
|
+
...style,
|
|
1598
|
+
...inlineCssToJs(node.attrs?.style),
|
|
1599
|
+
...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
|
|
1600
|
+
},
|
|
1601
|
+
children: isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}) : children
|
|
1602
|
+
});
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1033
1605
|
//#endregion
|
|
1034
1606
|
//#region src/extensions/placeholder.ts
|
|
1035
1607
|
const Placeholder = _tiptap_extension_placeholder.default.configure({
|
|
@@ -1040,80 +1612,6 @@ const Placeholder = _tiptap_extension_placeholder.default.configure({
|
|
|
1040
1612
|
includeChildren: true
|
|
1041
1613
|
});
|
|
1042
1614
|
|
|
1043
|
-
//#endregion
|
|
1044
|
-
//#region src/extensions/preserved-style.ts
|
|
1045
|
-
const PreservedStyle = _tiptap_core.Mark.create({
|
|
1046
|
-
name: "preservedStyle",
|
|
1047
|
-
addAttributes() {
|
|
1048
|
-
return { style: {
|
|
1049
|
-
default: null,
|
|
1050
|
-
parseHTML: (element) => element.getAttribute("style"),
|
|
1051
|
-
renderHTML: (attributes) => {
|
|
1052
|
-
if (!attributes.style) return {};
|
|
1053
|
-
return { style: attributes.style };
|
|
1054
|
-
}
|
|
1055
|
-
} };
|
|
1056
|
-
},
|
|
1057
|
-
parseHTML() {
|
|
1058
|
-
return [{
|
|
1059
|
-
tag: "span[style]",
|
|
1060
|
-
getAttrs: (element) => {
|
|
1061
|
-
if (typeof element === "string") return false;
|
|
1062
|
-
const style = element.getAttribute("style");
|
|
1063
|
-
if (style && hasPreservableStyles(style)) return { style };
|
|
1064
|
-
return false;
|
|
1065
|
-
}
|
|
1066
|
-
}];
|
|
1067
|
-
},
|
|
1068
|
-
renderHTML({ HTMLAttributes }) {
|
|
1069
|
-
return [
|
|
1070
|
-
"span",
|
|
1071
|
-
(0, _tiptap_core.mergeAttributes)(HTMLAttributes),
|
|
1072
|
-
0
|
|
1073
|
-
];
|
|
1074
|
-
}
|
|
1075
|
-
});
|
|
1076
|
-
const LINK_INDICATOR_STYLES = [
|
|
1077
|
-
"color",
|
|
1078
|
-
"text-decoration",
|
|
1079
|
-
"text-decoration-line",
|
|
1080
|
-
"text-decoration-color",
|
|
1081
|
-
"text-decoration-style"
|
|
1082
|
-
];
|
|
1083
|
-
function parseStyleString(styleString) {
|
|
1084
|
-
const temp = document.createElement("div");
|
|
1085
|
-
temp.style.cssText = styleString;
|
|
1086
|
-
return temp.style;
|
|
1087
|
-
}
|
|
1088
|
-
function hasBackground(style) {
|
|
1089
|
-
const bgColor = style.backgroundColor;
|
|
1090
|
-
const bg = style.background;
|
|
1091
|
-
if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") return true;
|
|
1092
|
-
if (bg && bg !== "transparent" && bg !== "none" && bg !== "rgba(0, 0, 0, 0)") return true;
|
|
1093
|
-
return false;
|
|
1094
|
-
}
|
|
1095
|
-
function hasPreservableStyles(styleString) {
|
|
1096
|
-
return processStylesForUnlink(styleString) !== null;
|
|
1097
|
-
}
|
|
1098
|
-
/**
|
|
1099
|
-
* Processes styles when unlinking:
|
|
1100
|
-
* - Has background (button-like): preserve all styles
|
|
1101
|
-
* - No background: strip link-indicator styles (color, text-decoration), keep the rest
|
|
1102
|
-
*/
|
|
1103
|
-
function processStylesForUnlink(styleString) {
|
|
1104
|
-
if (!styleString) return null;
|
|
1105
|
-
const style = parseStyleString(styleString);
|
|
1106
|
-
if (hasBackground(style)) return styleString;
|
|
1107
|
-
const filtered = [];
|
|
1108
|
-
for (let i = 0; i < style.length; i++) {
|
|
1109
|
-
const prop = style[i];
|
|
1110
|
-
if (LINK_INDICATOR_STYLES.includes(prop)) continue;
|
|
1111
|
-
const value = style.getPropertyValue(prop);
|
|
1112
|
-
if (value) filtered.push(`${prop}: ${value}`);
|
|
1113
|
-
}
|
|
1114
|
-
return filtered.length > 0 ? filtered.join("; ") : null;
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
1615
|
//#endregion
|
|
1118
1616
|
//#region src/extensions/preview-text.ts
|
|
1119
1617
|
const PreviewText = _tiptap_core.Node.create({
|
|
@@ -1155,17 +1653,6 @@ const PreviewText = _tiptap_core.Node.create({
|
|
|
1155
1653
|
}
|
|
1156
1654
|
});
|
|
1157
1655
|
|
|
1158
|
-
//#endregion
|
|
1159
|
-
//#region src/utils/get-text-alignment.ts
|
|
1160
|
-
function getTextAlignment(alignment) {
|
|
1161
|
-
switch (alignment) {
|
|
1162
|
-
case "left": return { textAlign: "left" };
|
|
1163
|
-
case "center": return { textAlign: "center" };
|
|
1164
|
-
case "right": return { textAlign: "right" };
|
|
1165
|
-
default: return {};
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
1656
|
//#endregion
|
|
1170
1657
|
//#region src/extensions/section.tsx
|
|
1171
1658
|
const Section = EmailNode.create({
|
|
@@ -1214,6 +1701,13 @@ const Section = EmailNode.create({
|
|
|
1214
1701
|
}
|
|
1215
1702
|
});
|
|
1216
1703
|
|
|
1704
|
+
//#endregion
|
|
1705
|
+
//#region src/extensions/strike.tsx
|
|
1706
|
+
const Strike = EmailMark.from(_tiptap_extension_strike.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("s", {
|
|
1707
|
+
style,
|
|
1708
|
+
children
|
|
1709
|
+
}));
|
|
1710
|
+
|
|
1217
1711
|
//#endregion
|
|
1218
1712
|
//#region src/extensions/style-attribute.tsx
|
|
1219
1713
|
const StyleAttribute = _tiptap_core.Extension.create({
|
|
@@ -1263,12 +1757,12 @@ const StyleAttribute = _tiptap_core.Extension.create({
|
|
|
1263
1757
|
});
|
|
1264
1758
|
|
|
1265
1759
|
//#endregion
|
|
1266
|
-
//#region src/extensions/sup.
|
|
1760
|
+
//#region src/extensions/sup.tsx
|
|
1267
1761
|
/**
|
|
1268
1762
|
* This extension allows you to mark text as superscript.
|
|
1269
1763
|
* @see https://tiptap.dev/api/marks/superscript
|
|
1270
1764
|
*/
|
|
1271
|
-
const Sup =
|
|
1765
|
+
const Sup = EmailMark.create({
|
|
1272
1766
|
name: "sup",
|
|
1273
1767
|
addOptions() {
|
|
1274
1768
|
return { HTMLAttributes: {} };
|
|
@@ -1283,6 +1777,12 @@ const Sup = _tiptap_core.Mark.create({
|
|
|
1283
1777
|
0
|
|
1284
1778
|
];
|
|
1285
1779
|
},
|
|
1780
|
+
renderToReactEmail({ children, style }) {
|
|
1781
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("sup", {
|
|
1782
|
+
style,
|
|
1783
|
+
children
|
|
1784
|
+
});
|
|
1785
|
+
},
|
|
1286
1786
|
addCommands() {
|
|
1287
1787
|
return {
|
|
1288
1788
|
setSup: () => ({ commands }) => {
|
|
@@ -1485,8 +1985,8 @@ const TableHeader = _tiptap_core.Node.create({
|
|
|
1485
1985
|
});
|
|
1486
1986
|
|
|
1487
1987
|
//#endregion
|
|
1488
|
-
//#region src/extensions/uppercase.
|
|
1489
|
-
const Uppercase =
|
|
1988
|
+
//#region src/extensions/uppercase.tsx
|
|
1989
|
+
const Uppercase = EmailMark.create({
|
|
1490
1990
|
name: "uppercase",
|
|
1491
1991
|
addOptions() {
|
|
1492
1992
|
return { HTMLAttributes: {} };
|
|
@@ -1507,6 +2007,15 @@ const Uppercase = _tiptap_core.Mark.create({
|
|
|
1507
2007
|
0
|
|
1508
2008
|
];
|
|
1509
2009
|
},
|
|
2010
|
+
renderToReactEmail({ children, style }) {
|
|
2011
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2012
|
+
style: {
|
|
2013
|
+
...style,
|
|
2014
|
+
textTransform: "uppercase"
|
|
2015
|
+
},
|
|
2016
|
+
children
|
|
2017
|
+
});
|
|
2018
|
+
},
|
|
1510
2019
|
addCommands() {
|
|
1511
2020
|
return {
|
|
1512
2021
|
setUppercase: () => ({ commands }) => {
|
|
@@ -1694,39 +2203,23 @@ const ColumnsColumn = EmailNode.create({
|
|
|
1694
2203
|
|
|
1695
2204
|
//#endregion
|
|
1696
2205
|
//#region src/extensions/index.ts
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
bulletList: { HTMLAttributes: { class: "node-bulletList" } },
|
|
1708
|
-
paragraph: { HTMLAttributes: { class: "node-paragraph" } },
|
|
1709
|
-
orderedList: { HTMLAttributes: { class: "node-orderedList" } },
|
|
1710
|
-
blockquote: { HTMLAttributes: { class: "node-blockquote" } },
|
|
1711
|
-
codeBlock: false,
|
|
1712
|
-
code: { HTMLAttributes: {
|
|
1713
|
-
class: "node-inlineCode",
|
|
1714
|
-
spellcheck: "false"
|
|
1715
|
-
} },
|
|
1716
|
-
horizontalRule: false,
|
|
1717
|
-
dropcursor: {
|
|
1718
|
-
color: "#61a8f8",
|
|
1719
|
-
class: "rounded-full animate-[fade-in_300ms_ease-in-out] !z-40",
|
|
1720
|
-
width: 4
|
|
1721
|
-
}
|
|
1722
|
-
}),
|
|
1723
|
-
CodeBlockPrism.configure({
|
|
1724
|
-
defaultLanguage: "javascript",
|
|
1725
|
-
HTMLAttributes: { class: "prism node-codeBlock" }
|
|
1726
|
-
}),
|
|
2206
|
+
const starterKitExtensions = {
|
|
2207
|
+
CodeBlockPrism,
|
|
2208
|
+
Code,
|
|
2209
|
+
Paragraph,
|
|
2210
|
+
BulletList,
|
|
2211
|
+
OrderedList,
|
|
2212
|
+
Blockquote,
|
|
2213
|
+
ListItem,
|
|
2214
|
+
HardBreak,
|
|
2215
|
+
Italic,
|
|
1727
2216
|
Placeholder,
|
|
1728
2217
|
PreviewText,
|
|
1729
2218
|
Bold,
|
|
2219
|
+
Strike,
|
|
2220
|
+
Heading,
|
|
2221
|
+
Divider,
|
|
2222
|
+
Link,
|
|
1730
2223
|
Sup,
|
|
1731
2224
|
Uppercase,
|
|
1732
2225
|
PreservedStyle,
|
|
@@ -1738,80 +2231,378 @@ const coreExtensions = [
|
|
|
1738
2231
|
Div,
|
|
1739
2232
|
Button,
|
|
1740
2233
|
Section,
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2234
|
+
GlobalContent,
|
|
2235
|
+
AlignmentAttribute,
|
|
2236
|
+
StyleAttribute,
|
|
2237
|
+
ClassAttribute,
|
|
2238
|
+
MaxNesting
|
|
2239
|
+
};
|
|
2240
|
+
const StarterKit = _tiptap_core.Extension.create({
|
|
2241
|
+
name: "reactEmailStarterKit",
|
|
2242
|
+
addOptions() {
|
|
2243
|
+
return {
|
|
2244
|
+
TiptapStarterKit: {},
|
|
2245
|
+
CodeBlockPrism: {
|
|
2246
|
+
defaultLanguage: "javascript",
|
|
2247
|
+
HTMLAttributes: { class: "prism node-codeBlock" }
|
|
2248
|
+
},
|
|
2249
|
+
Code: { HTMLAttributes: {
|
|
2250
|
+
class: "node-inlineCode",
|
|
2251
|
+
spellcheck: "false"
|
|
2252
|
+
} },
|
|
2253
|
+
Paragraph: { HTMLAttributes: { class: "node-paragraph" } },
|
|
2254
|
+
BulletList: { HTMLAttributes: { class: "node-bulletList" } },
|
|
2255
|
+
OrderedList: { HTMLAttributes: { class: "node-orderedList" } },
|
|
2256
|
+
Blockquote: { HTMLAttributes: { class: "node-blockquote" } },
|
|
2257
|
+
ListItem: {},
|
|
2258
|
+
HardBreak: {},
|
|
2259
|
+
Italic: {},
|
|
2260
|
+
Placeholder: {},
|
|
2261
|
+
PreviewText: {},
|
|
2262
|
+
Bold: {},
|
|
2263
|
+
Strike: {},
|
|
2264
|
+
Heading: {},
|
|
2265
|
+
Divider: {},
|
|
2266
|
+
Link: {},
|
|
2267
|
+
Sup: {},
|
|
2268
|
+
Uppercase: {},
|
|
2269
|
+
PreservedStyle: {},
|
|
2270
|
+
Table: {},
|
|
2271
|
+
TableRow: {},
|
|
2272
|
+
TableCell: {},
|
|
2273
|
+
TableHeader: {},
|
|
2274
|
+
Body: {},
|
|
2275
|
+
Div: {},
|
|
2276
|
+
Button: {},
|
|
2277
|
+
Section: {},
|
|
2278
|
+
GlobalContent: {},
|
|
2279
|
+
AlignmentAttribute: { types: [
|
|
2280
|
+
"heading",
|
|
2281
|
+
"paragraph",
|
|
2282
|
+
"image",
|
|
2283
|
+
"blockquote",
|
|
2284
|
+
"codeBlock",
|
|
2285
|
+
"bulletList",
|
|
2286
|
+
"orderedList",
|
|
2287
|
+
"listItem",
|
|
2288
|
+
"button",
|
|
2289
|
+
"youtube",
|
|
2290
|
+
"twitter",
|
|
2291
|
+
"table",
|
|
2292
|
+
"tableRow",
|
|
2293
|
+
"tableCell",
|
|
2294
|
+
"tableHeader",
|
|
2295
|
+
"columnsColumn"
|
|
2296
|
+
] },
|
|
2297
|
+
StyleAttribute: { types: [
|
|
2298
|
+
"heading",
|
|
2299
|
+
"paragraph",
|
|
2300
|
+
"image",
|
|
2301
|
+
"blockquote",
|
|
2302
|
+
"codeBlock",
|
|
2303
|
+
"bulletList",
|
|
2304
|
+
"orderedList",
|
|
2305
|
+
"listItem",
|
|
2306
|
+
"button",
|
|
2307
|
+
"youtube",
|
|
2308
|
+
"twitter",
|
|
2309
|
+
"horizontalRule",
|
|
2310
|
+
"footer",
|
|
2311
|
+
"section",
|
|
2312
|
+
"div",
|
|
2313
|
+
"body",
|
|
2314
|
+
"table",
|
|
2315
|
+
"tableRow",
|
|
2316
|
+
"tableCell",
|
|
2317
|
+
"tableHeader",
|
|
2318
|
+
"columnsColumn",
|
|
2319
|
+
"link"
|
|
2320
|
+
] },
|
|
2321
|
+
ClassAttribute: { types: [
|
|
2322
|
+
"heading",
|
|
2323
|
+
"paragraph",
|
|
2324
|
+
"image",
|
|
2325
|
+
"blockquote",
|
|
2326
|
+
"bulletList",
|
|
2327
|
+
"orderedList",
|
|
2328
|
+
"listItem",
|
|
2329
|
+
"button",
|
|
2330
|
+
"youtube",
|
|
2331
|
+
"twitter",
|
|
2332
|
+
"horizontalRule",
|
|
2333
|
+
"footer",
|
|
2334
|
+
"section",
|
|
2335
|
+
"div",
|
|
2336
|
+
"body",
|
|
2337
|
+
"table",
|
|
2338
|
+
"tableRow",
|
|
2339
|
+
"tableCell",
|
|
2340
|
+
"tableHeader",
|
|
2341
|
+
"columnsColumn",
|
|
2342
|
+
"link"
|
|
2343
|
+
] },
|
|
2344
|
+
MaxNesting: {
|
|
2345
|
+
maxDepth: 50,
|
|
2346
|
+
nodeTypes: [
|
|
2347
|
+
"section",
|
|
2348
|
+
"bulletList",
|
|
2349
|
+
"orderedList"
|
|
2350
|
+
]
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2353
|
+
},
|
|
2354
|
+
addExtensions() {
|
|
2355
|
+
const extensions = [];
|
|
2356
|
+
if (this.options.TiptapStarterKit !== false) extensions.push(_tiptap_starter_kit.default.configure({
|
|
2357
|
+
undoRedo: false,
|
|
2358
|
+
heading: false,
|
|
2359
|
+
link: false,
|
|
2360
|
+
underline: false,
|
|
2361
|
+
trailingNode: false,
|
|
2362
|
+
bold: false,
|
|
2363
|
+
italic: false,
|
|
2364
|
+
strike: false,
|
|
2365
|
+
code: false,
|
|
2366
|
+
paragraph: false,
|
|
2367
|
+
bulletList: false,
|
|
2368
|
+
orderedList: false,
|
|
2369
|
+
listItem: false,
|
|
2370
|
+
blockquote: false,
|
|
2371
|
+
hardBreak: false,
|
|
2372
|
+
gapcursor: false,
|
|
2373
|
+
codeBlock: false,
|
|
2374
|
+
horizontalRule: false,
|
|
2375
|
+
dropcursor: {
|
|
2376
|
+
color: "#61a8f8",
|
|
2377
|
+
class: "rounded-full animate-[fade-in_300ms_ease-in-out] !z-40",
|
|
2378
|
+
width: 4
|
|
2379
|
+
},
|
|
2380
|
+
...this.options.TiptapStarterKit
|
|
2381
|
+
}));
|
|
2382
|
+
for (const [name, extension] of Object.entries(starterKitExtensions)) {
|
|
2383
|
+
const key = name;
|
|
2384
|
+
const extensionOptions = this.options[key];
|
|
2385
|
+
if (extensionOptions !== false) extensions.push(extension.configure(extensionOptions));
|
|
2386
|
+
}
|
|
2387
|
+
return extensions;
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
|
|
2391
|
+
//#endregion
|
|
2392
|
+
//#region src/core/create-drop-handler.ts
|
|
2393
|
+
function createDropHandler({ onPaste, onUploadImage }) {
|
|
2394
|
+
return (view, event, _slice, moved) => {
|
|
2395
|
+
if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
|
|
2396
|
+
event.preventDefault();
|
|
2397
|
+
const file = event.dataTransfer.files[0];
|
|
2398
|
+
if (onPaste?.(file, view)) return true;
|
|
2399
|
+
if (file.type.includes("image/") && onUploadImage) {
|
|
2400
|
+
onUploadImage(file, view, (view.posAtCoords({
|
|
2401
|
+
left: event.clientX,
|
|
2402
|
+
top: event.clientY
|
|
2403
|
+
})?.pos || 0) - 1);
|
|
2404
|
+
return true;
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
return false;
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
//#endregion
|
|
2412
|
+
//#region src/utils/paste-sanitizer.ts
|
|
2413
|
+
/**
|
|
2414
|
+
* Sanitizes pasted HTML.
|
|
2415
|
+
* - From editor (has node-* classes): pass through as-is
|
|
2416
|
+
* - From external: strip all styles/classes, keep only semantic HTML
|
|
2417
|
+
*/
|
|
2418
|
+
/**
|
|
2419
|
+
* Detects content from the Resend editor by checking for node-* class names.
|
|
2420
|
+
*/
|
|
2421
|
+
const EDITOR_CLASS_PATTERN = /class="[^"]*node-/;
|
|
2422
|
+
/**
|
|
2423
|
+
* Attributes to preserve on specific elements for EXTERNAL content.
|
|
2424
|
+
* Only functional attributes - NO style or class.
|
|
2425
|
+
*/
|
|
2426
|
+
const PRESERVED_ATTRIBUTES = {
|
|
2427
|
+
a: [
|
|
2428
|
+
"href",
|
|
2429
|
+
"target",
|
|
2430
|
+
"rel"
|
|
2431
|
+
],
|
|
2432
|
+
img: [
|
|
2433
|
+
"src",
|
|
2434
|
+
"alt",
|
|
2435
|
+
"width",
|
|
2436
|
+
"height"
|
|
2437
|
+
],
|
|
2438
|
+
td: ["colspan", "rowspan"],
|
|
2439
|
+
th: [
|
|
2440
|
+
"colspan",
|
|
2441
|
+
"rowspan",
|
|
2442
|
+
"scope"
|
|
2443
|
+
],
|
|
2444
|
+
table: [
|
|
2445
|
+
"border",
|
|
2446
|
+
"cellpadding",
|
|
2447
|
+
"cellspacing"
|
|
2448
|
+
],
|
|
2449
|
+
"*": ["id"]
|
|
2450
|
+
};
|
|
2451
|
+
function isFromEditor(html) {
|
|
2452
|
+
return EDITOR_CLASS_PATTERN.test(html);
|
|
2453
|
+
}
|
|
2454
|
+
function sanitizePastedHtml(html) {
|
|
2455
|
+
if (isFromEditor(html)) return html;
|
|
2456
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
2457
|
+
sanitizeNode(doc.body);
|
|
2458
|
+
return doc.body.innerHTML;
|
|
2459
|
+
}
|
|
2460
|
+
function sanitizeNode(node) {
|
|
2461
|
+
if (node.nodeType === Node.ELEMENT_NODE) sanitizeElement(node);
|
|
2462
|
+
for (const child of Array.from(node.childNodes)) sanitizeNode(child);
|
|
2463
|
+
}
|
|
2464
|
+
function sanitizeElement(el) {
|
|
2465
|
+
const allowedForTag = PRESERVED_ATTRIBUTES[el.tagName.toLowerCase()] || [];
|
|
2466
|
+
const allowedGlobal = PRESERVED_ATTRIBUTES["*"] || [];
|
|
2467
|
+
const allowed = new Set([...allowedForTag, ...allowedGlobal]);
|
|
2468
|
+
const attributesToRemove = [];
|
|
2469
|
+
for (const attr of Array.from(el.attributes)) {
|
|
2470
|
+
if (attr.name.startsWith("data-")) {
|
|
2471
|
+
attributesToRemove.push(attr.name);
|
|
2472
|
+
continue;
|
|
2473
|
+
}
|
|
2474
|
+
if (!allowed.has(attr.name)) attributesToRemove.push(attr.name);
|
|
2475
|
+
}
|
|
2476
|
+
for (const attr of attributesToRemove) el.removeAttribute(attr);
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
//#endregion
|
|
2480
|
+
//#region src/core/create-paste-handler.ts
|
|
2481
|
+
function createPasteHandler({ onPaste, onUploadImage, extensions }) {
|
|
2482
|
+
return (view, event, slice) => {
|
|
2483
|
+
const text = event.clipboardData?.getData("text/plain");
|
|
2484
|
+
if (text && onPaste?.(text, view)) {
|
|
2485
|
+
event.preventDefault();
|
|
2486
|
+
return true;
|
|
2487
|
+
}
|
|
2488
|
+
if (event.clipboardData?.files?.[0]) {
|
|
2489
|
+
const file = event.clipboardData.files[0];
|
|
2490
|
+
if (onPaste?.(file, view)) {
|
|
2491
|
+
event.preventDefault();
|
|
2492
|
+
return true;
|
|
2493
|
+
}
|
|
2494
|
+
if (file.type.includes("image/") && onUploadImage) {
|
|
2495
|
+
const pos = view.state.selection.from;
|
|
2496
|
+
onUploadImage(file, view, pos);
|
|
2497
|
+
return true;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
/**
|
|
2501
|
+
* If the coming content has a single child, we can assume
|
|
2502
|
+
* it's a plain text and doesn't need to be parsed and
|
|
2503
|
+
* be introduced in a new line
|
|
2504
|
+
*/
|
|
2505
|
+
if (slice.content.childCount === 1) return false;
|
|
2506
|
+
if (event.clipboardData?.getData?.("text/html")) {
|
|
2507
|
+
event.preventDefault();
|
|
2508
|
+
const jsonContent = (0, _tiptap_html.generateJSON)(sanitizePastedHtml(event.clipboardData.getData("text/html")), extensions);
|
|
2509
|
+
const node = view.state.schema.nodeFromJSON(jsonContent);
|
|
2510
|
+
const transaction = view.state.tr.replaceSelectionWith(node, false);
|
|
2511
|
+
view.dispatch(transaction);
|
|
2512
|
+
return true;
|
|
2513
|
+
}
|
|
2514
|
+
return false;
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
//#endregion
|
|
2519
|
+
//#region src/core/is-document-visually-empty.ts
|
|
2520
|
+
function isDocumentVisuallyEmpty(doc) {
|
|
2521
|
+
let nonGlobalNodeCount = 0;
|
|
2522
|
+
let firstNonGlobalNode = null;
|
|
2523
|
+
for (let index = 0; index < doc.childCount; index += 1) {
|
|
2524
|
+
const node = doc.child(index);
|
|
2525
|
+
if (node.type.name === "globalContent") continue;
|
|
2526
|
+
nonGlobalNodeCount += 1;
|
|
2527
|
+
if (firstNonGlobalNode === null) firstNonGlobalNode = {
|
|
2528
|
+
type: node.type,
|
|
2529
|
+
textContent: node.textContent,
|
|
2530
|
+
childCount: node.content.childCount
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
if (nonGlobalNodeCount === 0) return true;
|
|
2534
|
+
if (nonGlobalNodeCount !== 1) return false;
|
|
2535
|
+
return firstNonGlobalNode?.type.name === "paragraph" && firstNonGlobalNode.textContent.trim().length === 0 && firstNonGlobalNode.childCount === 0;
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
//#endregion
|
|
2539
|
+
//#region src/core/use-editor.ts
|
|
2540
|
+
const COLLABORATION_EXTENSION_NAMES = new Set(["liveblocksExtension", "collaboration"]);
|
|
2541
|
+
function hasCollaborationExtension(exts) {
|
|
2542
|
+
return exts.some((ext) => COLLABORATION_EXTENSION_NAMES.has(ext.name));
|
|
2543
|
+
}
|
|
2544
|
+
function useEditor({ content, extensions = [], onUpdate, onPaste, onUploadImage, onReady, editable = true, ...rest }) {
|
|
2545
|
+
const [contentError, setContentError] = react.useState(null);
|
|
2546
|
+
const isCollaborative = hasCollaborationExtension(extensions);
|
|
2547
|
+
const effectiveExtensions = react.useMemo(() => [
|
|
2548
|
+
StarterKit,
|
|
2549
|
+
...isCollaborative ? [] : [_tiptap_extensions.UndoRedo],
|
|
2550
|
+
...extensions
|
|
2551
|
+
], [extensions, isCollaborative]);
|
|
2552
|
+
const editor = (0, _tiptap_react.useEditor)({
|
|
2553
|
+
content: isCollaborative ? void 0 : content,
|
|
2554
|
+
extensions: effectiveExtensions,
|
|
2555
|
+
editable,
|
|
2556
|
+
immediatelyRender: false,
|
|
2557
|
+
enableContentCheck: true,
|
|
2558
|
+
onContentError({ editor: editor$1, error, disableCollaboration }) {
|
|
2559
|
+
disableCollaboration();
|
|
2560
|
+
setContentError(error);
|
|
2561
|
+
console.error(error);
|
|
2562
|
+
editor$1.setEditable(false);
|
|
2563
|
+
},
|
|
2564
|
+
onCreate({ editor: editor$1 }) {
|
|
2565
|
+
onReady?.(editor$1);
|
|
2566
|
+
},
|
|
2567
|
+
onUpdate({ editor: editor$1, transaction }) {
|
|
2568
|
+
onUpdate?.(editor$1, transaction);
|
|
2569
|
+
},
|
|
2570
|
+
editorProps: {
|
|
2571
|
+
handleDOMEvents: { click: (view, event) => {
|
|
2572
|
+
if (!view.editable) {
|
|
2573
|
+
if (event.target.closest("a")) {
|
|
2574
|
+
event.preventDefault();
|
|
2575
|
+
return true;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
return false;
|
|
2579
|
+
} },
|
|
2580
|
+
handlePaste: createPasteHandler({
|
|
2581
|
+
onPaste,
|
|
2582
|
+
onUploadImage,
|
|
2583
|
+
extensions: effectiveExtensions
|
|
2584
|
+
}),
|
|
2585
|
+
handleDrop: createDropHandler({
|
|
2586
|
+
onPaste,
|
|
2587
|
+
onUploadImage
|
|
2588
|
+
})
|
|
2589
|
+
},
|
|
2590
|
+
...rest
|
|
2591
|
+
});
|
|
2592
|
+
return {
|
|
2593
|
+
editor,
|
|
2594
|
+
isEditorEmpty: (0, _tiptap_react.useEditorState)({
|
|
2595
|
+
editor,
|
|
2596
|
+
selector: (context) => {
|
|
2597
|
+
if (!context.editor) return true;
|
|
2598
|
+
return isDocumentVisuallyEmpty(context.editor.state.doc);
|
|
2599
|
+
}
|
|
2600
|
+
}) ?? true,
|
|
2601
|
+
extensions: effectiveExtensions,
|
|
2602
|
+
contentError,
|
|
2603
|
+
isCollaborative
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
1815
2606
|
|
|
1816
2607
|
//#endregion
|
|
1817
2608
|
//#region src/utils/set-text-alignment.ts
|
|
@@ -2967,13 +3758,12 @@ function groupByCategory(items) {
|
|
|
2967
3758
|
return ordered;
|
|
2968
3759
|
}
|
|
2969
3760
|
function CommandItem({ item, selected, onSelect }) {
|
|
2970
|
-
const Icon = item.icon;
|
|
2971
3761
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2972
3762
|
"data-re-slash-command-item": "",
|
|
2973
3763
|
"data-selected": selected || void 0,
|
|
2974
3764
|
onClick: onSelect,
|
|
2975
3765
|
type: "button",
|
|
2976
|
-
children: [
|
|
3766
|
+
children: [item.icon, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.title })]
|
|
2977
3767
|
});
|
|
2978
3768
|
}
|
|
2979
3769
|
function CommandList({ items, command, query, ref }) {
|
|
@@ -3048,11 +3838,11 @@ function CommandList({ items, command, query, ref }) {
|
|
|
3048
3838
|
}
|
|
3049
3839
|
|
|
3050
3840
|
//#endregion
|
|
3051
|
-
//#region src/ui/slash-command/commands.
|
|
3841
|
+
//#region src/ui/slash-command/commands.tsx
|
|
3052
3842
|
const TEXT = {
|
|
3053
3843
|
title: "Text",
|
|
3054
3844
|
description: "Plain text block",
|
|
3055
|
-
icon: lucide_react.Text,
|
|
3845
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Text, { size: 20 }),
|
|
3056
3846
|
category: "Text",
|
|
3057
3847
|
searchTerms: ["p", "paragraph"],
|
|
3058
3848
|
command: ({ editor, range }) => {
|
|
@@ -3062,7 +3852,7 @@ const TEXT = {
|
|
|
3062
3852
|
const H1 = {
|
|
3063
3853
|
title: "Title",
|
|
3064
3854
|
description: "Large heading",
|
|
3065
|
-
icon: lucide_react.Heading1,
|
|
3855
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Heading1, { size: 20 }),
|
|
3066
3856
|
category: "Text",
|
|
3067
3857
|
searchTerms: [
|
|
3068
3858
|
"title",
|
|
@@ -3077,7 +3867,7 @@ const H1 = {
|
|
|
3077
3867
|
const H2 = {
|
|
3078
3868
|
title: "Subtitle",
|
|
3079
3869
|
description: "Medium heading",
|
|
3080
|
-
icon: lucide_react.Heading2,
|
|
3870
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Heading2, { size: 20 }),
|
|
3081
3871
|
category: "Text",
|
|
3082
3872
|
searchTerms: [
|
|
3083
3873
|
"subtitle",
|
|
@@ -3091,7 +3881,7 @@ const H2 = {
|
|
|
3091
3881
|
const H3 = {
|
|
3092
3882
|
title: "Heading",
|
|
3093
3883
|
description: "Small heading",
|
|
3094
|
-
icon: lucide_react.Heading3,
|
|
3884
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Heading3, { size: 20 }),
|
|
3095
3885
|
category: "Text",
|
|
3096
3886
|
searchTerms: [
|
|
3097
3887
|
"subtitle",
|
|
@@ -3105,7 +3895,7 @@ const H3 = {
|
|
|
3105
3895
|
const BULLET_LIST = {
|
|
3106
3896
|
title: "Bullet list",
|
|
3107
3897
|
description: "Unordered list",
|
|
3108
|
-
icon: lucide_react.List,
|
|
3898
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.List, { size: 20 }),
|
|
3109
3899
|
category: "Text",
|
|
3110
3900
|
searchTerms: ["unordered", "point"],
|
|
3111
3901
|
command: ({ editor, range }) => {
|
|
@@ -3115,7 +3905,7 @@ const BULLET_LIST = {
|
|
|
3115
3905
|
const NUMBERED_LIST = {
|
|
3116
3906
|
title: "Numbered list",
|
|
3117
3907
|
description: "Ordered list",
|
|
3118
|
-
icon: lucide_react.ListOrdered,
|
|
3908
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ListOrdered, { size: 20 }),
|
|
3119
3909
|
category: "Text",
|
|
3120
3910
|
searchTerms: ["ordered"],
|
|
3121
3911
|
command: ({ editor, range }) => {
|
|
@@ -3125,7 +3915,7 @@ const NUMBERED_LIST = {
|
|
|
3125
3915
|
const QUOTE = {
|
|
3126
3916
|
title: "Quote",
|
|
3127
3917
|
description: "Block quote",
|
|
3128
|
-
icon: lucide_react.TextQuote,
|
|
3918
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.TextQuote, { size: 20 }),
|
|
3129
3919
|
category: "Text",
|
|
3130
3920
|
searchTerms: ["blockquote"],
|
|
3131
3921
|
command: ({ editor, range }) => {
|
|
@@ -3135,7 +3925,7 @@ const QUOTE = {
|
|
|
3135
3925
|
const CODE = {
|
|
3136
3926
|
title: "Code block",
|
|
3137
3927
|
description: "Code snippet",
|
|
3138
|
-
icon: lucide_react.SquareCode,
|
|
3928
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.SquareCode, { size: 20 }),
|
|
3139
3929
|
category: "Text",
|
|
3140
3930
|
searchTerms: ["codeblock"],
|
|
3141
3931
|
command: ({ editor, range }) => {
|
|
@@ -3145,7 +3935,7 @@ const CODE = {
|
|
|
3145
3935
|
const BUTTON = {
|
|
3146
3936
|
title: "Button",
|
|
3147
3937
|
description: "Clickable button",
|
|
3148
|
-
icon: lucide_react.MousePointer,
|
|
3938
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.MousePointer, { size: 20 }),
|
|
3149
3939
|
category: "Layout",
|
|
3150
3940
|
searchTerms: ["button"],
|
|
3151
3941
|
command: ({ editor, range }) => {
|
|
@@ -3155,7 +3945,7 @@ const BUTTON = {
|
|
|
3155
3945
|
const DIVIDER = {
|
|
3156
3946
|
title: "Divider",
|
|
3157
3947
|
description: "Horizontal separator",
|
|
3158
|
-
icon: lucide_react.SplitSquareVertical,
|
|
3948
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.SplitSquareVertical, { size: 20 }),
|
|
3159
3949
|
category: "Layout",
|
|
3160
3950
|
searchTerms: [
|
|
3161
3951
|
"hr",
|
|
@@ -3169,7 +3959,7 @@ const DIVIDER = {
|
|
|
3169
3959
|
const SECTION = {
|
|
3170
3960
|
title: "Section",
|
|
3171
3961
|
description: "Content section",
|
|
3172
|
-
icon: lucide_react.Rows2,
|
|
3962
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Rows2, { size: 20 }),
|
|
3173
3963
|
category: "Layout",
|
|
3174
3964
|
searchTerms: [
|
|
3175
3965
|
"section",
|
|
@@ -3183,7 +3973,7 @@ const SECTION = {
|
|
|
3183
3973
|
const TWO_COLUMNS = {
|
|
3184
3974
|
title: "2 columns",
|
|
3185
3975
|
description: "Two column layout",
|
|
3186
|
-
icon: lucide_react.Columns2,
|
|
3976
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Columns2, { size: 20 }),
|
|
3187
3977
|
category: "Layout",
|
|
3188
3978
|
searchTerms: [
|
|
3189
3979
|
"columns",
|
|
@@ -3204,7 +3994,7 @@ const TWO_COLUMNS = {
|
|
|
3204
3994
|
const THREE_COLUMNS = {
|
|
3205
3995
|
title: "3 columns",
|
|
3206
3996
|
description: "Three column layout",
|
|
3207
|
-
icon: lucide_react.Columns3,
|
|
3997
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Columns3, { size: 20 }),
|
|
3208
3998
|
category: "Layout",
|
|
3209
3999
|
searchTerms: [
|
|
3210
4000
|
"columns",
|
|
@@ -3224,7 +4014,7 @@ const THREE_COLUMNS = {
|
|
|
3224
4014
|
const FOUR_COLUMNS = {
|
|
3225
4015
|
title: "4 columns",
|
|
3226
4016
|
description: "Four column layout",
|
|
3227
|
-
icon: lucide_react.Columns4,
|
|
4017
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Columns4, { size: 20 }),
|
|
3228
4018
|
category: "Layout",
|
|
3229
4019
|
searchTerms: [
|
|
3230
4020
|
"columns",
|
|
@@ -3379,6 +4169,7 @@ const SlashCommand = createSlashCommand();
|
|
|
3379
4169
|
exports.AlignmentAttribute = AlignmentAttribute;
|
|
3380
4170
|
exports.BULLET_LIST = BULLET_LIST;
|
|
3381
4171
|
exports.BUTTON = BUTTON;
|
|
4172
|
+
exports.Blockquote = Blockquote;
|
|
3382
4173
|
exports.Body = Body;
|
|
3383
4174
|
exports.Bold = Bold;
|
|
3384
4175
|
exports.BubbleMenu = BubbleMenu;
|
|
@@ -3398,6 +4189,7 @@ exports.BubbleMenuSeparator = BubbleMenuSeparator;
|
|
|
3398
4189
|
exports.BubbleMenuStrike = BubbleMenuStrike;
|
|
3399
4190
|
exports.BubbleMenuUnderline = BubbleMenuUnderline;
|
|
3400
4191
|
exports.BubbleMenuUppercase = BubbleMenuUppercase;
|
|
4192
|
+
exports.BulletList = BulletList;
|
|
3401
4193
|
exports.Button = Button;
|
|
3402
4194
|
exports.ButtonBubbleMenu = ButtonBubbleMenu;
|
|
3403
4195
|
exports.ButtonBubbleMenuDefault = ButtonBubbleMenuDefault;
|
|
@@ -3407,6 +4199,7 @@ exports.ButtonBubbleMenuToolbar = ButtonBubbleMenuToolbar;
|
|
|
3407
4199
|
exports.CODE = CODE;
|
|
3408
4200
|
exports.COLUMN_PARENT_TYPES = COLUMN_PARENT_TYPES;
|
|
3409
4201
|
exports.ClassAttribute = ClassAttribute;
|
|
4202
|
+
exports.Code = Code;
|
|
3410
4203
|
exports.CodeBlockPrism = CodeBlockPrism;
|
|
3411
4204
|
exports.ColumnsColumn = ColumnsColumn;
|
|
3412
4205
|
exports.CommandList = CommandList;
|
|
@@ -3415,14 +4208,17 @@ exports.Div = Div;
|
|
|
3415
4208
|
exports.EmailNode = EmailNode;
|
|
3416
4209
|
exports.FOUR_COLUMNS = FOUR_COLUMNS;
|
|
3417
4210
|
exports.FourColumns = FourColumns;
|
|
4211
|
+
exports.GlobalContent = GlobalContent;
|
|
3418
4212
|
exports.H1 = H1;
|
|
3419
4213
|
exports.H2 = H2;
|
|
3420
4214
|
exports.H3 = H3;
|
|
4215
|
+
exports.HardBreak = HardBreak;
|
|
3421
4216
|
exports.ImageBubbleMenu = ImageBubbleMenu;
|
|
3422
4217
|
exports.ImageBubbleMenuDefault = ImageBubbleMenuDefault;
|
|
3423
4218
|
exports.ImageBubbleMenuEditLink = ImageBubbleMenuEditLink;
|
|
3424
4219
|
exports.ImageBubbleMenuRoot = ImageBubbleMenuRoot;
|
|
3425
4220
|
exports.ImageBubbleMenuToolbar = ImageBubbleMenuToolbar;
|
|
4221
|
+
exports.Italic = Italic;
|
|
3426
4222
|
exports.LinkBubbleMenu = LinkBubbleMenu;
|
|
3427
4223
|
exports.LinkBubbleMenuDefault = LinkBubbleMenuDefault;
|
|
3428
4224
|
exports.LinkBubbleMenuEditLink = LinkBubbleMenuEditLink;
|
|
@@ -3431,12 +4227,15 @@ exports.LinkBubbleMenuOpenLink = LinkBubbleMenuOpenLink;
|
|
|
3431
4227
|
exports.LinkBubbleMenuRoot = LinkBubbleMenuRoot;
|
|
3432
4228
|
exports.LinkBubbleMenuToolbar = LinkBubbleMenuToolbar;
|
|
3433
4229
|
exports.LinkBubbleMenuUnlink = LinkBubbleMenuUnlink;
|
|
4230
|
+
exports.ListItem = ListItem;
|
|
3434
4231
|
exports.MAX_COLUMNS_DEPTH = MAX_COLUMNS_DEPTH;
|
|
3435
4232
|
exports.MaxNesting = MaxNesting;
|
|
3436
4233
|
exports.NUMBERED_LIST = NUMBERED_LIST;
|
|
3437
4234
|
exports.NodeSelectorContent = NodeSelectorContent;
|
|
3438
4235
|
exports.NodeSelectorRoot = NodeSelectorRoot;
|
|
3439
4236
|
exports.NodeSelectorTrigger = NodeSelectorTrigger;
|
|
4237
|
+
exports.OrderedList = OrderedList;
|
|
4238
|
+
exports.Paragraph = Paragraph;
|
|
3440
4239
|
exports.Placeholder = Placeholder;
|
|
3441
4240
|
exports.PreservedStyle = PreservedStyle;
|
|
3442
4241
|
exports.PreviewText = PreviewText;
|
|
@@ -3444,6 +4243,8 @@ exports.QUOTE = QUOTE;
|
|
|
3444
4243
|
exports.SECTION = SECTION;
|
|
3445
4244
|
exports.Section = Section;
|
|
3446
4245
|
exports.SlashCommand = SlashCommand;
|
|
4246
|
+
exports.StarterKit = StarterKit;
|
|
4247
|
+
exports.Strike = Strike;
|
|
3447
4248
|
exports.StyleAttribute = StyleAttribute;
|
|
3448
4249
|
exports.Sup = Sup;
|
|
3449
4250
|
exports.TEXT = TEXT;
|
|
@@ -3456,17 +4257,19 @@ exports.TableRow = TableRow;
|
|
|
3456
4257
|
exports.ThreeColumns = ThreeColumns;
|
|
3457
4258
|
exports.TwoColumns = TwoColumns;
|
|
3458
4259
|
exports.Uppercase = Uppercase;
|
|
3459
|
-
exports.
|
|
4260
|
+
exports.composeReactEmail = composeReactEmail;
|
|
3460
4261
|
exports.createSlashCommand = createSlashCommand;
|
|
3461
4262
|
exports.defaultSlashCommands = defaultSlashCommands;
|
|
3462
4263
|
exports.editorEventBus = editorEventBus;
|
|
3463
4264
|
exports.filterAndRankItems = filterAndRankItems;
|
|
3464
4265
|
exports.getColumnsDepth = getColumnsDepth;
|
|
4266
|
+
exports.getGlobalContent = getGlobalContent;
|
|
3465
4267
|
exports.isAtMaxColumnsDepth = isAtMaxColumnsDepth;
|
|
3466
4268
|
exports.isInsideNode = isInsideNode;
|
|
3467
4269
|
exports.processStylesForUnlink = processStylesForUnlink;
|
|
3468
4270
|
exports.scoreItem = scoreItem;
|
|
3469
4271
|
exports.setTextAlignment = setTextAlignment;
|
|
3470
4272
|
exports.useButtonBubbleMenuContext = useButtonBubbleMenuContext;
|
|
4273
|
+
exports.useEditor = useEditor;
|
|
3471
4274
|
exports.useImageBubbleMenuContext = useImageBubbleMenuContext;
|
|
3472
4275
|
exports.useLinkBubbleMenuContext = useLinkBubbleMenuContext;
|