@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.mjs CHANGED
@@ -1,54 +1,34 @@
1
- import { Extension, Mark, Node, findChildren, markInputRule, markPasteRule, mergeAttributes } from "@tiptap/core";
2
- import { StarterKit } from "@tiptap/starter-kit";
3
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
1
  import * as ReactEmailComponents from "@react-email/components";
5
- import { Button as Button$1, CodeBlock, Column, Row, Section as Section$1 } from "@react-email/components";
2
+ import { Body as Body$1, Button as Button$1, CodeBlock, Column, Head, Html, Preview, Row, Section as Section$1, pretty, render, toPlainText } from "@react-email/components";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { Extension, Mark, Node as Node$1, findChildren, markInputRule, markPasteRule, mergeAttributes } from "@tiptap/core";
5
+ import { UndoRedo } from "@tiptap/extensions";
6
+ import { ReactRenderer, useCurrentEditor, useEditor as useEditor$1, useEditorState } from "@tiptap/react";
7
+ import * as React from "react";
8
+ import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
9
+ import { StarterKit } from "@tiptap/starter-kit";
10
+ import BlockquoteBase from "@tiptap/extension-blockquote";
11
+ import BulletListBase from "@tiptap/extension-bullet-list";
12
+ import CodeBase from "@tiptap/extension-code";
6
13
  import CodeBlock$1 from "@tiptap/extension-code-block";
7
14
  import { Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
8
15
  import { Decoration, DecorationSet } from "@tiptap/pm/view";
9
16
  import { fromHtml } from "hast-util-from-html";
10
17
  import Prism from "prismjs";
18
+ import HardBreakBase from "@tiptap/extension-hard-break";
19
+ import ItalicBase from "@tiptap/extension-italic";
20
+ import ListItemBase from "@tiptap/extension-list-item";
21
+ import OrderedListBase from "@tiptap/extension-ordered-list";
22
+ import ParagraphBase from "@tiptap/extension-paragraph";
11
23
  import TipTapPlaceholder from "@tiptap/extension-placeholder";
12
- import { ReactRenderer, useCurrentEditor, useEditorState } from "@tiptap/react";
13
- import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, CaseUpperIcon, Check, ChevronDown, Code, CodeIcon, Columns2, Columns3, Columns4, ExternalLinkIcon, Heading1, Heading2, Heading3, ItalicIcon, LinkIcon, List, ListOrdered, MousePointer, PencilIcon, Rows2, SplitSquareVertical, SquareCode, StrikethroughIcon, Text, TextIcon, TextQuote, UnderlineIcon, UnlinkIcon } from "lucide-react";
14
- import * as React from "react";
15
- import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
24
+ import StrikeBase from "@tiptap/extension-strike";
25
+ import { generateJSON } from "@tiptap/html";
26
+ import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, CaseUpperIcon, Check, ChevronDown, Code as Code$1, CodeIcon, Columns2, Columns3, Columns4, ExternalLinkIcon, Heading1, Heading2, Heading3, ItalicIcon, LinkIcon, List, ListOrdered, MousePointer, PencilIcon, Rows2, SplitSquareVertical, SquareCode, StrikethroughIcon, Text, TextIcon, TextQuote, UnderlineIcon, UnlinkIcon } from "lucide-react";
16
27
  import * as Popover from "@radix-ui/react-popover";
17
28
  import { BubbleMenu as BubbleMenu$1 } from "@tiptap/react/menus";
18
29
  import Suggestion from "@tiptap/suggestion";
19
30
  import tippy from "tippy.js";
20
31
 
21
- //#region src/core/email-node.ts
22
- var EmailNode = class EmailNode extends Node {
23
- constructor(config) {
24
- super(config);
25
- }
26
- /**
27
- * Create a new Node instance
28
- * @param config - Node configuration object or a function that returns a configuration object
29
- */
30
- static create(config) {
31
- return new EmailNode(typeof config === "function" ? config() : config);
32
- }
33
- static from(node, renderToReactEmail) {
34
- const customNode = EmailNode.create({});
35
- Object.assign(customNode, { ...node });
36
- customNode.config = {
37
- ...node.config,
38
- renderToReactEmail
39
- };
40
- return customNode;
41
- }
42
- configure(options) {
43
- return super.configure(options);
44
- }
45
- extend(extendedConfig) {
46
- const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
47
- return super.extend(resolvedConfig);
48
- }
49
- };
50
-
51
- //#endregion
52
32
  //#region src/core/event-bus.ts
53
33
  const EVENT_PREFIX = "@react-email/editor:";
54
34
  var EditorEventBus = class {
@@ -90,139 +70,6 @@ var EditorEventBus = class {
90
70
  };
91
71
  const editorEventBus = new EditorEventBus();
92
72
 
93
- //#endregion
94
- //#region src/extensions/alignment-attribute.tsx
95
- const AlignmentAttribute = Extension.create({
96
- name: "alignmentAttribute",
97
- addOptions() {
98
- return {
99
- types: [],
100
- alignments: [
101
- "left",
102
- "center",
103
- "right",
104
- "justify"
105
- ]
106
- };
107
- },
108
- addGlobalAttributes() {
109
- return [{
110
- types: this.options.types,
111
- attributes: { alignment: {
112
- parseHTML: (element) => {
113
- const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
114
- if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
115
- return null;
116
- },
117
- renderHTML: (attributes) => {
118
- if (attributes.alignment === "left") return {};
119
- return { alignment: attributes.alignment };
120
- }
121
- } }
122
- }];
123
- },
124
- addCommands() {
125
- return { setAlignment: (alignment) => ({ commands }) => {
126
- if (!this.options.alignments.includes(alignment)) return false;
127
- return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
128
- } };
129
- },
130
- addKeyboardShortcuts() {
131
- return {
132
- Enter: () => {
133
- const { from } = this.editor.state.selection;
134
- const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
135
- if (currentAlignment) requestAnimationFrame(() => {
136
- this.editor.commands.setAlignment(currentAlignment);
137
- });
138
- return false;
139
- },
140
- "Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
141
- "Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
142
- "Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
143
- "Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
144
- };
145
- }
146
- });
147
-
148
- //#endregion
149
- //#region src/utils/attribute-helpers.ts
150
- /**
151
- * Creates TipTap attribute definitions for a list of HTML attributes.
152
- * Each attribute will have the same pattern:
153
- * - default: null
154
- * - parseHTML: extracts the attribute from the element
155
- * - renderHTML: conditionally renders the attribute if it has a value
156
- *
157
- * @param attributeNames - Array of HTML attribute names to create definitions for
158
- * @returns Object with TipTap attribute definitions
159
- *
160
- * @example
161
- * const attrs = createStandardAttributes(['class', 'id', 'title']);
162
- * // Returns:
163
- * // {
164
- * // class: {
165
- * // default: null,
166
- * // parseHTML: (element) => element.getAttribute('class'),
167
- * // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
168
- * // },
169
- * // ...
170
- * // }
171
- */
172
- function createStandardAttributes(attributeNames) {
173
- return Object.fromEntries(attributeNames.map((attr) => [attr, {
174
- default: null,
175
- parseHTML: (element) => element.getAttribute(attr),
176
- renderHTML: (attributes) => {
177
- if (!attributes[attr]) return {};
178
- return { [attr]: attributes[attr] };
179
- }
180
- }]));
181
- }
182
- /**
183
- * Common HTML attributes used across multiple extensions.
184
- * These preserve attributes during HTML import and editing for better
185
- * fidelity when importing existing email templates.
186
- */
187
- const COMMON_HTML_ATTRIBUTES = [
188
- "id",
189
- "class",
190
- "title",
191
- "lang",
192
- "dir",
193
- "data-id"
194
- ];
195
- /**
196
- * Layout-specific HTML attributes used for positioning and sizing.
197
- */
198
- const LAYOUT_ATTRIBUTES = [
199
- "align",
200
- "width",
201
- "height"
202
- ];
203
- /**
204
- * Table-specific HTML attributes used for table layout and styling.
205
- */
206
- const TABLE_ATTRIBUTES = [
207
- "border",
208
- "cellpadding",
209
- "cellspacing"
210
- ];
211
- /**
212
- * Table cell-specific HTML attributes.
213
- */
214
- const TABLE_CELL_ATTRIBUTES = [
215
- "valign",
216
- "bgcolor",
217
- "colspan",
218
- "rowspan"
219
- ];
220
- /**
221
- * Table header cell-specific HTML attributes.
222
- * These are additional attributes that only apply to <th> elements.
223
- */
224
- const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
225
-
226
73
  //#endregion
227
74
  //#region src/utils/styles.ts
228
75
  const WHITE_SPACE_REGEX = /\s+/;
@@ -392,6 +239,342 @@ function resolveConflictingStyles(resetStyles, inlineStyles) {
392
239
  };
393
240
  }
394
241
 
242
+ //#endregion
243
+ //#region src/core/serializer/default-base-template.tsx
244
+ function DefaultBaseTemplate({ children, previewText }) {
245
+ return /* @__PURE__ */ jsxs(Html, { children: [
246
+ /* @__PURE__ */ jsxs(Head, { children: [
247
+ /* @__PURE__ */ jsx("meta", {
248
+ content: "width=device-width",
249
+ name: "viewport"
250
+ }),
251
+ /* @__PURE__ */ jsx("meta", {
252
+ content: "IE=edge",
253
+ httpEquiv: "X-UA-Compatible"
254
+ }),
255
+ /* @__PURE__ */ jsx("meta", { name: "x-apple-disable-message-reformatting" }),
256
+ /* @__PURE__ */ jsx("meta", {
257
+ content: "telephone=no,address=no,email=no,date=no,url=no",
258
+ name: "format-detection"
259
+ })
260
+ ] }),
261
+ previewText && previewText !== "" && /* @__PURE__ */ jsx(Preview, { children: previewText }),
262
+ /* @__PURE__ */ jsx(Body$1, { children: /* @__PURE__ */ jsx(Section$1, {
263
+ width: "100%",
264
+ align: "center",
265
+ children: /* @__PURE__ */ jsx(Section$1, {
266
+ style: { width: "100%" },
267
+ children
268
+ })
269
+ }) })
270
+ ] });
271
+ }
272
+
273
+ //#endregion
274
+ //#region src/core/serializer/email-mark.ts
275
+ var EmailMark = class EmailMark extends Mark {
276
+ constructor(config) {
277
+ super(config);
278
+ }
279
+ /**
280
+ * Create a new Mark instance
281
+ * @param config - Mark configuration object or a function that returns a configuration object
282
+ */
283
+ static create(config) {
284
+ return new EmailMark(typeof config === "function" ? config() : config);
285
+ }
286
+ static from(mark, renderToReactEmail) {
287
+ const customMark = EmailMark.create({});
288
+ Object.assign(customMark, { ...mark });
289
+ customMark.config = {
290
+ ...mark.config,
291
+ renderToReactEmail
292
+ };
293
+ return customMark;
294
+ }
295
+ configure(options) {
296
+ return super.configure(options);
297
+ }
298
+ extend(extendedConfig) {
299
+ const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
300
+ return super.extend(resolvedConfig);
301
+ }
302
+ };
303
+
304
+ //#endregion
305
+ //#region src/core/serializer/email-node.ts
306
+ var EmailNode = class EmailNode extends Node$1 {
307
+ constructor(config) {
308
+ super(config);
309
+ }
310
+ /**
311
+ * Create a new Node instance
312
+ * @param config - Node configuration object or a function that returns a configuration object
313
+ */
314
+ static create(config) {
315
+ return new EmailNode(typeof config === "function" ? config() : config);
316
+ }
317
+ static from(node, renderToReactEmail) {
318
+ const customNode = EmailNode.create({});
319
+ Object.assign(customNode, { ...node });
320
+ customNode.config = {
321
+ ...node.config,
322
+ renderToReactEmail
323
+ };
324
+ return customNode;
325
+ }
326
+ configure(options) {
327
+ return super.configure(options);
328
+ }
329
+ extend(extendedConfig) {
330
+ const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
331
+ return super.extend(resolvedConfig);
332
+ }
333
+ };
334
+
335
+ //#endregion
336
+ //#region src/core/serializer/compose-react-email.tsx
337
+ const MARK_ORDER = {
338
+ preservedStyle: 0,
339
+ italic: 1,
340
+ strike: 2,
341
+ underline: 3,
342
+ link: 4,
343
+ bold: 5,
344
+ code: 6
345
+ };
346
+ const NODES_WITH_INCREMENTED_CHILD_DEPTH = new Set(["bulletList", "orderedList"]);
347
+ function getOrderedMarks(marks) {
348
+ if (!marks) return [];
349
+ return [...marks].sort((a, b) => (MARK_ORDER[a.type] ?? Number.MAX_SAFE_INTEGER) - (MARK_ORDER[b.type] ?? Number.MAX_SAFE_INTEGER));
350
+ }
351
+ const composeReactEmail = async ({ editor, preview }) => {
352
+ const data = editor.getJSON();
353
+ const extensions = editor.extensionManager.extensions;
354
+ const serializerPlugin = extensions.map((ext) => ext.options?.serializerPlugin).filter((p) => Boolean(p)).at(-1);
355
+ const emailNodeComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailNode).map((extension) => [extension.name, extension.config.renderToReactEmail]));
356
+ const emailMarkComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailMark).map((extension) => [extension.name, extension.config.renderToReactEmail]));
357
+ function renderMark(mark, node, children, depth) {
358
+ const markStyle = serializerPlugin?.getNodeStyles({
359
+ type: mark.type,
360
+ attrs: mark.attrs ?? {}
361
+ }, depth, editor) ?? {};
362
+ const markRenderer = emailMarkComponentRegistry[mark.type];
363
+ if (markRenderer) return markRenderer({
364
+ mark,
365
+ node,
366
+ style: markStyle,
367
+ children
368
+ });
369
+ return children;
370
+ }
371
+ function parseContent(content, depth = 0) {
372
+ if (!content) return;
373
+ return content.map((node, index) => {
374
+ const style = serializerPlugin?.getNodeStyles(node, depth, editor) ?? {};
375
+ const inlineStyles = inlineCssToJs(node.attrs?.style);
376
+ if (node.type && emailNodeComponentRegistry[node.type]) {
377
+ const Component = emailNodeComponentRegistry[node.type];
378
+ const childDepth = NODES_WITH_INCREMENTED_CHILD_DEPTH.has(node.type) ? depth + 1 : depth;
379
+ return /* @__PURE__ */ jsx(Component, {
380
+ node: node.type === "table" && inlineStyles.width && !node.attrs?.width ? {
381
+ ...node,
382
+ attrs: {
383
+ ...node.attrs,
384
+ width: inlineStyles.width
385
+ }
386
+ } : node,
387
+ style,
388
+ children: parseContent(node.content, childDepth)
389
+ }, index);
390
+ }
391
+ switch (node.type) {
392
+ case "text": {
393
+ let wrappedText = node.text;
394
+ getOrderedMarks(node.marks).forEach((mark) => {
395
+ wrappedText = renderMark(mark, node, wrappedText, depth);
396
+ });
397
+ const textAttributes = node.marks?.find((mark) => mark.type === "textStyle")?.attrs;
398
+ return /* @__PURE__ */ jsx("span", {
399
+ style: {
400
+ ...textAttributes,
401
+ ...style
402
+ },
403
+ children: wrappedText
404
+ }, index);
405
+ }
406
+ default: return null;
407
+ }
408
+ });
409
+ }
410
+ const unformattedHtml = await render(/* @__PURE__ */ jsx(serializerPlugin?.BaseTemplate ?? DefaultBaseTemplate, {
411
+ previewText: preview,
412
+ editor,
413
+ children: parseContent(data.content)
414
+ }));
415
+ const [prettyHtml, text] = await Promise.all([pretty(unformattedHtml), toPlainText(unformattedHtml)]);
416
+ return {
417
+ html: prettyHtml,
418
+ text
419
+ };
420
+ };
421
+
422
+ //#endregion
423
+ //#region src/extensions/alignment-attribute.tsx
424
+ const AlignmentAttribute = Extension.create({
425
+ name: "alignmentAttribute",
426
+ addOptions() {
427
+ return {
428
+ types: [],
429
+ alignments: [
430
+ "left",
431
+ "center",
432
+ "right",
433
+ "justify"
434
+ ]
435
+ };
436
+ },
437
+ addGlobalAttributes() {
438
+ return [{
439
+ types: this.options.types,
440
+ attributes: { alignment: {
441
+ parseHTML: (element) => {
442
+ const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
443
+ if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
444
+ return null;
445
+ },
446
+ renderHTML: (attributes) => {
447
+ if (attributes.alignment === "left") return {};
448
+ return { alignment: attributes.alignment };
449
+ }
450
+ } }
451
+ }];
452
+ },
453
+ addCommands() {
454
+ return { setAlignment: (alignment) => ({ commands }) => {
455
+ if (!this.options.alignments.includes(alignment)) return false;
456
+ return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
457
+ } };
458
+ },
459
+ addKeyboardShortcuts() {
460
+ return {
461
+ Enter: () => {
462
+ const { from } = this.editor.state.selection;
463
+ const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
464
+ if (currentAlignment) requestAnimationFrame(() => {
465
+ this.editor.commands.setAlignment(currentAlignment);
466
+ });
467
+ return false;
468
+ },
469
+ "Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
470
+ "Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
471
+ "Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
472
+ "Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
473
+ };
474
+ }
475
+ });
476
+
477
+ //#endregion
478
+ //#region src/utils/get-text-alignment.ts
479
+ function getTextAlignment(alignment) {
480
+ switch (alignment) {
481
+ case "left": return { textAlign: "left" };
482
+ case "center": return { textAlign: "center" };
483
+ case "right": return { textAlign: "right" };
484
+ default: return {};
485
+ }
486
+ }
487
+
488
+ //#endregion
489
+ //#region src/extensions/blockquote.tsx
490
+ const Blockquote = EmailNode.from(BlockquoteBase, ({ children, node, style }) => /* @__PURE__ */ jsx("blockquote", {
491
+ className: node.attrs?.class || void 0,
492
+ style: {
493
+ ...style,
494
+ ...inlineCssToJs(node.attrs?.style),
495
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
496
+ },
497
+ children
498
+ }));
499
+
500
+ //#endregion
501
+ //#region src/utils/attribute-helpers.ts
502
+ /**
503
+ * Creates TipTap attribute definitions for a list of HTML attributes.
504
+ * Each attribute will have the same pattern:
505
+ * - default: null
506
+ * - parseHTML: extracts the attribute from the element
507
+ * - renderHTML: conditionally renders the attribute if it has a value
508
+ *
509
+ * @param attributeNames - Array of HTML attribute names to create definitions for
510
+ * @returns Object with TipTap attribute definitions
511
+ *
512
+ * @example
513
+ * const attrs = createStandardAttributes(['class', 'id', 'title']);
514
+ * // Returns:
515
+ * // {
516
+ * // class: {
517
+ * // default: null,
518
+ * // parseHTML: (element) => element.getAttribute('class'),
519
+ * // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
520
+ * // },
521
+ * // ...
522
+ * // }
523
+ */
524
+ function createStandardAttributes(attributeNames) {
525
+ return Object.fromEntries(attributeNames.map((attr) => [attr, {
526
+ default: null,
527
+ parseHTML: (element) => element.getAttribute(attr),
528
+ renderHTML: (attributes) => {
529
+ if (!attributes[attr]) return {};
530
+ return { [attr]: attributes[attr] };
531
+ }
532
+ }]));
533
+ }
534
+ /**
535
+ * Common HTML attributes used across multiple extensions.
536
+ * These preserve attributes during HTML import and editing for better
537
+ * fidelity when importing existing email templates.
538
+ */
539
+ const COMMON_HTML_ATTRIBUTES = [
540
+ "id",
541
+ "class",
542
+ "title",
543
+ "lang",
544
+ "dir",
545
+ "data-id"
546
+ ];
547
+ /**
548
+ * Layout-specific HTML attributes used for positioning and sizing.
549
+ */
550
+ const LAYOUT_ATTRIBUTES = [
551
+ "align",
552
+ "width",
553
+ "height"
554
+ ];
555
+ /**
556
+ * Table-specific HTML attributes used for table layout and styling.
557
+ */
558
+ const TABLE_ATTRIBUTES = [
559
+ "border",
560
+ "cellpadding",
561
+ "cellspacing"
562
+ ];
563
+ /**
564
+ * Table cell-specific HTML attributes.
565
+ */
566
+ const TABLE_CELL_ATTRIBUTES = [
567
+ "valign",
568
+ "bgcolor",
569
+ "colspan",
570
+ "rowspan"
571
+ ];
572
+ /**
573
+ * Table header cell-specific HTML attributes.
574
+ * These are additional attributes that only apply to <th> elements.
575
+ */
576
+ const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
577
+
395
578
  //#endregion
396
579
  //#region src/extensions/body.tsx
397
580
  const Body = EmailNode.create({
@@ -438,7 +621,7 @@ const Body = EmailNode.create({
438
621
  });
439
622
 
440
623
  //#endregion
441
- //#region src/extensions/bold.ts
624
+ //#region src/extensions/bold.tsx
442
625
  /**
443
626
  * Matches bold text via `**` as input.
444
627
  */
@@ -459,7 +642,7 @@ const underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
459
642
  * This extension allows you to mark text as bold.
460
643
  * @see https://tiptap.dev/api/marks/bold
461
644
  */
462
- const Bold = Mark.create({
645
+ const Bold = EmailMark.create({
463
646
  name: "bold",
464
647
  addOptions() {
465
648
  return { HTMLAttributes: {} };
@@ -484,6 +667,12 @@ const Bold = Mark.create({
484
667
  0
485
668
  ];
486
669
  },
670
+ renderToReactEmail({ children, style }) {
671
+ return /* @__PURE__ */ jsx("strong", {
672
+ style,
673
+ children
674
+ });
675
+ },
487
676
  addCommands() {
488
677
  return {
489
678
  setBold: () => ({ commands }) => {
@@ -523,6 +712,17 @@ const Bold = Mark.create({
523
712
  }
524
713
  });
525
714
 
715
+ //#endregion
716
+ //#region src/extensions/bullet-list.tsx
717
+ const BulletList = EmailNode.from(BulletListBase, ({ children, node, style }) => /* @__PURE__ */ jsx("ul", {
718
+ className: node.attrs?.class || void 0,
719
+ style: {
720
+ ...style,
721
+ ...inlineCssToJs(node.attrs?.style)
722
+ },
723
+ children
724
+ }));
725
+
526
726
  //#endregion
527
727
  //#region src/extensions/button.tsx
528
728
  const Button = EmailNode.create({
@@ -644,6 +844,16 @@ const ClassAttribute = Extension.create({
644
844
  }
645
845
  });
646
846
 
847
+ //#endregion
848
+ //#region src/extensions/code.tsx
849
+ const Code = EmailMark.from(CodeBase, ({ children, node, style }) => /* @__PURE__ */ jsx("code", {
850
+ style: {
851
+ ...style,
852
+ ...inlineCssToJs(node.attrs?.style)
853
+ },
854
+ children
855
+ }));
856
+
647
857
  //#endregion
648
858
  //#region src/utils/prism-utils.ts
649
859
  const publicURL = "/styles/prism";
@@ -922,6 +1132,29 @@ const Div = EmailNode.create({
922
1132
  }
923
1133
  });
924
1134
 
1135
+ //#endregion
1136
+ //#region src/extensions/hard-break.tsx
1137
+ const HardBreak = EmailNode.from(HardBreakBase, () => /* @__PURE__ */ jsx("br", {}));
1138
+
1139
+ //#endregion
1140
+ //#region src/extensions/italic.tsx
1141
+ const Italic = EmailMark.from(ItalicBase, ({ children, style }) => /* @__PURE__ */ jsx("em", {
1142
+ style,
1143
+ children
1144
+ }));
1145
+
1146
+ //#endregion
1147
+ //#region src/extensions/list-item.tsx
1148
+ const ListItem = EmailNode.from(ListItemBase, ({ children, node, style }) => /* @__PURE__ */ jsx("li", {
1149
+ className: node.attrs?.class || void 0,
1150
+ style: {
1151
+ ...style,
1152
+ ...inlineCssToJs(node.attrs?.style),
1153
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
1154
+ },
1155
+ children
1156
+ }));
1157
+
925
1158
  //#endregion
926
1159
  //#region src/extensions/max-nesting.ts
927
1160
  const MaxNesting = Extension.create({
@@ -997,6 +1230,33 @@ const MaxNesting = Extension.create({
997
1230
  }
998
1231
  });
999
1232
 
1233
+ //#endregion
1234
+ //#region src/extensions/ordered-list.tsx
1235
+ const OrderedList = EmailNode.from(OrderedListBase, ({ children, node, style }) => /* @__PURE__ */ jsx("ol", {
1236
+ className: node.attrs?.class || void 0,
1237
+ start: node.attrs?.start,
1238
+ style: {
1239
+ ...style,
1240
+ ...inlineCssToJs(node.attrs?.style)
1241
+ },
1242
+ children
1243
+ }));
1244
+
1245
+ //#endregion
1246
+ //#region src/extensions/paragraph.tsx
1247
+ const Paragraph = EmailNode.from(ParagraphBase, ({ children, node, style }) => {
1248
+ const isEmpty = !node.content || node.content.length === 0;
1249
+ return /* @__PURE__ */ jsx("p", {
1250
+ className: node.attrs?.class || void 0,
1251
+ style: {
1252
+ ...style,
1253
+ ...inlineCssToJs(node.attrs?.style),
1254
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
1255
+ },
1256
+ children: isEmpty ? /* @__PURE__ */ jsx("br", {}) : children
1257
+ });
1258
+ });
1259
+
1000
1260
  //#endregion
1001
1261
  //#region src/extensions/placeholder.ts
1002
1262
  const Placeholder = TipTapPlaceholder.configure({
@@ -1008,8 +1268,8 @@ const Placeholder = TipTapPlaceholder.configure({
1008
1268
  });
1009
1269
 
1010
1270
  //#endregion
1011
- //#region src/extensions/preserved-style.ts
1012
- const PreservedStyle = Mark.create({
1271
+ //#region src/extensions/preserved-style.tsx
1272
+ const PreservedStyle = EmailMark.create({
1013
1273
  name: "preservedStyle",
1014
1274
  addAttributes() {
1015
1275
  return { style: {
@@ -1038,6 +1298,12 @@ const PreservedStyle = Mark.create({
1038
1298
  mergeAttributes(HTMLAttributes),
1039
1299
  0
1040
1300
  ];
1301
+ },
1302
+ renderToReactEmail({ children, mark }) {
1303
+ return /* @__PURE__ */ jsx("span", {
1304
+ style: mark.attrs?.style ? inlineCssToJs(mark.attrs.style) : void 0,
1305
+ children
1306
+ });
1041
1307
  }
1042
1308
  });
1043
1309
  const LINK_INDICATOR_STYLES = [
@@ -1083,7 +1349,7 @@ function processStylesForUnlink(styleString) {
1083
1349
 
1084
1350
  //#endregion
1085
1351
  //#region src/extensions/preview-text.ts
1086
- const PreviewText = Node.create({
1352
+ const PreviewText = Node$1.create({
1087
1353
  name: "previewText",
1088
1354
  group: "block",
1089
1355
  selectable: false,
@@ -1122,17 +1388,6 @@ const PreviewText = Node.create({
1122
1388
  }
1123
1389
  });
1124
1390
 
1125
- //#endregion
1126
- //#region src/utils/get-text-alignment.ts
1127
- function getTextAlignment(alignment) {
1128
- switch (alignment) {
1129
- case "left": return { textAlign: "left" };
1130
- case "center": return { textAlign: "center" };
1131
- case "right": return { textAlign: "right" };
1132
- default: return {};
1133
- }
1134
- }
1135
-
1136
1391
  //#endregion
1137
1392
  //#region src/extensions/section.tsx
1138
1393
  const Section = EmailNode.create({
@@ -1181,6 +1436,13 @@ const Section = EmailNode.create({
1181
1436
  }
1182
1437
  });
1183
1438
 
1439
+ //#endregion
1440
+ //#region src/extensions/strike.tsx
1441
+ const Strike = EmailMark.from(StrikeBase, ({ children, style }) => /* @__PURE__ */ jsx("s", {
1442
+ style,
1443
+ children
1444
+ }));
1445
+
1184
1446
  //#endregion
1185
1447
  //#region src/extensions/style-attribute.tsx
1186
1448
  const StyleAttribute = Extension.create({
@@ -1230,12 +1492,12 @@ const StyleAttribute = Extension.create({
1230
1492
  });
1231
1493
 
1232
1494
  //#endregion
1233
- //#region src/extensions/sup.ts
1495
+ //#region src/extensions/sup.tsx
1234
1496
  /**
1235
1497
  * This extension allows you to mark text as superscript.
1236
1498
  * @see https://tiptap.dev/api/marks/superscript
1237
1499
  */
1238
- const Sup = Mark.create({
1500
+ const Sup = EmailMark.create({
1239
1501
  name: "sup",
1240
1502
  addOptions() {
1241
1503
  return { HTMLAttributes: {} };
@@ -1250,6 +1512,12 @@ const Sup = Mark.create({
1250
1512
  0
1251
1513
  ];
1252
1514
  },
1515
+ renderToReactEmail({ children, style }) {
1516
+ return /* @__PURE__ */ jsx("sup", {
1517
+ style,
1518
+ children
1519
+ });
1520
+ },
1253
1521
  addCommands() {
1254
1522
  return {
1255
1523
  setSup: () => ({ commands }) => {
@@ -1415,7 +1683,7 @@ const TableCell = EmailNode.create({
1415
1683
  });
1416
1684
  }
1417
1685
  });
1418
- const TableHeader = Node.create({
1686
+ const TableHeader = Node$1.create({
1419
1687
  name: "tableHeader",
1420
1688
  group: "tableCell",
1421
1689
  content: "block+",
@@ -1452,8 +1720,8 @@ const TableHeader = Node.create({
1452
1720
  });
1453
1721
 
1454
1722
  //#endregion
1455
- //#region src/extensions/uppercase.ts
1456
- const Uppercase = Mark.create({
1723
+ //#region src/extensions/uppercase.tsx
1724
+ const Uppercase = EmailMark.create({
1457
1725
  name: "uppercase",
1458
1726
  addOptions() {
1459
1727
  return { HTMLAttributes: {} };
@@ -1474,6 +1742,15 @@ const Uppercase = Mark.create({
1474
1742
  0
1475
1743
  ];
1476
1744
  },
1745
+ renderToReactEmail({ children, style }) {
1746
+ return /* @__PURE__ */ jsx("span", {
1747
+ style: {
1748
+ ...style,
1749
+ textTransform: "uppercase"
1750
+ },
1751
+ children
1752
+ });
1753
+ },
1477
1754
  addCommands() {
1478
1755
  return {
1479
1756
  setUppercase: () => ({ commands }) => {
@@ -1669,17 +1946,17 @@ const coreExtensions = [
1669
1946
  underline: false,
1670
1947
  trailingNode: false,
1671
1948
  bold: false,
1949
+ italic: false,
1950
+ strike: false,
1951
+ code: false,
1952
+ paragraph: false,
1953
+ bulletList: false,
1954
+ orderedList: false,
1955
+ listItem: false,
1956
+ blockquote: false,
1957
+ hardBreak: false,
1672
1958
  gapcursor: false,
1673
- listItem: {},
1674
- bulletList: { HTMLAttributes: { class: "node-bulletList" } },
1675
- paragraph: { HTMLAttributes: { class: "node-paragraph" } },
1676
- orderedList: { HTMLAttributes: { class: "node-orderedList" } },
1677
- blockquote: { HTMLAttributes: { class: "node-blockquote" } },
1678
1959
  codeBlock: false,
1679
- code: { HTMLAttributes: {
1680
- class: "node-inlineCode",
1681
- spellcheck: "false"
1682
- } },
1683
1960
  horizontalRule: false,
1684
1961
  dropcursor: {
1685
1962
  color: "#61a8f8",
@@ -1691,9 +1968,21 @@ const coreExtensions = [
1691
1968
  defaultLanguage: "javascript",
1692
1969
  HTMLAttributes: { class: "prism node-codeBlock" }
1693
1970
  }),
1971
+ Code.configure({ HTMLAttributes: {
1972
+ class: "node-inlineCode",
1973
+ spellcheck: "false"
1974
+ } }),
1975
+ Paragraph.configure({ HTMLAttributes: { class: "node-paragraph" } }),
1976
+ BulletList.configure({ HTMLAttributes: { class: "node-bulletList" } }),
1977
+ OrderedList.configure({ HTMLAttributes: { class: "node-orderedList" } }),
1978
+ Blockquote.configure({ HTMLAttributes: { class: "node-blockquote" } }),
1979
+ ListItem,
1980
+ HardBreak,
1981
+ Italic,
1694
1982
  Placeholder,
1695
1983
  PreviewText,
1696
1984
  Bold,
1985
+ Strike,
1697
1986
  Sup,
1698
1987
  Uppercase,
1699
1988
  PreservedStyle,
@@ -1780,6 +2069,221 @@ const coreExtensions = [
1780
2069
  })
1781
2070
  ];
1782
2071
 
2072
+ //#endregion
2073
+ //#region src/core/create-drop-handler.ts
2074
+ function createDropHandler({ onPaste, onUploadImage }) {
2075
+ return (view, event, _slice, moved) => {
2076
+ if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
2077
+ event.preventDefault();
2078
+ const file = event.dataTransfer.files[0];
2079
+ if (onPaste?.(file, view)) return true;
2080
+ if (file.type.includes("image/") && onUploadImage) {
2081
+ onUploadImage(file, view, (view.posAtCoords({
2082
+ left: event.clientX,
2083
+ top: event.clientY
2084
+ })?.pos || 0) - 1);
2085
+ return true;
2086
+ }
2087
+ }
2088
+ return false;
2089
+ };
2090
+ }
2091
+
2092
+ //#endregion
2093
+ //#region src/utils/paste-sanitizer.ts
2094
+ /**
2095
+ * Sanitizes pasted HTML.
2096
+ * - From editor (has node-* classes): pass through as-is
2097
+ * - From external: strip all styles/classes, keep only semantic HTML
2098
+ */
2099
+ /**
2100
+ * Detects content from the Resend editor by checking for node-* class names.
2101
+ */
2102
+ const EDITOR_CLASS_PATTERN = /class="[^"]*node-/;
2103
+ /**
2104
+ * Attributes to preserve on specific elements for EXTERNAL content.
2105
+ * Only functional attributes - NO style or class.
2106
+ */
2107
+ const PRESERVED_ATTRIBUTES = {
2108
+ a: [
2109
+ "href",
2110
+ "target",
2111
+ "rel"
2112
+ ],
2113
+ img: [
2114
+ "src",
2115
+ "alt",
2116
+ "width",
2117
+ "height"
2118
+ ],
2119
+ td: ["colspan", "rowspan"],
2120
+ th: [
2121
+ "colspan",
2122
+ "rowspan",
2123
+ "scope"
2124
+ ],
2125
+ table: [
2126
+ "border",
2127
+ "cellpadding",
2128
+ "cellspacing"
2129
+ ],
2130
+ "*": ["id"]
2131
+ };
2132
+ function isFromEditor(html) {
2133
+ return EDITOR_CLASS_PATTERN.test(html);
2134
+ }
2135
+ function sanitizePastedHtml(html) {
2136
+ if (isFromEditor(html)) return html;
2137
+ const doc = new DOMParser().parseFromString(html, "text/html");
2138
+ sanitizeNode(doc.body);
2139
+ return doc.body.innerHTML;
2140
+ }
2141
+ function sanitizeNode(node) {
2142
+ if (node.nodeType === Node.ELEMENT_NODE) sanitizeElement(node);
2143
+ for (const child of Array.from(node.childNodes)) sanitizeNode(child);
2144
+ }
2145
+ function sanitizeElement(el) {
2146
+ const allowedForTag = PRESERVED_ATTRIBUTES[el.tagName.toLowerCase()] || [];
2147
+ const allowedGlobal = PRESERVED_ATTRIBUTES["*"] || [];
2148
+ const allowed = new Set([...allowedForTag, ...allowedGlobal]);
2149
+ const attributesToRemove = [];
2150
+ for (const attr of Array.from(el.attributes)) {
2151
+ if (attr.name.startsWith("data-")) {
2152
+ attributesToRemove.push(attr.name);
2153
+ continue;
2154
+ }
2155
+ if (!allowed.has(attr.name)) attributesToRemove.push(attr.name);
2156
+ }
2157
+ for (const attr of attributesToRemove) el.removeAttribute(attr);
2158
+ }
2159
+
2160
+ //#endregion
2161
+ //#region src/core/create-paste-handler.ts
2162
+ function createPasteHandler({ onPaste, onUploadImage, extensions }) {
2163
+ return (view, event, slice) => {
2164
+ const text = event.clipboardData?.getData("text/plain");
2165
+ if (text && onPaste?.(text, view)) {
2166
+ event.preventDefault();
2167
+ return true;
2168
+ }
2169
+ if (event.clipboardData?.files?.[0]) {
2170
+ const file = event.clipboardData.files[0];
2171
+ if (onPaste?.(file, view)) {
2172
+ event.preventDefault();
2173
+ return true;
2174
+ }
2175
+ if (file.type.includes("image/") && onUploadImage) {
2176
+ const pos = view.state.selection.from;
2177
+ onUploadImage(file, view, pos);
2178
+ return true;
2179
+ }
2180
+ }
2181
+ /**
2182
+ * If the coming content has a single child, we can assume
2183
+ * it's a plain text and doesn't need to be parsed and
2184
+ * be introduced in a new line
2185
+ */
2186
+ if (slice.content.childCount === 1) return false;
2187
+ if (event.clipboardData?.getData?.("text/html")) {
2188
+ event.preventDefault();
2189
+ const jsonContent = generateJSON(sanitizePastedHtml(event.clipboardData.getData("text/html")), extensions);
2190
+ const node = view.state.schema.nodeFromJSON(jsonContent);
2191
+ const transaction = view.state.tr.replaceSelectionWith(node, false);
2192
+ view.dispatch(transaction);
2193
+ return true;
2194
+ }
2195
+ return false;
2196
+ };
2197
+ }
2198
+
2199
+ //#endregion
2200
+ //#region src/core/is-document-visually-empty.ts
2201
+ function isDocumentVisuallyEmpty(doc) {
2202
+ let nonGlobalNodeCount = 0;
2203
+ let firstNonGlobalNode = null;
2204
+ for (let index = 0; index < doc.childCount; index += 1) {
2205
+ const node = doc.child(index);
2206
+ if (node.type.name === "globalContent") continue;
2207
+ nonGlobalNodeCount += 1;
2208
+ if (firstNonGlobalNode === null) firstNonGlobalNode = {
2209
+ type: node.type,
2210
+ textContent: node.textContent,
2211
+ childCount: node.content.childCount
2212
+ };
2213
+ }
2214
+ if (nonGlobalNodeCount === 0) return true;
2215
+ if (nonGlobalNodeCount !== 1) return false;
2216
+ return firstNonGlobalNode?.type.name === "paragraph" && firstNonGlobalNode.textContent.trim().length === 0 && firstNonGlobalNode.childCount === 0;
2217
+ }
2218
+
2219
+ //#endregion
2220
+ //#region src/core/use-editor.ts
2221
+ const COLLABORATION_EXTENSION_NAMES = new Set(["liveblocksExtension", "collaboration"]);
2222
+ function hasCollaborationExtension(exts) {
2223
+ return exts.some((ext) => COLLABORATION_EXTENSION_NAMES.has(ext.name));
2224
+ }
2225
+ function useEditor({ content, extensions = [], onUpdate, onPaste, onUploadImage, onReady, editable = true, ...rest }) {
2226
+ const [contentError, setContentError] = React.useState(null);
2227
+ const isCollaborative = hasCollaborationExtension(extensions);
2228
+ const effectiveExtensions = React.useMemo(() => [
2229
+ ...coreExtensions,
2230
+ ...isCollaborative ? [] : [UndoRedo],
2231
+ ...extensions
2232
+ ], [extensions, isCollaborative]);
2233
+ const editor = useEditor$1({
2234
+ content: isCollaborative ? void 0 : content,
2235
+ extensions: effectiveExtensions,
2236
+ immediatelyRender: false,
2237
+ enableContentCheck: true,
2238
+ onContentError({ editor: editor$1, error, disableCollaboration }) {
2239
+ disableCollaboration();
2240
+ setContentError(error);
2241
+ console.error(error);
2242
+ editor$1.setEditable(false);
2243
+ },
2244
+ onCreate({ editor: editor$1 }) {
2245
+ onReady?.(editor$1);
2246
+ },
2247
+ onUpdate({ editor: editor$1, transaction }) {
2248
+ onUpdate?.(editor$1, transaction);
2249
+ },
2250
+ editorProps: {
2251
+ handleDOMEvents: { click: (view, event) => {
2252
+ if (!view.editable) {
2253
+ if (event.target.closest("a")) {
2254
+ event.preventDefault();
2255
+ return true;
2256
+ }
2257
+ }
2258
+ return false;
2259
+ } },
2260
+ handlePaste: createPasteHandler({
2261
+ onPaste,
2262
+ onUploadImage,
2263
+ extensions: effectiveExtensions
2264
+ }),
2265
+ handleDrop: createDropHandler({
2266
+ onPaste,
2267
+ onUploadImage
2268
+ })
2269
+ },
2270
+ ...rest
2271
+ });
2272
+ return {
2273
+ editor,
2274
+ isEditorEmpty: useEditorState({
2275
+ editor,
2276
+ selector: (context) => {
2277
+ if (!context.editor) return true;
2278
+ return isDocumentVisuallyEmpty(context.editor.state.doc);
2279
+ }
2280
+ }) ?? true,
2281
+ extensions: effectiveExtensions,
2282
+ contentError,
2283
+ isCollaborative
2284
+ };
2285
+ }
2286
+
1783
2287
  //#endregion
1784
2288
  //#region src/utils/set-text-alignment.ts
1785
2289
  function setTextAlignment(editor, alignment) {
@@ -2219,7 +2723,7 @@ function NodeSelectorRoot({ omit = [], open: controlledOpen, onOpenChange, class
2219
2723
  },
2220
2724
  {
2221
2725
  name: "Code",
2222
- icon: Code,
2726
+ icon: Code$1,
2223
2727
  command: () => editor.chain().focus().clearNodes().toggleCodeBlock().run(),
2224
2728
  isActive: editorState?.isCodeBlockActive ?? false
2225
2729
  }
@@ -2934,13 +3438,12 @@ function groupByCategory(items) {
2934
3438
  return ordered;
2935
3439
  }
2936
3440
  function CommandItem({ item, selected, onSelect }) {
2937
- const Icon = item.icon;
2938
3441
  return /* @__PURE__ */ jsxs("button", {
2939
3442
  "data-re-slash-command-item": "",
2940
3443
  "data-selected": selected || void 0,
2941
3444
  onClick: onSelect,
2942
3445
  type: "button",
2943
- children: [/* @__PURE__ */ jsx(Icon, { size: 20 }), /* @__PURE__ */ jsx("span", { children: item.title })]
3446
+ children: [item.icon, /* @__PURE__ */ jsx("span", { children: item.title })]
2944
3447
  });
2945
3448
  }
2946
3449
  function CommandList({ items, command, query, ref }) {
@@ -3015,11 +3518,11 @@ function CommandList({ items, command, query, ref }) {
3015
3518
  }
3016
3519
 
3017
3520
  //#endregion
3018
- //#region src/ui/slash-command/commands.ts
3521
+ //#region src/ui/slash-command/commands.tsx
3019
3522
  const TEXT = {
3020
3523
  title: "Text",
3021
3524
  description: "Plain text block",
3022
- icon: Text,
3525
+ icon: /* @__PURE__ */ jsx(Text, { size: 20 }),
3023
3526
  category: "Text",
3024
3527
  searchTerms: ["p", "paragraph"],
3025
3528
  command: ({ editor, range }) => {
@@ -3029,7 +3532,7 @@ const TEXT = {
3029
3532
  const H1 = {
3030
3533
  title: "Title",
3031
3534
  description: "Large heading",
3032
- icon: Heading1,
3535
+ icon: /* @__PURE__ */ jsx(Heading1, { size: 20 }),
3033
3536
  category: "Text",
3034
3537
  searchTerms: [
3035
3538
  "title",
@@ -3044,7 +3547,7 @@ const H1 = {
3044
3547
  const H2 = {
3045
3548
  title: "Subtitle",
3046
3549
  description: "Medium heading",
3047
- icon: Heading2,
3550
+ icon: /* @__PURE__ */ jsx(Heading2, { size: 20 }),
3048
3551
  category: "Text",
3049
3552
  searchTerms: [
3050
3553
  "subtitle",
@@ -3058,7 +3561,7 @@ const H2 = {
3058
3561
  const H3 = {
3059
3562
  title: "Heading",
3060
3563
  description: "Small heading",
3061
- icon: Heading3,
3564
+ icon: /* @__PURE__ */ jsx(Heading3, { size: 20 }),
3062
3565
  category: "Text",
3063
3566
  searchTerms: [
3064
3567
  "subtitle",
@@ -3072,7 +3575,7 @@ const H3 = {
3072
3575
  const BULLET_LIST = {
3073
3576
  title: "Bullet list",
3074
3577
  description: "Unordered list",
3075
- icon: List,
3578
+ icon: /* @__PURE__ */ jsx(List, { size: 20 }),
3076
3579
  category: "Text",
3077
3580
  searchTerms: ["unordered", "point"],
3078
3581
  command: ({ editor, range }) => {
@@ -3082,7 +3585,7 @@ const BULLET_LIST = {
3082
3585
  const NUMBERED_LIST = {
3083
3586
  title: "Numbered list",
3084
3587
  description: "Ordered list",
3085
- icon: ListOrdered,
3588
+ icon: /* @__PURE__ */ jsx(ListOrdered, { size: 20 }),
3086
3589
  category: "Text",
3087
3590
  searchTerms: ["ordered"],
3088
3591
  command: ({ editor, range }) => {
@@ -3092,7 +3595,7 @@ const NUMBERED_LIST = {
3092
3595
  const QUOTE = {
3093
3596
  title: "Quote",
3094
3597
  description: "Block quote",
3095
- icon: TextQuote,
3598
+ icon: /* @__PURE__ */ jsx(TextQuote, { size: 20 }),
3096
3599
  category: "Text",
3097
3600
  searchTerms: ["blockquote"],
3098
3601
  command: ({ editor, range }) => {
@@ -3102,7 +3605,7 @@ const QUOTE = {
3102
3605
  const CODE = {
3103
3606
  title: "Code block",
3104
3607
  description: "Code snippet",
3105
- icon: SquareCode,
3608
+ icon: /* @__PURE__ */ jsx(SquareCode, { size: 20 }),
3106
3609
  category: "Text",
3107
3610
  searchTerms: ["codeblock"],
3108
3611
  command: ({ editor, range }) => {
@@ -3112,7 +3615,7 @@ const CODE = {
3112
3615
  const BUTTON = {
3113
3616
  title: "Button",
3114
3617
  description: "Clickable button",
3115
- icon: MousePointer,
3618
+ icon: /* @__PURE__ */ jsx(MousePointer, { size: 20 }),
3116
3619
  category: "Layout",
3117
3620
  searchTerms: ["button"],
3118
3621
  command: ({ editor, range }) => {
@@ -3122,7 +3625,7 @@ const BUTTON = {
3122
3625
  const DIVIDER = {
3123
3626
  title: "Divider",
3124
3627
  description: "Horizontal separator",
3125
- icon: SplitSquareVertical,
3628
+ icon: /* @__PURE__ */ jsx(SplitSquareVertical, { size: 20 }),
3126
3629
  category: "Layout",
3127
3630
  searchTerms: [
3128
3631
  "hr",
@@ -3136,7 +3639,7 @@ const DIVIDER = {
3136
3639
  const SECTION = {
3137
3640
  title: "Section",
3138
3641
  description: "Content section",
3139
- icon: Rows2,
3642
+ icon: /* @__PURE__ */ jsx(Rows2, { size: 20 }),
3140
3643
  category: "Layout",
3141
3644
  searchTerms: [
3142
3645
  "section",
@@ -3150,7 +3653,7 @@ const SECTION = {
3150
3653
  const TWO_COLUMNS = {
3151
3654
  title: "2 columns",
3152
3655
  description: "Two column layout",
3153
- icon: Columns2,
3656
+ icon: /* @__PURE__ */ jsx(Columns2, { size: 20 }),
3154
3657
  category: "Layout",
3155
3658
  searchTerms: [
3156
3659
  "columns",
@@ -3171,7 +3674,7 @@ const TWO_COLUMNS = {
3171
3674
  const THREE_COLUMNS = {
3172
3675
  title: "3 columns",
3173
3676
  description: "Three column layout",
3174
- icon: Columns3,
3677
+ icon: /* @__PURE__ */ jsx(Columns3, { size: 20 }),
3175
3678
  category: "Layout",
3176
3679
  searchTerms: [
3177
3680
  "columns",
@@ -3191,7 +3694,7 @@ const THREE_COLUMNS = {
3191
3694
  const FOUR_COLUMNS = {
3192
3695
  title: "4 columns",
3193
3696
  description: "Four column layout",
3194
- icon: Columns4,
3697
+ icon: /* @__PURE__ */ jsx(Columns4, { size: 20 }),
3195
3698
  category: "Layout",
3196
3699
  searchTerms: [
3197
3700
  "columns",
@@ -3343,5 +3846,5 @@ function createSlashCommand(options) {
3343
3846
  const SlashCommand = createSlashCommand();
3344
3847
 
3345
3848
  //#endregion
3346
- export { AlignmentAttribute, BULLET_LIST, BUTTON, Body, Bold, BubbleMenu, BubbleMenuAlignCenter, BubbleMenuAlignLeft, BubbleMenuAlignRight, BubbleMenuBold, BubbleMenuCode, BubbleMenuDefault, BubbleMenuItalic, BubbleMenuItem, BubbleMenuItemGroup, BubbleMenuLinkSelector, BubbleMenuNodeSelector, BubbleMenuRoot, BubbleMenuSeparator, BubbleMenuStrike, BubbleMenuUnderline, BubbleMenuUppercase, Button, ButtonBubbleMenu, ButtonBubbleMenuDefault, ButtonBubbleMenuEditLink, ButtonBubbleMenuRoot, ButtonBubbleMenuToolbar, CODE, COLUMN_PARENT_TYPES, ClassAttribute, CodeBlockPrism, ColumnsColumn, CommandList, DIVIDER, Div, EmailNode, FOUR_COLUMNS, FourColumns, H1, H2, H3, ImageBubbleMenu, ImageBubbleMenuDefault, ImageBubbleMenuEditLink, ImageBubbleMenuRoot, ImageBubbleMenuToolbar, LinkBubbleMenu, LinkBubbleMenuDefault, LinkBubbleMenuEditLink, LinkBubbleMenuForm, LinkBubbleMenuOpenLink, LinkBubbleMenuRoot, LinkBubbleMenuToolbar, LinkBubbleMenuUnlink, MAX_COLUMNS_DEPTH, MaxNesting, NUMBERED_LIST, NodeSelectorContent, NodeSelectorRoot, NodeSelectorTrigger, Placeholder, PreservedStyle, PreviewText, QUOTE, SECTION, Section, SlashCommand, StyleAttribute, Sup, TEXT, THREE_COLUMNS, TWO_COLUMNS, Table, TableCell, TableHeader, TableRow, ThreeColumns, TwoColumns, Uppercase, coreExtensions, createSlashCommand, defaultSlashCommands, editorEventBus, filterAndRankItems, getColumnsDepth, isAtMaxColumnsDepth, isInsideNode, processStylesForUnlink, scoreItem, setTextAlignment, useButtonBubbleMenuContext, useImageBubbleMenuContext, useLinkBubbleMenuContext };
3849
+ export { AlignmentAttribute, BULLET_LIST, BUTTON, Blockquote, Body, Bold, BubbleMenu, BubbleMenuAlignCenter, BubbleMenuAlignLeft, BubbleMenuAlignRight, BubbleMenuBold, BubbleMenuCode, BubbleMenuDefault, BubbleMenuItalic, BubbleMenuItem, BubbleMenuItemGroup, BubbleMenuLinkSelector, BubbleMenuNodeSelector, BubbleMenuRoot, BubbleMenuSeparator, BubbleMenuStrike, BubbleMenuUnderline, BubbleMenuUppercase, BulletList, Button, ButtonBubbleMenu, ButtonBubbleMenuDefault, ButtonBubbleMenuEditLink, ButtonBubbleMenuRoot, ButtonBubbleMenuToolbar, CODE, COLUMN_PARENT_TYPES, ClassAttribute, Code, CodeBlockPrism, ColumnsColumn, CommandList, DIVIDER, Div, EmailNode, FOUR_COLUMNS, FourColumns, H1, H2, H3, HardBreak, ImageBubbleMenu, ImageBubbleMenuDefault, ImageBubbleMenuEditLink, ImageBubbleMenuRoot, ImageBubbleMenuToolbar, Italic, LinkBubbleMenu, LinkBubbleMenuDefault, LinkBubbleMenuEditLink, LinkBubbleMenuForm, LinkBubbleMenuOpenLink, LinkBubbleMenuRoot, LinkBubbleMenuToolbar, LinkBubbleMenuUnlink, ListItem, MAX_COLUMNS_DEPTH, MaxNesting, NUMBERED_LIST, NodeSelectorContent, NodeSelectorRoot, NodeSelectorTrigger, OrderedList, Paragraph, Placeholder, PreservedStyle, PreviewText, QUOTE, SECTION, Section, SlashCommand, Strike, StyleAttribute, Sup, TEXT, THREE_COLUMNS, TWO_COLUMNS, Table, TableCell, TableHeader, TableRow, ThreeColumns, TwoColumns, Uppercase, composeReactEmail, coreExtensions, createSlashCommand, defaultSlashCommands, editorEventBus, filterAndRankItems, getColumnsDepth, isAtMaxColumnsDepth, isInsideNode, processStylesForUnlink, scoreItem, setTextAlignment, useButtonBubbleMenuContext, useEditor, useImageBubbleMenuContext, useLinkBubbleMenuContext };
3347
3850
  //# sourceMappingURL=index.mjs.map