@react-email/editor 0.0.0-experimental.15 → 0.0.0-experimental.16

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 CHANGED
@@ -25,11 +25,21 @@ 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
+ let _tiptap_extension_blockquote = require("@tiptap/extension-blockquote");
38
+ _tiptap_extension_blockquote = __toESM(_tiptap_extension_blockquote);
39
+ let _tiptap_extension_bullet_list = require("@tiptap/extension-bullet-list");
40
+ _tiptap_extension_bullet_list = __toESM(_tiptap_extension_bullet_list);
41
+ let _tiptap_extension_code = require("@tiptap/extension-code");
42
+ _tiptap_extension_code = __toESM(_tiptap_extension_code);
33
43
  let _tiptap_extension_code_block = require("@tiptap/extension-code-block");
34
44
  _tiptap_extension_code_block = __toESM(_tiptap_extension_code_block);
35
45
  let _tiptap_pm_state = require("@tiptap/pm/state");
@@ -37,12 +47,22 @@ let _tiptap_pm_view = require("@tiptap/pm/view");
37
47
  let hast_util_from_html = require("hast-util-from-html");
38
48
  let prismjs = require("prismjs");
39
49
  prismjs = __toESM(prismjs);
50
+ let _tiptap_extension_hard_break = require("@tiptap/extension-hard-break");
51
+ _tiptap_extension_hard_break = __toESM(_tiptap_extension_hard_break);
52
+ let _tiptap_extension_italic = require("@tiptap/extension-italic");
53
+ _tiptap_extension_italic = __toESM(_tiptap_extension_italic);
54
+ let _tiptap_extension_list_item = require("@tiptap/extension-list-item");
55
+ _tiptap_extension_list_item = __toESM(_tiptap_extension_list_item);
56
+ let _tiptap_extension_ordered_list = require("@tiptap/extension-ordered-list");
57
+ _tiptap_extension_ordered_list = __toESM(_tiptap_extension_ordered_list);
58
+ let _tiptap_extension_paragraph = require("@tiptap/extension-paragraph");
59
+ _tiptap_extension_paragraph = __toESM(_tiptap_extension_paragraph);
40
60
  let _tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
41
61
  _tiptap_extension_placeholder = __toESM(_tiptap_extension_placeholder);
42
- let _tiptap_react = require("@tiptap/react");
62
+ let _tiptap_extension_strike = require("@tiptap/extension-strike");
63
+ _tiptap_extension_strike = __toESM(_tiptap_extension_strike);
64
+ let _tiptap_html = require("@tiptap/html");
43
65
  let lucide_react = require("lucide-react");
44
- let react = require("react");
45
- react = __toESM(react);
46
66
  let _radix_ui_react_popover = require("@radix-ui/react-popover");
47
67
  _radix_ui_react_popover = __toESM(_radix_ui_react_popover);
48
68
  let _tiptap_react_menus = require("@tiptap/react/menus");
@@ -51,37 +71,6 @@ _tiptap_suggestion = __toESM(_tiptap_suggestion);
51
71
  let tippy_js = require("tippy.js");
52
72
  tippy_js = __toESM(tippy_js);
53
73
 
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
74
  //#region src/core/event-bus.ts
86
75
  const EVENT_PREFIX = "@react-email/editor:";
87
76
  var EditorEventBus = class {
@@ -123,139 +112,6 @@ var EditorEventBus = class {
123
112
  };
124
113
  const editorEventBus = new EditorEventBus();
125
114
 
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
115
  //#endregion
260
116
  //#region src/utils/styles.ts
261
117
  const WHITE_SPACE_REGEX = /\s+/;
@@ -425,6 +281,342 @@ function resolveConflictingStyles(resetStyles, inlineStyles) {
425
281
  };
426
282
  }
427
283
 
284
+ //#endregion
285
+ //#region src/core/serializer/default-base-template.tsx
286
+ function DefaultBaseTemplate({ children, previewText }) {
287
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_email_components.Html, { children: [
288
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_email_components.Head, { children: [
289
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
290
+ content: "width=device-width",
291
+ name: "viewport"
292
+ }),
293
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
294
+ content: "IE=edge",
295
+ httpEquiv: "X-UA-Compatible"
296
+ }),
297
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", { name: "x-apple-disable-message-reformatting" }),
298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
299
+ content: "telephone=no,address=no,email=no,date=no,url=no",
300
+ name: "format-detection"
301
+ })
302
+ ] }),
303
+ previewText && previewText !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Preview, { children: previewText }),
304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Body, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
305
+ width: "100%",
306
+ align: "center",
307
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
308
+ style: { width: "100%" },
309
+ children
310
+ })
311
+ }) })
312
+ ] });
313
+ }
314
+
315
+ //#endregion
316
+ //#region src/core/serializer/email-mark.ts
317
+ var EmailMark = class EmailMark extends _tiptap_core.Mark {
318
+ constructor(config) {
319
+ super(config);
320
+ }
321
+ /**
322
+ * Create a new Mark instance
323
+ * @param config - Mark configuration object or a function that returns a configuration object
324
+ */
325
+ static create(config) {
326
+ return new EmailMark(typeof config === "function" ? config() : config);
327
+ }
328
+ static from(mark, renderToReactEmail) {
329
+ const customMark = EmailMark.create({});
330
+ Object.assign(customMark, { ...mark });
331
+ customMark.config = {
332
+ ...mark.config,
333
+ renderToReactEmail
334
+ };
335
+ return customMark;
336
+ }
337
+ configure(options) {
338
+ return super.configure(options);
339
+ }
340
+ extend(extendedConfig) {
341
+ const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
342
+ return super.extend(resolvedConfig);
343
+ }
344
+ };
345
+
346
+ //#endregion
347
+ //#region src/core/serializer/email-node.ts
348
+ var EmailNode = class EmailNode extends _tiptap_core.Node {
349
+ constructor(config) {
350
+ super(config);
351
+ }
352
+ /**
353
+ * Create a new Node instance
354
+ * @param config - Node configuration object or a function that returns a configuration object
355
+ */
356
+ static create(config) {
357
+ return new EmailNode(typeof config === "function" ? config() : config);
358
+ }
359
+ static from(node, renderToReactEmail) {
360
+ const customNode = EmailNode.create({});
361
+ Object.assign(customNode, { ...node });
362
+ customNode.config = {
363
+ ...node.config,
364
+ renderToReactEmail
365
+ };
366
+ return customNode;
367
+ }
368
+ configure(options) {
369
+ return super.configure(options);
370
+ }
371
+ extend(extendedConfig) {
372
+ const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
373
+ return super.extend(resolvedConfig);
374
+ }
375
+ };
376
+
377
+ //#endregion
378
+ //#region src/core/serializer/compose-react-email.tsx
379
+ const MARK_ORDER = {
380
+ preservedStyle: 0,
381
+ italic: 1,
382
+ strike: 2,
383
+ underline: 3,
384
+ link: 4,
385
+ bold: 5,
386
+ code: 6
387
+ };
388
+ const NODES_WITH_INCREMENTED_CHILD_DEPTH = new Set(["bulletList", "orderedList"]);
389
+ function getOrderedMarks(marks) {
390
+ if (!marks) return [];
391
+ return [...marks].sort((a, b) => (MARK_ORDER[a.type] ?? Number.MAX_SAFE_INTEGER) - (MARK_ORDER[b.type] ?? Number.MAX_SAFE_INTEGER));
392
+ }
393
+ const composeReactEmail = async ({ editor, preview }) => {
394
+ const data = editor.getJSON();
395
+ const extensions = editor.extensionManager.extensions;
396
+ const serializerPlugin = extensions.map((ext) => ext.options?.serializerPlugin).filter((p) => Boolean(p)).at(-1);
397
+ const emailNodeComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailNode).map((extension) => [extension.name, extension.config.renderToReactEmail]));
398
+ const emailMarkComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailMark).map((extension) => [extension.name, extension.config.renderToReactEmail]));
399
+ function renderMark(mark, node, children, depth) {
400
+ const markStyle = serializerPlugin?.getNodeStyles({
401
+ type: mark.type,
402
+ attrs: mark.attrs ?? {}
403
+ }, depth, editor) ?? {};
404
+ const markRenderer = emailMarkComponentRegistry[mark.type];
405
+ if (markRenderer) return markRenderer({
406
+ mark,
407
+ node,
408
+ style: markStyle,
409
+ children
410
+ });
411
+ return children;
412
+ }
413
+ function parseContent(content, depth = 0) {
414
+ if (!content) return;
415
+ return content.map((node, index) => {
416
+ const style = serializerPlugin?.getNodeStyles(node, depth, editor) ?? {};
417
+ const inlineStyles = inlineCssToJs(node.attrs?.style);
418
+ if (node.type && emailNodeComponentRegistry[node.type]) {
419
+ const Component = emailNodeComponentRegistry[node.type];
420
+ const childDepth = NODES_WITH_INCREMENTED_CHILD_DEPTH.has(node.type) ? depth + 1 : depth;
421
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
422
+ node: node.type === "table" && inlineStyles.width && !node.attrs?.width ? {
423
+ ...node,
424
+ attrs: {
425
+ ...node.attrs,
426
+ width: inlineStyles.width
427
+ }
428
+ } : node,
429
+ style,
430
+ children: parseContent(node.content, childDepth)
431
+ }, index);
432
+ }
433
+ switch (node.type) {
434
+ case "text": {
435
+ let wrappedText = node.text;
436
+ getOrderedMarks(node.marks).forEach((mark) => {
437
+ wrappedText = renderMark(mark, node, wrappedText, depth);
438
+ });
439
+ const textAttributes = node.marks?.find((mark) => mark.type === "textStyle")?.attrs;
440
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
441
+ style: {
442
+ ...textAttributes,
443
+ ...style
444
+ },
445
+ children: wrappedText
446
+ }, index);
447
+ }
448
+ default: return null;
449
+ }
450
+ });
451
+ }
452
+ const unformattedHtml = await (0, _react_email_components.render)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(serializerPlugin?.BaseTemplate ?? DefaultBaseTemplate, {
453
+ previewText: preview,
454
+ editor,
455
+ children: parseContent(data.content)
456
+ }));
457
+ const [prettyHtml, text] = await Promise.all([(0, _react_email_components.pretty)(unformattedHtml), (0, _react_email_components.toPlainText)(unformattedHtml)]);
458
+ return {
459
+ html: prettyHtml,
460
+ text
461
+ };
462
+ };
463
+
464
+ //#endregion
465
+ //#region src/extensions/alignment-attribute.tsx
466
+ const AlignmentAttribute = _tiptap_core.Extension.create({
467
+ name: "alignmentAttribute",
468
+ addOptions() {
469
+ return {
470
+ types: [],
471
+ alignments: [
472
+ "left",
473
+ "center",
474
+ "right",
475
+ "justify"
476
+ ]
477
+ };
478
+ },
479
+ addGlobalAttributes() {
480
+ return [{
481
+ types: this.options.types,
482
+ attributes: { alignment: {
483
+ parseHTML: (element) => {
484
+ const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
485
+ if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
486
+ return null;
487
+ },
488
+ renderHTML: (attributes) => {
489
+ if (attributes.alignment === "left") return {};
490
+ return { alignment: attributes.alignment };
491
+ }
492
+ } }
493
+ }];
494
+ },
495
+ addCommands() {
496
+ return { setAlignment: (alignment) => ({ commands }) => {
497
+ if (!this.options.alignments.includes(alignment)) return false;
498
+ return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
499
+ } };
500
+ },
501
+ addKeyboardShortcuts() {
502
+ return {
503
+ Enter: () => {
504
+ const { from } = this.editor.state.selection;
505
+ const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
506
+ if (currentAlignment) requestAnimationFrame(() => {
507
+ this.editor.commands.setAlignment(currentAlignment);
508
+ });
509
+ return false;
510
+ },
511
+ "Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
512
+ "Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
513
+ "Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
514
+ "Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
515
+ };
516
+ }
517
+ });
518
+
519
+ //#endregion
520
+ //#region src/utils/get-text-alignment.ts
521
+ function getTextAlignment(alignment) {
522
+ switch (alignment) {
523
+ case "left": return { textAlign: "left" };
524
+ case "center": return { textAlign: "center" };
525
+ case "right": return { textAlign: "right" };
526
+ default: return {};
527
+ }
528
+ }
529
+
530
+ //#endregion
531
+ //#region src/extensions/blockquote.tsx
532
+ const Blockquote = EmailNode.from(_tiptap_extension_blockquote.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("blockquote", {
533
+ className: node.attrs?.class || void 0,
534
+ style: {
535
+ ...style,
536
+ ...inlineCssToJs(node.attrs?.style),
537
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
538
+ },
539
+ children
540
+ }));
541
+
542
+ //#endregion
543
+ //#region src/utils/attribute-helpers.ts
544
+ /**
545
+ * Creates TipTap attribute definitions for a list of HTML attributes.
546
+ * Each attribute will have the same pattern:
547
+ * - default: null
548
+ * - parseHTML: extracts the attribute from the element
549
+ * - renderHTML: conditionally renders the attribute if it has a value
550
+ *
551
+ * @param attributeNames - Array of HTML attribute names to create definitions for
552
+ * @returns Object with TipTap attribute definitions
553
+ *
554
+ * @example
555
+ * const attrs = createStandardAttributes(['class', 'id', 'title']);
556
+ * // Returns:
557
+ * // {
558
+ * // class: {
559
+ * // default: null,
560
+ * // parseHTML: (element) => element.getAttribute('class'),
561
+ * // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
562
+ * // },
563
+ * // ...
564
+ * // }
565
+ */
566
+ function createStandardAttributes(attributeNames) {
567
+ return Object.fromEntries(attributeNames.map((attr) => [attr, {
568
+ default: null,
569
+ parseHTML: (element) => element.getAttribute(attr),
570
+ renderHTML: (attributes) => {
571
+ if (!attributes[attr]) return {};
572
+ return { [attr]: attributes[attr] };
573
+ }
574
+ }]));
575
+ }
576
+ /**
577
+ * Common HTML attributes used across multiple extensions.
578
+ * These preserve attributes during HTML import and editing for better
579
+ * fidelity when importing existing email templates.
580
+ */
581
+ const COMMON_HTML_ATTRIBUTES = [
582
+ "id",
583
+ "class",
584
+ "title",
585
+ "lang",
586
+ "dir",
587
+ "data-id"
588
+ ];
589
+ /**
590
+ * Layout-specific HTML attributes used for positioning and sizing.
591
+ */
592
+ const LAYOUT_ATTRIBUTES = [
593
+ "align",
594
+ "width",
595
+ "height"
596
+ ];
597
+ /**
598
+ * Table-specific HTML attributes used for table layout and styling.
599
+ */
600
+ const TABLE_ATTRIBUTES = [
601
+ "border",
602
+ "cellpadding",
603
+ "cellspacing"
604
+ ];
605
+ /**
606
+ * Table cell-specific HTML attributes.
607
+ */
608
+ const TABLE_CELL_ATTRIBUTES = [
609
+ "valign",
610
+ "bgcolor",
611
+ "colspan",
612
+ "rowspan"
613
+ ];
614
+ /**
615
+ * Table header cell-specific HTML attributes.
616
+ * These are additional attributes that only apply to <th> elements.
617
+ */
618
+ const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
619
+
428
620
  //#endregion
429
621
  //#region src/extensions/body.tsx
430
622
  const Body = EmailNode.create({
@@ -471,7 +663,7 @@ const Body = EmailNode.create({
471
663
  });
472
664
 
473
665
  //#endregion
474
- //#region src/extensions/bold.ts
666
+ //#region src/extensions/bold.tsx
475
667
  /**
476
668
  * Matches bold text via `**` as input.
477
669
  */
@@ -492,7 +684,7 @@ const underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
492
684
  * This extension allows you to mark text as bold.
493
685
  * @see https://tiptap.dev/api/marks/bold
494
686
  */
495
- const Bold = _tiptap_core.Mark.create({
687
+ const Bold = EmailMark.create({
496
688
  name: "bold",
497
689
  addOptions() {
498
690
  return { HTMLAttributes: {} };
@@ -517,6 +709,12 @@ const Bold = _tiptap_core.Mark.create({
517
709
  0
518
710
  ];
519
711
  },
712
+ renderToReactEmail({ children, style }) {
713
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
714
+ style,
715
+ children
716
+ });
717
+ },
520
718
  addCommands() {
521
719
  return {
522
720
  setBold: () => ({ commands }) => {
@@ -556,6 +754,17 @@ const Bold = _tiptap_core.Mark.create({
556
754
  }
557
755
  });
558
756
 
757
+ //#endregion
758
+ //#region src/extensions/bullet-list.tsx
759
+ const BulletList = EmailNode.from(_tiptap_extension_bullet_list.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
760
+ className: node.attrs?.class || void 0,
761
+ style: {
762
+ ...style,
763
+ ...inlineCssToJs(node.attrs?.style)
764
+ },
765
+ children
766
+ }));
767
+
559
768
  //#endregion
560
769
  //#region src/extensions/button.tsx
561
770
  const Button = EmailNode.create({
@@ -677,6 +886,16 @@ const ClassAttribute = _tiptap_core.Extension.create({
677
886
  }
678
887
  });
679
888
 
889
+ //#endregion
890
+ //#region src/extensions/code.tsx
891
+ const Code = EmailMark.from(_tiptap_extension_code.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
892
+ style: {
893
+ ...style,
894
+ ...inlineCssToJs(node.attrs?.style)
895
+ },
896
+ children
897
+ }));
898
+
680
899
  //#endregion
681
900
  //#region src/utils/prism-utils.ts
682
901
  const publicURL = "/styles/prism";
@@ -955,6 +1174,29 @@ const Div = EmailNode.create({
955
1174
  }
956
1175
  });
957
1176
 
1177
+ //#endregion
1178
+ //#region src/extensions/hard-break.tsx
1179
+ const HardBreak = EmailNode.from(_tiptap_extension_hard_break.default, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}));
1180
+
1181
+ //#endregion
1182
+ //#region src/extensions/italic.tsx
1183
+ const Italic = EmailMark.from(_tiptap_extension_italic.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", {
1184
+ style,
1185
+ children
1186
+ }));
1187
+
1188
+ //#endregion
1189
+ //#region src/extensions/list-item.tsx
1190
+ const ListItem = EmailNode.from(_tiptap_extension_list_item.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
1191
+ className: node.attrs?.class || void 0,
1192
+ style: {
1193
+ ...style,
1194
+ ...inlineCssToJs(node.attrs?.style),
1195
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
1196
+ },
1197
+ children
1198
+ }));
1199
+
958
1200
  //#endregion
959
1201
  //#region src/extensions/max-nesting.ts
960
1202
  const MaxNesting = _tiptap_core.Extension.create({
@@ -1030,6 +1272,33 @@ const MaxNesting = _tiptap_core.Extension.create({
1030
1272
  }
1031
1273
  });
1032
1274
 
1275
+ //#endregion
1276
+ //#region src/extensions/ordered-list.tsx
1277
+ const OrderedList = EmailNode.from(_tiptap_extension_ordered_list.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
1278
+ className: node.attrs?.class || void 0,
1279
+ start: node.attrs?.start,
1280
+ style: {
1281
+ ...style,
1282
+ ...inlineCssToJs(node.attrs?.style)
1283
+ },
1284
+ children
1285
+ }));
1286
+
1287
+ //#endregion
1288
+ //#region src/extensions/paragraph.tsx
1289
+ const Paragraph = EmailNode.from(_tiptap_extension_paragraph.default, ({ children, node, style }) => {
1290
+ const isEmpty = !node.content || node.content.length === 0;
1291
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1292
+ className: node.attrs?.class || void 0,
1293
+ style: {
1294
+ ...style,
1295
+ ...inlineCssToJs(node.attrs?.style),
1296
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
1297
+ },
1298
+ children: isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}) : children
1299
+ });
1300
+ });
1301
+
1033
1302
  //#endregion
1034
1303
  //#region src/extensions/placeholder.ts
1035
1304
  const Placeholder = _tiptap_extension_placeholder.default.configure({
@@ -1041,8 +1310,8 @@ const Placeholder = _tiptap_extension_placeholder.default.configure({
1041
1310
  });
1042
1311
 
1043
1312
  //#endregion
1044
- //#region src/extensions/preserved-style.ts
1045
- const PreservedStyle = _tiptap_core.Mark.create({
1313
+ //#region src/extensions/preserved-style.tsx
1314
+ const PreservedStyle = EmailMark.create({
1046
1315
  name: "preservedStyle",
1047
1316
  addAttributes() {
1048
1317
  return { style: {
@@ -1071,6 +1340,12 @@ const PreservedStyle = _tiptap_core.Mark.create({
1071
1340
  (0, _tiptap_core.mergeAttributes)(HTMLAttributes),
1072
1341
  0
1073
1342
  ];
1343
+ },
1344
+ renderToReactEmail({ children, mark }) {
1345
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1346
+ style: mark.attrs?.style ? inlineCssToJs(mark.attrs.style) : void 0,
1347
+ children
1348
+ });
1074
1349
  }
1075
1350
  });
1076
1351
  const LINK_INDICATOR_STYLES = [
@@ -1155,17 +1430,6 @@ const PreviewText = _tiptap_core.Node.create({
1155
1430
  }
1156
1431
  });
1157
1432
 
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
1433
  //#endregion
1170
1434
  //#region src/extensions/section.tsx
1171
1435
  const Section = EmailNode.create({
@@ -1214,6 +1478,13 @@ const Section = EmailNode.create({
1214
1478
  }
1215
1479
  });
1216
1480
 
1481
+ //#endregion
1482
+ //#region src/extensions/strike.tsx
1483
+ const Strike = EmailMark.from(_tiptap_extension_strike.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("s", {
1484
+ style,
1485
+ children
1486
+ }));
1487
+
1217
1488
  //#endregion
1218
1489
  //#region src/extensions/style-attribute.tsx
1219
1490
  const StyleAttribute = _tiptap_core.Extension.create({
@@ -1263,12 +1534,12 @@ const StyleAttribute = _tiptap_core.Extension.create({
1263
1534
  });
1264
1535
 
1265
1536
  //#endregion
1266
- //#region src/extensions/sup.ts
1537
+ //#region src/extensions/sup.tsx
1267
1538
  /**
1268
1539
  * This extension allows you to mark text as superscript.
1269
1540
  * @see https://tiptap.dev/api/marks/superscript
1270
1541
  */
1271
- const Sup = _tiptap_core.Mark.create({
1542
+ const Sup = EmailMark.create({
1272
1543
  name: "sup",
1273
1544
  addOptions() {
1274
1545
  return { HTMLAttributes: {} };
@@ -1283,6 +1554,12 @@ const Sup = _tiptap_core.Mark.create({
1283
1554
  0
1284
1555
  ];
1285
1556
  },
1557
+ renderToReactEmail({ children, style }) {
1558
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("sup", {
1559
+ style,
1560
+ children
1561
+ });
1562
+ },
1286
1563
  addCommands() {
1287
1564
  return {
1288
1565
  setSup: () => ({ commands }) => {
@@ -1485,8 +1762,8 @@ const TableHeader = _tiptap_core.Node.create({
1485
1762
  });
1486
1763
 
1487
1764
  //#endregion
1488
- //#region src/extensions/uppercase.ts
1489
- const Uppercase = _tiptap_core.Mark.create({
1765
+ //#region src/extensions/uppercase.tsx
1766
+ const Uppercase = EmailMark.create({
1490
1767
  name: "uppercase",
1491
1768
  addOptions() {
1492
1769
  return { HTMLAttributes: {} };
@@ -1507,6 +1784,15 @@ const Uppercase = _tiptap_core.Mark.create({
1507
1784
  0
1508
1785
  ];
1509
1786
  },
1787
+ renderToReactEmail({ children, style }) {
1788
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1789
+ style: {
1790
+ ...style,
1791
+ textTransform: "uppercase"
1792
+ },
1793
+ children
1794
+ });
1795
+ },
1510
1796
  addCommands() {
1511
1797
  return {
1512
1798
  setUppercase: () => ({ commands }) => {
@@ -1702,17 +1988,17 @@ const coreExtensions = [
1702
1988
  underline: false,
1703
1989
  trailingNode: false,
1704
1990
  bold: false,
1991
+ italic: false,
1992
+ strike: false,
1993
+ code: false,
1994
+ paragraph: false,
1995
+ bulletList: false,
1996
+ orderedList: false,
1997
+ listItem: false,
1998
+ blockquote: false,
1999
+ hardBreak: false,
1705
2000
  gapcursor: false,
1706
- listItem: {},
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
2001
  codeBlock: false,
1712
- code: { HTMLAttributes: {
1713
- class: "node-inlineCode",
1714
- spellcheck: "false"
1715
- } },
1716
2002
  horizontalRule: false,
1717
2003
  dropcursor: {
1718
2004
  color: "#61a8f8",
@@ -1724,9 +2010,21 @@ const coreExtensions = [
1724
2010
  defaultLanguage: "javascript",
1725
2011
  HTMLAttributes: { class: "prism node-codeBlock" }
1726
2012
  }),
2013
+ Code.configure({ HTMLAttributes: {
2014
+ class: "node-inlineCode",
2015
+ spellcheck: "false"
2016
+ } }),
2017
+ Paragraph.configure({ HTMLAttributes: { class: "node-paragraph" } }),
2018
+ BulletList.configure({ HTMLAttributes: { class: "node-bulletList" } }),
2019
+ OrderedList.configure({ HTMLAttributes: { class: "node-orderedList" } }),
2020
+ Blockquote.configure({ HTMLAttributes: { class: "node-blockquote" } }),
2021
+ ListItem,
2022
+ HardBreak,
2023
+ Italic,
1727
2024
  Placeholder,
1728
2025
  PreviewText,
1729
2026
  Bold,
2027
+ Strike,
1730
2028
  Sup,
1731
2029
  Uppercase,
1732
2030
  PreservedStyle,
@@ -1813,6 +2111,221 @@ const coreExtensions = [
1813
2111
  })
1814
2112
  ];
1815
2113
 
2114
+ //#endregion
2115
+ //#region src/core/create-drop-handler.ts
2116
+ function createDropHandler({ onPaste, onUploadImage }) {
2117
+ return (view, event, _slice, moved) => {
2118
+ if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
2119
+ event.preventDefault();
2120
+ const file = event.dataTransfer.files[0];
2121
+ if (onPaste?.(file, view)) return true;
2122
+ if (file.type.includes("image/") && onUploadImage) {
2123
+ onUploadImage(file, view, (view.posAtCoords({
2124
+ left: event.clientX,
2125
+ top: event.clientY
2126
+ })?.pos || 0) - 1);
2127
+ return true;
2128
+ }
2129
+ }
2130
+ return false;
2131
+ };
2132
+ }
2133
+
2134
+ //#endregion
2135
+ //#region src/utils/paste-sanitizer.ts
2136
+ /**
2137
+ * Sanitizes pasted HTML.
2138
+ * - From editor (has node-* classes): pass through as-is
2139
+ * - From external: strip all styles/classes, keep only semantic HTML
2140
+ */
2141
+ /**
2142
+ * Detects content from the Resend editor by checking for node-* class names.
2143
+ */
2144
+ const EDITOR_CLASS_PATTERN = /class="[^"]*node-/;
2145
+ /**
2146
+ * Attributes to preserve on specific elements for EXTERNAL content.
2147
+ * Only functional attributes - NO style or class.
2148
+ */
2149
+ const PRESERVED_ATTRIBUTES = {
2150
+ a: [
2151
+ "href",
2152
+ "target",
2153
+ "rel"
2154
+ ],
2155
+ img: [
2156
+ "src",
2157
+ "alt",
2158
+ "width",
2159
+ "height"
2160
+ ],
2161
+ td: ["colspan", "rowspan"],
2162
+ th: [
2163
+ "colspan",
2164
+ "rowspan",
2165
+ "scope"
2166
+ ],
2167
+ table: [
2168
+ "border",
2169
+ "cellpadding",
2170
+ "cellspacing"
2171
+ ],
2172
+ "*": ["id"]
2173
+ };
2174
+ function isFromEditor(html) {
2175
+ return EDITOR_CLASS_PATTERN.test(html);
2176
+ }
2177
+ function sanitizePastedHtml(html) {
2178
+ if (isFromEditor(html)) return html;
2179
+ const doc = new DOMParser().parseFromString(html, "text/html");
2180
+ sanitizeNode(doc.body);
2181
+ return doc.body.innerHTML;
2182
+ }
2183
+ function sanitizeNode(node) {
2184
+ if (node.nodeType === Node.ELEMENT_NODE) sanitizeElement(node);
2185
+ for (const child of Array.from(node.childNodes)) sanitizeNode(child);
2186
+ }
2187
+ function sanitizeElement(el) {
2188
+ const allowedForTag = PRESERVED_ATTRIBUTES[el.tagName.toLowerCase()] || [];
2189
+ const allowedGlobal = PRESERVED_ATTRIBUTES["*"] || [];
2190
+ const allowed = new Set([...allowedForTag, ...allowedGlobal]);
2191
+ const attributesToRemove = [];
2192
+ for (const attr of Array.from(el.attributes)) {
2193
+ if (attr.name.startsWith("data-")) {
2194
+ attributesToRemove.push(attr.name);
2195
+ continue;
2196
+ }
2197
+ if (!allowed.has(attr.name)) attributesToRemove.push(attr.name);
2198
+ }
2199
+ for (const attr of attributesToRemove) el.removeAttribute(attr);
2200
+ }
2201
+
2202
+ //#endregion
2203
+ //#region src/core/create-paste-handler.ts
2204
+ function createPasteHandler({ onPaste, onUploadImage, extensions }) {
2205
+ return (view, event, slice) => {
2206
+ const text = event.clipboardData?.getData("text/plain");
2207
+ if (text && onPaste?.(text, view)) {
2208
+ event.preventDefault();
2209
+ return true;
2210
+ }
2211
+ if (event.clipboardData?.files?.[0]) {
2212
+ const file = event.clipboardData.files[0];
2213
+ if (onPaste?.(file, view)) {
2214
+ event.preventDefault();
2215
+ return true;
2216
+ }
2217
+ if (file.type.includes("image/") && onUploadImage) {
2218
+ const pos = view.state.selection.from;
2219
+ onUploadImage(file, view, pos);
2220
+ return true;
2221
+ }
2222
+ }
2223
+ /**
2224
+ * If the coming content has a single child, we can assume
2225
+ * it's a plain text and doesn't need to be parsed and
2226
+ * be introduced in a new line
2227
+ */
2228
+ if (slice.content.childCount === 1) return false;
2229
+ if (event.clipboardData?.getData?.("text/html")) {
2230
+ event.preventDefault();
2231
+ const jsonContent = (0, _tiptap_html.generateJSON)(sanitizePastedHtml(event.clipboardData.getData("text/html")), extensions);
2232
+ const node = view.state.schema.nodeFromJSON(jsonContent);
2233
+ const transaction = view.state.tr.replaceSelectionWith(node, false);
2234
+ view.dispatch(transaction);
2235
+ return true;
2236
+ }
2237
+ return false;
2238
+ };
2239
+ }
2240
+
2241
+ //#endregion
2242
+ //#region src/core/is-document-visually-empty.ts
2243
+ function isDocumentVisuallyEmpty(doc) {
2244
+ let nonGlobalNodeCount = 0;
2245
+ let firstNonGlobalNode = null;
2246
+ for (let index = 0; index < doc.childCount; index += 1) {
2247
+ const node = doc.child(index);
2248
+ if (node.type.name === "globalContent") continue;
2249
+ nonGlobalNodeCount += 1;
2250
+ if (firstNonGlobalNode === null) firstNonGlobalNode = {
2251
+ type: node.type,
2252
+ textContent: node.textContent,
2253
+ childCount: node.content.childCount
2254
+ };
2255
+ }
2256
+ if (nonGlobalNodeCount === 0) return true;
2257
+ if (nonGlobalNodeCount !== 1) return false;
2258
+ return firstNonGlobalNode?.type.name === "paragraph" && firstNonGlobalNode.textContent.trim().length === 0 && firstNonGlobalNode.childCount === 0;
2259
+ }
2260
+
2261
+ //#endregion
2262
+ //#region src/core/use-editor.ts
2263
+ const COLLABORATION_EXTENSION_NAMES = new Set(["liveblocksExtension", "collaboration"]);
2264
+ function hasCollaborationExtension(exts) {
2265
+ return exts.some((ext) => COLLABORATION_EXTENSION_NAMES.has(ext.name));
2266
+ }
2267
+ function useEditor({ content, extensions = [], onUpdate, onPaste, onUploadImage, onReady, editable = true, ...rest }) {
2268
+ const [contentError, setContentError] = react.useState(null);
2269
+ const isCollaborative = hasCollaborationExtension(extensions);
2270
+ const effectiveExtensions = react.useMemo(() => [
2271
+ ...coreExtensions,
2272
+ ...isCollaborative ? [] : [_tiptap_extensions.UndoRedo],
2273
+ ...extensions
2274
+ ], [extensions, isCollaborative]);
2275
+ const editor = (0, _tiptap_react.useEditor)({
2276
+ content: isCollaborative ? void 0 : content,
2277
+ extensions: effectiveExtensions,
2278
+ immediatelyRender: false,
2279
+ enableContentCheck: true,
2280
+ onContentError({ editor: editor$1, error, disableCollaboration }) {
2281
+ disableCollaboration();
2282
+ setContentError(error);
2283
+ console.error(error);
2284
+ editor$1.setEditable(false);
2285
+ },
2286
+ onCreate({ editor: editor$1 }) {
2287
+ onReady?.(editor$1);
2288
+ },
2289
+ onUpdate({ editor: editor$1, transaction }) {
2290
+ onUpdate?.(editor$1, transaction);
2291
+ },
2292
+ editorProps: {
2293
+ handleDOMEvents: { click: (view, event) => {
2294
+ if (!view.editable) {
2295
+ if (event.target.closest("a")) {
2296
+ event.preventDefault();
2297
+ return true;
2298
+ }
2299
+ }
2300
+ return false;
2301
+ } },
2302
+ handlePaste: createPasteHandler({
2303
+ onPaste,
2304
+ onUploadImage,
2305
+ extensions: effectiveExtensions
2306
+ }),
2307
+ handleDrop: createDropHandler({
2308
+ onPaste,
2309
+ onUploadImage
2310
+ })
2311
+ },
2312
+ ...rest
2313
+ });
2314
+ return {
2315
+ editor,
2316
+ isEditorEmpty: (0, _tiptap_react.useEditorState)({
2317
+ editor,
2318
+ selector: (context) => {
2319
+ if (!context.editor) return true;
2320
+ return isDocumentVisuallyEmpty(context.editor.state.doc);
2321
+ }
2322
+ }) ?? true,
2323
+ extensions: effectiveExtensions,
2324
+ contentError,
2325
+ isCollaborative
2326
+ };
2327
+ }
2328
+
1816
2329
  //#endregion
1817
2330
  //#region src/utils/set-text-alignment.ts
1818
2331
  function setTextAlignment(editor, alignment) {
@@ -2967,13 +3480,12 @@ function groupByCategory(items) {
2967
3480
  return ordered;
2968
3481
  }
2969
3482
  function CommandItem({ item, selected, onSelect }) {
2970
- const Icon = item.icon;
2971
3483
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2972
3484
  "data-re-slash-command-item": "",
2973
3485
  "data-selected": selected || void 0,
2974
3486
  onClick: onSelect,
2975
3487
  type: "button",
2976
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 20 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.title })]
3488
+ children: [item.icon, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.title })]
2977
3489
  });
2978
3490
  }
2979
3491
  function CommandList({ items, command, query, ref }) {
@@ -3048,11 +3560,11 @@ function CommandList({ items, command, query, ref }) {
3048
3560
  }
3049
3561
 
3050
3562
  //#endregion
3051
- //#region src/ui/slash-command/commands.ts
3563
+ //#region src/ui/slash-command/commands.tsx
3052
3564
  const TEXT = {
3053
3565
  title: "Text",
3054
3566
  description: "Plain text block",
3055
- icon: lucide_react.Text,
3567
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Text, { size: 20 }),
3056
3568
  category: "Text",
3057
3569
  searchTerms: ["p", "paragraph"],
3058
3570
  command: ({ editor, range }) => {
@@ -3062,7 +3574,7 @@ const TEXT = {
3062
3574
  const H1 = {
3063
3575
  title: "Title",
3064
3576
  description: "Large heading",
3065
- icon: lucide_react.Heading1,
3577
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Heading1, { size: 20 }),
3066
3578
  category: "Text",
3067
3579
  searchTerms: [
3068
3580
  "title",
@@ -3077,7 +3589,7 @@ const H1 = {
3077
3589
  const H2 = {
3078
3590
  title: "Subtitle",
3079
3591
  description: "Medium heading",
3080
- icon: lucide_react.Heading2,
3592
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Heading2, { size: 20 }),
3081
3593
  category: "Text",
3082
3594
  searchTerms: [
3083
3595
  "subtitle",
@@ -3091,7 +3603,7 @@ const H2 = {
3091
3603
  const H3 = {
3092
3604
  title: "Heading",
3093
3605
  description: "Small heading",
3094
- icon: lucide_react.Heading3,
3606
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Heading3, { size: 20 }),
3095
3607
  category: "Text",
3096
3608
  searchTerms: [
3097
3609
  "subtitle",
@@ -3105,7 +3617,7 @@ const H3 = {
3105
3617
  const BULLET_LIST = {
3106
3618
  title: "Bullet list",
3107
3619
  description: "Unordered list",
3108
- icon: lucide_react.List,
3620
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.List, { size: 20 }),
3109
3621
  category: "Text",
3110
3622
  searchTerms: ["unordered", "point"],
3111
3623
  command: ({ editor, range }) => {
@@ -3115,7 +3627,7 @@ const BULLET_LIST = {
3115
3627
  const NUMBERED_LIST = {
3116
3628
  title: "Numbered list",
3117
3629
  description: "Ordered list",
3118
- icon: lucide_react.ListOrdered,
3630
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ListOrdered, { size: 20 }),
3119
3631
  category: "Text",
3120
3632
  searchTerms: ["ordered"],
3121
3633
  command: ({ editor, range }) => {
@@ -3125,7 +3637,7 @@ const NUMBERED_LIST = {
3125
3637
  const QUOTE = {
3126
3638
  title: "Quote",
3127
3639
  description: "Block quote",
3128
- icon: lucide_react.TextQuote,
3640
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.TextQuote, { size: 20 }),
3129
3641
  category: "Text",
3130
3642
  searchTerms: ["blockquote"],
3131
3643
  command: ({ editor, range }) => {
@@ -3135,7 +3647,7 @@ const QUOTE = {
3135
3647
  const CODE = {
3136
3648
  title: "Code block",
3137
3649
  description: "Code snippet",
3138
- icon: lucide_react.SquareCode,
3650
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.SquareCode, { size: 20 }),
3139
3651
  category: "Text",
3140
3652
  searchTerms: ["codeblock"],
3141
3653
  command: ({ editor, range }) => {
@@ -3145,7 +3657,7 @@ const CODE = {
3145
3657
  const BUTTON = {
3146
3658
  title: "Button",
3147
3659
  description: "Clickable button",
3148
- icon: lucide_react.MousePointer,
3660
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.MousePointer, { size: 20 }),
3149
3661
  category: "Layout",
3150
3662
  searchTerms: ["button"],
3151
3663
  command: ({ editor, range }) => {
@@ -3155,7 +3667,7 @@ const BUTTON = {
3155
3667
  const DIVIDER = {
3156
3668
  title: "Divider",
3157
3669
  description: "Horizontal separator",
3158
- icon: lucide_react.SplitSquareVertical,
3670
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.SplitSquareVertical, { size: 20 }),
3159
3671
  category: "Layout",
3160
3672
  searchTerms: [
3161
3673
  "hr",
@@ -3169,7 +3681,7 @@ const DIVIDER = {
3169
3681
  const SECTION = {
3170
3682
  title: "Section",
3171
3683
  description: "Content section",
3172
- icon: lucide_react.Rows2,
3684
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Rows2, { size: 20 }),
3173
3685
  category: "Layout",
3174
3686
  searchTerms: [
3175
3687
  "section",
@@ -3183,7 +3695,7 @@ const SECTION = {
3183
3695
  const TWO_COLUMNS = {
3184
3696
  title: "2 columns",
3185
3697
  description: "Two column layout",
3186
- icon: lucide_react.Columns2,
3698
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Columns2, { size: 20 }),
3187
3699
  category: "Layout",
3188
3700
  searchTerms: [
3189
3701
  "columns",
@@ -3204,7 +3716,7 @@ const TWO_COLUMNS = {
3204
3716
  const THREE_COLUMNS = {
3205
3717
  title: "3 columns",
3206
3718
  description: "Three column layout",
3207
- icon: lucide_react.Columns3,
3719
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Columns3, { size: 20 }),
3208
3720
  category: "Layout",
3209
3721
  searchTerms: [
3210
3722
  "columns",
@@ -3224,7 +3736,7 @@ const THREE_COLUMNS = {
3224
3736
  const FOUR_COLUMNS = {
3225
3737
  title: "4 columns",
3226
3738
  description: "Four column layout",
3227
- icon: lucide_react.Columns4,
3739
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Columns4, { size: 20 }),
3228
3740
  category: "Layout",
3229
3741
  searchTerms: [
3230
3742
  "columns",
@@ -3379,6 +3891,7 @@ const SlashCommand = createSlashCommand();
3379
3891
  exports.AlignmentAttribute = AlignmentAttribute;
3380
3892
  exports.BULLET_LIST = BULLET_LIST;
3381
3893
  exports.BUTTON = BUTTON;
3894
+ exports.Blockquote = Blockquote;
3382
3895
  exports.Body = Body;
3383
3896
  exports.Bold = Bold;
3384
3897
  exports.BubbleMenu = BubbleMenu;
@@ -3398,6 +3911,7 @@ exports.BubbleMenuSeparator = BubbleMenuSeparator;
3398
3911
  exports.BubbleMenuStrike = BubbleMenuStrike;
3399
3912
  exports.BubbleMenuUnderline = BubbleMenuUnderline;
3400
3913
  exports.BubbleMenuUppercase = BubbleMenuUppercase;
3914
+ exports.BulletList = BulletList;
3401
3915
  exports.Button = Button;
3402
3916
  exports.ButtonBubbleMenu = ButtonBubbleMenu;
3403
3917
  exports.ButtonBubbleMenuDefault = ButtonBubbleMenuDefault;
@@ -3407,6 +3921,7 @@ exports.ButtonBubbleMenuToolbar = ButtonBubbleMenuToolbar;
3407
3921
  exports.CODE = CODE;
3408
3922
  exports.COLUMN_PARENT_TYPES = COLUMN_PARENT_TYPES;
3409
3923
  exports.ClassAttribute = ClassAttribute;
3924
+ exports.Code = Code;
3410
3925
  exports.CodeBlockPrism = CodeBlockPrism;
3411
3926
  exports.ColumnsColumn = ColumnsColumn;
3412
3927
  exports.CommandList = CommandList;
@@ -3418,11 +3933,13 @@ exports.FourColumns = FourColumns;
3418
3933
  exports.H1 = H1;
3419
3934
  exports.H2 = H2;
3420
3935
  exports.H3 = H3;
3936
+ exports.HardBreak = HardBreak;
3421
3937
  exports.ImageBubbleMenu = ImageBubbleMenu;
3422
3938
  exports.ImageBubbleMenuDefault = ImageBubbleMenuDefault;
3423
3939
  exports.ImageBubbleMenuEditLink = ImageBubbleMenuEditLink;
3424
3940
  exports.ImageBubbleMenuRoot = ImageBubbleMenuRoot;
3425
3941
  exports.ImageBubbleMenuToolbar = ImageBubbleMenuToolbar;
3942
+ exports.Italic = Italic;
3426
3943
  exports.LinkBubbleMenu = LinkBubbleMenu;
3427
3944
  exports.LinkBubbleMenuDefault = LinkBubbleMenuDefault;
3428
3945
  exports.LinkBubbleMenuEditLink = LinkBubbleMenuEditLink;
@@ -3431,12 +3948,15 @@ exports.LinkBubbleMenuOpenLink = LinkBubbleMenuOpenLink;
3431
3948
  exports.LinkBubbleMenuRoot = LinkBubbleMenuRoot;
3432
3949
  exports.LinkBubbleMenuToolbar = LinkBubbleMenuToolbar;
3433
3950
  exports.LinkBubbleMenuUnlink = LinkBubbleMenuUnlink;
3951
+ exports.ListItem = ListItem;
3434
3952
  exports.MAX_COLUMNS_DEPTH = MAX_COLUMNS_DEPTH;
3435
3953
  exports.MaxNesting = MaxNesting;
3436
3954
  exports.NUMBERED_LIST = NUMBERED_LIST;
3437
3955
  exports.NodeSelectorContent = NodeSelectorContent;
3438
3956
  exports.NodeSelectorRoot = NodeSelectorRoot;
3439
3957
  exports.NodeSelectorTrigger = NodeSelectorTrigger;
3958
+ exports.OrderedList = OrderedList;
3959
+ exports.Paragraph = Paragraph;
3440
3960
  exports.Placeholder = Placeholder;
3441
3961
  exports.PreservedStyle = PreservedStyle;
3442
3962
  exports.PreviewText = PreviewText;
@@ -3444,6 +3964,7 @@ exports.QUOTE = QUOTE;
3444
3964
  exports.SECTION = SECTION;
3445
3965
  exports.Section = Section;
3446
3966
  exports.SlashCommand = SlashCommand;
3967
+ exports.Strike = Strike;
3447
3968
  exports.StyleAttribute = StyleAttribute;
3448
3969
  exports.Sup = Sup;
3449
3970
  exports.TEXT = TEXT;
@@ -3456,6 +3977,7 @@ exports.TableRow = TableRow;
3456
3977
  exports.ThreeColumns = ThreeColumns;
3457
3978
  exports.TwoColumns = TwoColumns;
3458
3979
  exports.Uppercase = Uppercase;
3980
+ exports.composeReactEmail = composeReactEmail;
3459
3981
  exports.coreExtensions = coreExtensions;
3460
3982
  exports.createSlashCommand = createSlashCommand;
3461
3983
  exports.defaultSlashCommands = defaultSlashCommands;
@@ -3468,5 +3990,6 @@ exports.processStylesForUnlink = processStylesForUnlink;
3468
3990
  exports.scoreItem = scoreItem;
3469
3991
  exports.setTextAlignment = setTextAlignment;
3470
3992
  exports.useButtonBubbleMenuContext = useButtonBubbleMenuContext;
3993
+ exports.useEditor = useEditor;
3471
3994
  exports.useImageBubbleMenuContext = useImageBubbleMenuContext;
3472
3995
  exports.useLinkBubbleMenuContext = useLinkBubbleMenuContext;