@react-email/editor 0.0.0-experimental.6 → 0.0.0-experimental.8

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.js CHANGED
@@ -23,6 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  let __tiptap_core = require("@tiptap/core");
25
25
  __tiptap_core = __toESM(__tiptap_core);
26
+ let __tiptap_starter_kit = require("@tiptap/starter-kit");
27
+ __tiptap_starter_kit = __toESM(__tiptap_starter_kit);
26
28
  let react_jsx_runtime = require("react/jsx-runtime");
27
29
  react_jsx_runtime = __toESM(react_jsx_runtime);
28
30
  let __react_email_components = require("@react-email/components");
@@ -39,6 +41,16 @@ let prismjs = require("prismjs");
39
41
  prismjs = __toESM(prismjs);
40
42
  let __tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
41
43
  __tiptap_extension_placeholder = __toESM(__tiptap_extension_placeholder);
44
+ let __tiptap_react = require("@tiptap/react");
45
+ __tiptap_react = __toESM(__tiptap_react);
46
+ let lucide_react = require("lucide-react");
47
+ lucide_react = __toESM(lucide_react);
48
+ let react = require("react");
49
+ react = __toESM(react);
50
+ let __radix_ui_react_popover = require("@radix-ui/react-popover");
51
+ __radix_ui_react_popover = __toESM(__radix_ui_react_popover);
52
+ let __tiptap_react_menus = require("@tiptap/react/menus");
53
+ __tiptap_react_menus = __toESM(__tiptap_react_menus);
42
54
 
43
55
  //#region src/core/email-node.ts
44
56
  var EmailNode = class EmailNode extends __tiptap_core.Node {
@@ -70,6 +82,48 @@ var EmailNode = class EmailNode extends __tiptap_core.Node {
70
82
  }
71
83
  };
72
84
 
85
+ //#endregion
86
+ //#region src/core/event-bus.ts
87
+ const EVENT_PREFIX = "@react-email/editor:";
88
+ var EditorEventBus = class {
89
+ prefixEventName(eventName) {
90
+ return `${EVENT_PREFIX}${String(eventName)}`;
91
+ }
92
+ dispatch(eventName, payload, options) {
93
+ const target = options?.target ?? window;
94
+ const prefixedEventName = this.prefixEventName(eventName);
95
+ const event = new CustomEvent(prefixedEventName, {
96
+ detail: payload,
97
+ bubbles: false,
98
+ cancelable: false
99
+ });
100
+ target.dispatchEvent(event);
101
+ }
102
+ on(eventName, handler, options) {
103
+ const target = options?.target ?? window;
104
+ const prefixedEventName = this.prefixEventName(eventName);
105
+ const abortController = new AbortController();
106
+ const wrappedHandler = (event) => {
107
+ const customEvent = event;
108
+ const result = handler(customEvent.detail);
109
+ if (result instanceof Promise) result.catch((error) => {
110
+ console.error(`Error in async event handler for ${prefixedEventName}:`, {
111
+ event: customEvent.detail,
112
+ error
113
+ });
114
+ });
115
+ };
116
+ target.addEventListener(prefixedEventName, wrappedHandler, {
117
+ ...options,
118
+ signal: abortController.signal
119
+ });
120
+ return { unsubscribe: () => {
121
+ abortController.abort();
122
+ } };
123
+ }
124
+ };
125
+ const editorEventBus = new EditorEventBus();
126
+
73
127
  //#endregion
74
128
  //#region src/extensions/alignment-attribute.tsx
75
129
  const AlignmentAttribute = __tiptap_core.Extension.create({
@@ -404,12 +458,12 @@ const Body = EmailNode.create({
404
458
  0
405
459
  ];
406
460
  },
407
- renderToReactEmail({ children, node, styles }) {
461
+ renderToReactEmail({ children, node, style }) {
408
462
  const inlineStyles = inlineCssToJs(node.attrs?.style);
409
463
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
410
464
  className: node.attrs?.class || void 0,
411
465
  style: {
412
- ...styles.reset,
466
+ ...style,
413
467
  ...inlineStyles
414
468
  },
415
469
  children
@@ -565,7 +619,7 @@ const Button = EmailNode.create({
565
619
  }
566
620
  };
567
621
  },
568
- renderToReactEmail({ children, node, styles }) {
622
+ renderToReactEmail({ children, node, style }) {
569
623
  const inlineStyles = inlineCssToJs(node.attrs?.style);
570
624
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
571
625
  align: node.attrs?.align || node.attrs?.alignment,
@@ -573,8 +627,7 @@ const Button = EmailNode.create({
573
627
  className: node.attrs?.class || void 0,
574
628
  href: node.attrs?.href,
575
629
  style: {
576
- ...styles.reset,
577
- ...styles.button,
630
+ ...style,
578
631
  ...inlineStyles
579
632
  },
580
633
  children
@@ -803,6 +856,25 @@ const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.exte
803
856
  ]
804
857
  ];
805
858
  },
859
+ addKeyboardShortcuts() {
860
+ return {
861
+ ...this.parent?.(),
862
+ "Mod-a": ({ editor }) => {
863
+ const { state } = editor;
864
+ const { selection } = state;
865
+ const { $from } = selection;
866
+ for (let depth = $from.depth; depth >= 1; depth--) if ($from.node(depth).type.name === this.name) {
867
+ const blockStart = $from.start(depth);
868
+ const blockEnd = $from.end(depth);
869
+ if (selection.from === blockStart && selection.to === blockEnd) return false;
870
+ const tr = state.tr.setSelection(__tiptap_pm_state.TextSelection.create(state.doc, blockStart, blockEnd));
871
+ editor.view.dispatch(tr);
872
+ return true;
873
+ }
874
+ return false;
875
+ }
876
+ };
877
+ },
806
878
  addProseMirrorPlugins() {
807
879
  return [...this.parent?.() || [], PrismPlugin({
808
880
  name: this.name,
@@ -810,7 +882,7 @@ const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.exte
810
882
  defaultTheme: this.options.defaultTheme
811
883
  })];
812
884
  }
813
- }), ({ node, styles }) => {
885
+ }), ({ node, style }) => {
814
886
  const language = node.attrs?.language ? `${node.attrs.language}` : "javascript";
815
887
  const userTheme = __react_email_components[node.attrs?.theme];
816
888
  const theme = userTheme ? {
@@ -834,179 +906,9 @@ const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.exte
834
906
  theme,
835
907
  style: {
836
908
  width: "auto",
837
- ...styles.codeBlock
838
- }
839
- });
840
- });
841
-
842
- //#endregion
843
- //#region src/extensions/columns.tsx
844
- const COLUMN_PARENT_TYPES = [
845
- "twoColumns",
846
- "threeColumns",
847
- "fourColumns"
848
- ];
849
- const COLUMN_PARENT_SET = new Set(COLUMN_PARENT_TYPES);
850
- const MAX_COLUMNS_DEPTH = 3;
851
- function getColumnsDepth(doc, from) {
852
- const $from = doc.resolve(from);
853
- let depth = 0;
854
- for (let d = $from.depth; d > 0; d--) if (COLUMN_PARENT_SET.has($from.node(d).type.name)) depth++;
855
- return depth;
856
- }
857
- const VARIANTS = [
858
- {
859
- name: "twoColumns",
860
- columnCount: 2,
861
- content: "columnsColumn columnsColumn",
862
- dataType: "two-columns"
863
- },
864
- {
865
- name: "threeColumns",
866
- columnCount: 3,
867
- content: "columnsColumn columnsColumn columnsColumn",
868
- dataType: "three-columns"
869
- },
870
- {
871
- name: "fourColumns",
872
- columnCount: 4,
873
- content: "columnsColumn{4}",
874
- dataType: "four-columns"
875
- }
876
- ];
877
- const NODE_TYPE_MAP = {
878
- 2: "twoColumns",
879
- 3: "threeColumns",
880
- 4: "fourColumns"
881
- };
882
- function createColumnsNode(config, includeCommands) {
883
- return EmailNode.create({
884
- name: config.name,
885
- group: "block",
886
- content: config.content,
887
- isolating: true,
888
- defining: true,
889
- addAttributes() {
890
- return createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]);
891
- },
892
- parseHTML() {
893
- return [{ tag: `div[data-type="${config.dataType}"]` }];
894
- },
895
- renderHTML({ HTMLAttributes }) {
896
- return [
897
- "div",
898
- (0, __tiptap_core.mergeAttributes)({
899
- "data-type": config.dataType,
900
- class: "node-columns"
901
- }, HTMLAttributes),
902
- 0
903
- ];
904
- },
905
- ...includeCommands && { addCommands() {
906
- return { insertColumns: (count) => ({ commands, state }) => {
907
- if (getColumnsDepth(state.doc, state.selection.from) >= MAX_COLUMNS_DEPTH) return false;
908
- const nodeType = NODE_TYPE_MAP[count];
909
- const children = Array.from({ length: count }, () => ({
910
- type: "columnsColumn",
911
- content: [{
912
- type: "paragraph",
913
- content: []
914
- }]
915
- }));
916
- return commands.insertContent({
917
- type: nodeType,
918
- content: children
919
- });
920
- } };
921
- } },
922
- renderToReactEmail({ children, node, styles }) {
923
- const inlineStyles = inlineCssToJs(node.attrs?.style);
924
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, {
925
- className: node.attrs?.class || void 0,
926
- style: {
927
- ...styles.reset,
928
- ...inlineStyles
929
- },
930
- children
931
- });
909
+ ...style
932
910
  }
933
911
  });
934
- }
935
- const TwoColumns = createColumnsNode(VARIANTS[0], true);
936
- const ThreeColumns = createColumnsNode(VARIANTS[1], false);
937
- const FourColumns = createColumnsNode(VARIANTS[2], false);
938
- const ColumnsColumn = EmailNode.create({
939
- name: "columnsColumn",
940
- group: "columnsColumn",
941
- content: "block+",
942
- isolating: true,
943
- addAttributes() {
944
- return { ...createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]) };
945
- },
946
- parseHTML() {
947
- return [{ tag: "div[data-type=\"column\"]" }];
948
- },
949
- renderHTML({ HTMLAttributes }) {
950
- return [
951
- "div",
952
- (0, __tiptap_core.mergeAttributes)({
953
- "data-type": "column",
954
- class: "node-column"
955
- }, HTMLAttributes),
956
- 0
957
- ];
958
- },
959
- addKeyboardShortcuts() {
960
- return {
961
- Backspace: ({ editor }) => {
962
- const { state } = editor;
963
- const { selection } = state;
964
- const { empty, $from } = selection;
965
- if (!empty) return false;
966
- for (let depth = $from.depth; depth >= 1; depth--) {
967
- if ($from.pos !== $from.start(depth)) break;
968
- const indexInParent = $from.index(depth - 1);
969
- if (indexInParent === 0) continue;
970
- const prevNode = $from.node(depth - 1).child(indexInParent - 1);
971
- if (COLUMN_PARENT_SET.has(prevNode.type.name)) {
972
- const deleteFrom = $from.before(depth) - prevNode.nodeSize;
973
- const deleteTo = $from.before(depth);
974
- editor.view.dispatch(state.tr.delete(deleteFrom, deleteTo));
975
- return true;
976
- }
977
- break;
978
- }
979
- return false;
980
- },
981
- "Mod-a": ({ editor }) => {
982
- const { state } = editor;
983
- const { $from } = state.selection;
984
- for (let d = $from.depth; d > 0; d--) {
985
- if ($from.node(d).type.name !== "columnsColumn") continue;
986
- const columnStart = $from.start(d);
987
- const columnEnd = $from.end(d);
988
- const { from, to } = state.selection;
989
- if (from === columnStart && to === columnEnd) return false;
990
- editor.view.dispatch(state.tr.setSelection(__tiptap_pm_state.TextSelection.create(state.doc, columnStart, columnEnd)));
991
- return true;
992
- }
993
- return false;
994
- }
995
- };
996
- },
997
- renderToReactEmail({ children, node, styles }) {
998
- const inlineStyles = inlineCssToJs(node.attrs?.style);
999
- const width = node.attrs?.width;
1000
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
1001
- className: node.attrs?.class || void 0,
1002
- style: {
1003
- ...styles.reset,
1004
- ...inlineStyles,
1005
- ...width ? { width } : {}
1006
- },
1007
- children
1008
- });
1009
- }
1010
912
  });
1011
913
 
1012
914
  //#endregion
@@ -1041,12 +943,12 @@ const Div = EmailNode.create({
1041
943
  addAttributes() {
1042
944
  return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
1043
945
  },
1044
- renderToReactEmail({ children, node, styles }) {
946
+ renderToReactEmail({ children, node, style }) {
1045
947
  const inlineStyles = inlineCssToJs(node.attrs?.style);
1046
948
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1047
949
  className: node.attrs?.class || void 0,
1048
950
  style: {
1049
- ...styles.reset,
951
+ ...style,
1050
952
  ...inlineStyles
1051
953
  },
1052
954
  children
@@ -1297,14 +1199,14 @@ const Section = EmailNode.create({
1297
1199
  });
1298
1200
  } };
1299
1201
  },
1300
- renderToReactEmail({ children, node, styles }) {
1202
+ renderToReactEmail({ children, node, style }) {
1301
1203
  const inlineStyles = inlineCssToJs(node.attrs?.style);
1302
1204
  const textAlign = node.attrs?.align || node.attrs?.alignment;
1303
1205
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
1304
1206
  className: node.attrs?.class || void 0,
1305
1207
  align: textAlign,
1306
1208
  style: {
1307
- ...styles.section,
1209
+ ...style,
1308
1210
  ...inlineStyles,
1309
1211
  ...getTextAlignment(textAlign)
1310
1212
  },
@@ -1437,7 +1339,7 @@ const Table = EmailNode.create({
1437
1339
  ]
1438
1340
  ];
1439
1341
  },
1440
- renderToReactEmail({ children, node, styles }) {
1342
+ renderToReactEmail({ children, node, style }) {
1441
1343
  const inlineStyles = inlineCssToJs(node.attrs?.style);
1442
1344
  const alignment = node.attrs?.align || node.attrs?.alignment;
1443
1345
  const width = node.attrs?.width;
@@ -1448,7 +1350,7 @@ const Table = EmailNode.create({
1448
1350
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
1449
1351
  className: node.attrs?.class || void 0,
1450
1352
  align: alignment,
1451
- style: resolveConflictingStyles(styles.reset, {
1353
+ style: resolveConflictingStyles(style, {
1452
1354
  ...inlineStyles,
1453
1355
  ...centeringStyles
1454
1356
  }),
@@ -1489,12 +1391,12 @@ const TableRow = EmailNode.create({
1489
1391
  0
1490
1392
  ];
1491
1393
  },
1492
- renderToReactEmail({ children, node, styles }) {
1394
+ renderToReactEmail({ children, node, style }) {
1493
1395
  const inlineStyles = inlineCssToJs(node.attrs?.style);
1494
1396
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
1495
1397
  className: node.attrs?.class || void 0,
1496
1398
  style: {
1497
- ...styles.reset,
1399
+ ...style,
1498
1400
  ...inlineStyles
1499
1401
  },
1500
1402
  children
@@ -1534,13 +1436,13 @@ const TableCell = EmailNode.create({
1534
1436
  0
1535
1437
  ];
1536
1438
  },
1537
- renderToReactEmail({ children, node, styles }) {
1439
+ renderToReactEmail({ children, node, style }) {
1538
1440
  const inlineStyles = inlineCssToJs(node.attrs?.style);
1539
1441
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
1540
1442
  className: node.attrs?.class || void 0,
1541
1443
  align: node.attrs?.align || node.attrs?.alignment,
1542
1444
  style: {
1543
- ...styles.reset,
1445
+ ...style,
1544
1446
  ...inlineStyles
1545
1447
  },
1546
1448
  children
@@ -1583,10 +1485,949 @@ const TableHeader = __tiptap_core.Node.create({
1583
1485
  }
1584
1486
  });
1585
1487
 
1488
+ //#endregion
1489
+ //#region src/extensions/uppercase.ts
1490
+ const Uppercase = __tiptap_core.Mark.create({
1491
+ name: "uppercase",
1492
+ addOptions() {
1493
+ return { HTMLAttributes: {} };
1494
+ },
1495
+ parseHTML() {
1496
+ return [{
1497
+ tag: "span",
1498
+ getAttrs: (node) => {
1499
+ if (node.style.textTransform === "uppercase") return {};
1500
+ return false;
1501
+ }
1502
+ }];
1503
+ },
1504
+ renderHTML({ HTMLAttributes }) {
1505
+ return [
1506
+ "span",
1507
+ (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { style: "text-transform: uppercase" }),
1508
+ 0
1509
+ ];
1510
+ },
1511
+ addCommands() {
1512
+ return {
1513
+ setUppercase: () => ({ commands }) => {
1514
+ return commands.setMark(this.name);
1515
+ },
1516
+ toggleUppercase: () => ({ commands }) => {
1517
+ return commands.toggleMark(this.name);
1518
+ },
1519
+ unsetUppercase: () => ({ commands }) => {
1520
+ return commands.unsetMark(this.name);
1521
+ }
1522
+ };
1523
+ }
1524
+ });
1525
+
1526
+ //#endregion
1527
+ //#region src/extensions/columns.tsx
1528
+ const COLUMN_PARENT_TYPES = [
1529
+ "twoColumns",
1530
+ "threeColumns",
1531
+ "fourColumns"
1532
+ ];
1533
+ const COLUMN_PARENT_SET = new Set(COLUMN_PARENT_TYPES);
1534
+ const MAX_COLUMNS_DEPTH = 3;
1535
+ function getColumnsDepth(doc, from) {
1536
+ const $from = doc.resolve(from);
1537
+ let depth = 0;
1538
+ for (let d = $from.depth; d > 0; d--) if (COLUMN_PARENT_SET.has($from.node(d).type.name)) depth++;
1539
+ return depth;
1540
+ }
1541
+ const VARIANTS = [
1542
+ {
1543
+ name: "twoColumns",
1544
+ columnCount: 2,
1545
+ content: "columnsColumn columnsColumn",
1546
+ dataType: "two-columns"
1547
+ },
1548
+ {
1549
+ name: "threeColumns",
1550
+ columnCount: 3,
1551
+ content: "columnsColumn columnsColumn columnsColumn",
1552
+ dataType: "three-columns"
1553
+ },
1554
+ {
1555
+ name: "fourColumns",
1556
+ columnCount: 4,
1557
+ content: "columnsColumn{4}",
1558
+ dataType: "four-columns"
1559
+ }
1560
+ ];
1561
+ const NODE_TYPE_MAP = {
1562
+ 2: "twoColumns",
1563
+ 3: "threeColumns",
1564
+ 4: "fourColumns"
1565
+ };
1566
+ function createColumnsNode(config, includeCommands) {
1567
+ return EmailNode.create({
1568
+ name: config.name,
1569
+ group: "block",
1570
+ content: config.content,
1571
+ isolating: true,
1572
+ defining: true,
1573
+ addAttributes() {
1574
+ return createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]);
1575
+ },
1576
+ parseHTML() {
1577
+ return [{ tag: `div[data-type="${config.dataType}"]` }];
1578
+ },
1579
+ renderHTML({ HTMLAttributes }) {
1580
+ return [
1581
+ "div",
1582
+ (0, __tiptap_core.mergeAttributes)({
1583
+ "data-type": config.dataType,
1584
+ class: "node-columns"
1585
+ }, HTMLAttributes),
1586
+ 0
1587
+ ];
1588
+ },
1589
+ ...includeCommands && { addCommands() {
1590
+ return { insertColumns: (count) => ({ commands, state }) => {
1591
+ if (getColumnsDepth(state.doc, state.selection.from) >= MAX_COLUMNS_DEPTH) return false;
1592
+ const nodeType = NODE_TYPE_MAP[count];
1593
+ const children = Array.from({ length: count }, () => ({
1594
+ type: "columnsColumn",
1595
+ content: [{
1596
+ type: "paragraph",
1597
+ content: []
1598
+ }]
1599
+ }));
1600
+ return commands.insertContent({
1601
+ type: nodeType,
1602
+ content: children
1603
+ });
1604
+ } };
1605
+ } },
1606
+ renderToReactEmail({ children, node, style }) {
1607
+ const inlineStyles = inlineCssToJs(node.attrs?.style);
1608
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, {
1609
+ className: node.attrs?.class || void 0,
1610
+ style: {
1611
+ ...style,
1612
+ ...inlineStyles
1613
+ },
1614
+ children
1615
+ });
1616
+ }
1617
+ });
1618
+ }
1619
+ const TwoColumns = createColumnsNode(VARIANTS[0], true);
1620
+ const ThreeColumns = createColumnsNode(VARIANTS[1], false);
1621
+ const FourColumns = createColumnsNode(VARIANTS[2], false);
1622
+ const ColumnsColumn = EmailNode.create({
1623
+ name: "columnsColumn",
1624
+ group: "columnsColumn",
1625
+ content: "block+",
1626
+ isolating: true,
1627
+ addAttributes() {
1628
+ return { ...createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]) };
1629
+ },
1630
+ parseHTML() {
1631
+ return [{ tag: "div[data-type=\"column\"]" }];
1632
+ },
1633
+ renderHTML({ HTMLAttributes }) {
1634
+ return [
1635
+ "div",
1636
+ (0, __tiptap_core.mergeAttributes)({
1637
+ "data-type": "column",
1638
+ class: "node-column"
1639
+ }, HTMLAttributes),
1640
+ 0
1641
+ ];
1642
+ },
1643
+ addKeyboardShortcuts() {
1644
+ return {
1645
+ Backspace: ({ editor }) => {
1646
+ const { state } = editor;
1647
+ const { selection } = state;
1648
+ const { empty, $from } = selection;
1649
+ if (!empty) return false;
1650
+ for (let depth = $from.depth; depth >= 1; depth--) {
1651
+ if ($from.pos !== $from.start(depth)) break;
1652
+ const indexInParent = $from.index(depth - 1);
1653
+ if (indexInParent === 0) continue;
1654
+ const prevNode = $from.node(depth - 1).child(indexInParent - 1);
1655
+ if (COLUMN_PARENT_SET.has(prevNode.type.name)) {
1656
+ const deleteFrom = $from.before(depth) - prevNode.nodeSize;
1657
+ const deleteTo = $from.before(depth);
1658
+ editor.view.dispatch(state.tr.delete(deleteFrom, deleteTo));
1659
+ return true;
1660
+ }
1661
+ break;
1662
+ }
1663
+ return false;
1664
+ },
1665
+ "Mod-a": ({ editor }) => {
1666
+ const { state } = editor;
1667
+ const { $from } = state.selection;
1668
+ for (let d = $from.depth; d > 0; d--) {
1669
+ if ($from.node(d).type.name !== "columnsColumn") continue;
1670
+ const columnStart = $from.start(d);
1671
+ const columnEnd = $from.end(d);
1672
+ const { from, to } = state.selection;
1673
+ if (from === columnStart && to === columnEnd) return false;
1674
+ editor.view.dispatch(state.tr.setSelection(__tiptap_pm_state.TextSelection.create(state.doc, columnStart, columnEnd)));
1675
+ return true;
1676
+ }
1677
+ return false;
1678
+ }
1679
+ };
1680
+ },
1681
+ renderToReactEmail({ children, node, style }) {
1682
+ const inlineStyles = inlineCssToJs(node.attrs?.style);
1683
+ const width = node.attrs?.width;
1684
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
1685
+ className: node.attrs?.class || void 0,
1686
+ style: {
1687
+ ...style,
1688
+ ...inlineStyles,
1689
+ ...width ? { width } : {}
1690
+ },
1691
+ children
1692
+ });
1693
+ }
1694
+ });
1695
+
1696
+ //#endregion
1697
+ //#region src/extensions/index.ts
1698
+ const coreExtensions = [
1699
+ __tiptap_starter_kit.StarterKit.configure({
1700
+ undoRedo: false,
1701
+ heading: false,
1702
+ link: false,
1703
+ underline: false,
1704
+ trailingNode: false,
1705
+ bold: false,
1706
+ gapcursor: false,
1707
+ listItem: {},
1708
+ bulletList: { HTMLAttributes: { class: "node-bulletList" } },
1709
+ paragraph: { HTMLAttributes: { class: "node-paragraph" } },
1710
+ orderedList: { HTMLAttributes: { class: "node-orderedList" } },
1711
+ blockquote: { HTMLAttributes: { class: "node-blockquote" } },
1712
+ codeBlock: false,
1713
+ code: { HTMLAttributes: {
1714
+ class: "node-inlineCode",
1715
+ spellcheck: "false"
1716
+ } },
1717
+ horizontalRule: false,
1718
+ dropcursor: {
1719
+ color: "#61a8f8",
1720
+ class: "rounded-full animate-[fade-in_300ms_ease-in-out] !z-40",
1721
+ width: 4
1722
+ }
1723
+ }),
1724
+ CodeBlockPrism.configure({
1725
+ defaultLanguage: "javascript",
1726
+ HTMLAttributes: { class: "prism node-codeBlock" }
1727
+ }),
1728
+ Placeholder,
1729
+ PreviewText,
1730
+ Bold,
1731
+ Sup,
1732
+ Uppercase,
1733
+ PreservedStyle,
1734
+ Table,
1735
+ TableRow,
1736
+ TableCell,
1737
+ TableHeader,
1738
+ Body,
1739
+ Div,
1740
+ Button,
1741
+ Section,
1742
+ AlignmentAttribute.configure({ types: [
1743
+ "heading",
1744
+ "paragraph",
1745
+ "image",
1746
+ "blockquote",
1747
+ "codeBlock",
1748
+ "bulletList",
1749
+ "orderedList",
1750
+ "listItem",
1751
+ "button",
1752
+ "youtube",
1753
+ "twitter",
1754
+ "table",
1755
+ "tableRow",
1756
+ "tableCell",
1757
+ "tableHeader",
1758
+ "columnsColumn"
1759
+ ] }),
1760
+ StyleAttribute.configure({ types: [
1761
+ "heading",
1762
+ "paragraph",
1763
+ "image",
1764
+ "blockquote",
1765
+ "codeBlock",
1766
+ "bulletList",
1767
+ "orderedList",
1768
+ "listItem",
1769
+ "button",
1770
+ "youtube",
1771
+ "twitter",
1772
+ "horizontalRule",
1773
+ "footer",
1774
+ "section",
1775
+ "div",
1776
+ "body",
1777
+ "table",
1778
+ "tableRow",
1779
+ "tableCell",
1780
+ "tableHeader",
1781
+ "columnsColumn",
1782
+ "link"
1783
+ ] }),
1784
+ ClassAttribute.configure({ types: [
1785
+ "heading",
1786
+ "paragraph",
1787
+ "image",
1788
+ "blockquote",
1789
+ "bulletList",
1790
+ "orderedList",
1791
+ "listItem",
1792
+ "button",
1793
+ "youtube",
1794
+ "twitter",
1795
+ "horizontalRule",
1796
+ "footer",
1797
+ "section",
1798
+ "div",
1799
+ "body",
1800
+ "table",
1801
+ "tableRow",
1802
+ "tableCell",
1803
+ "tableHeader",
1804
+ "columnsColumn",
1805
+ "link"
1806
+ ] }),
1807
+ MaxNesting.configure({
1808
+ maxDepth: 50,
1809
+ nodeTypes: [
1810
+ "section",
1811
+ "bulletList",
1812
+ "orderedList"
1813
+ ]
1814
+ })
1815
+ ];
1816
+
1817
+ //#endregion
1818
+ //#region src/utils/set-text-alignment.ts
1819
+ function setTextAlignment(editor, alignment) {
1820
+ const { from, to } = editor.state.selection;
1821
+ const tr = editor.state.tr;
1822
+ editor.state.doc.nodesBetween(from, to, (node, pos) => {
1823
+ if (node.isTextblock) {
1824
+ const prop = "align" in node.attrs ? "align" : "alignment";
1825
+ tr.setNodeMarkup(pos, null, {
1826
+ ...node.attrs,
1827
+ [prop]: alignment
1828
+ });
1829
+ }
1830
+ });
1831
+ editor.view.dispatch(tr);
1832
+ }
1833
+
1834
+ //#endregion
1835
+ //#region src/ui/bubble-menu/context.tsx
1836
+ const BubbleMenuContext = react.createContext(null);
1837
+ function useBubbleMenuContext() {
1838
+ const context = react.useContext(BubbleMenuContext);
1839
+ if (!context) throw new Error("BubbleMenu compound components must be used within <BubbleMenu.Root>");
1840
+ return context;
1841
+ }
1842
+
1843
+ //#endregion
1844
+ //#region src/ui/bubble-menu/item.tsx
1845
+ function BubbleMenuItem({ name, isActive, onCommand, className, children,...rest }) {
1846
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1847
+ type: "button",
1848
+ "aria-label": name,
1849
+ "aria-pressed": isActive,
1850
+ className,
1851
+ "data-re-bubble-menu-item": "",
1852
+ "data-item": name,
1853
+ ...isActive ? { "data-active": "" } : {},
1854
+ onMouseDown: (e) => e.preventDefault(),
1855
+ onClick: onCommand,
1856
+ ...rest,
1857
+ children
1858
+ });
1859
+ }
1860
+
1861
+ //#endregion
1862
+ //#region src/ui/bubble-menu/align-center.tsx
1863
+ function BubbleMenuAlignCenter({ className, children }) {
1864
+ const { editor } = useBubbleMenuContext();
1865
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
1866
+ name: "align-center",
1867
+ isActive: (0, __tiptap_react.useEditorState)({
1868
+ editor,
1869
+ selector: ({ editor: editor$1 }) => editor$1?.isActive({ alignment: "center" }) ?? false
1870
+ }),
1871
+ onCommand: () => setTextAlignment(editor, "center"),
1872
+ className,
1873
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignCenterIcon, {})
1874
+ });
1875
+ }
1876
+
1877
+ //#endregion
1878
+ //#region src/ui/bubble-menu/align-left.tsx
1879
+ function BubbleMenuAlignLeft({ className, children }) {
1880
+ const { editor } = useBubbleMenuContext();
1881
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
1882
+ name: "align-left",
1883
+ isActive: (0, __tiptap_react.useEditorState)({
1884
+ editor,
1885
+ selector: ({ editor: editor$1 }) => editor$1?.isActive({ alignment: "left" }) ?? false
1886
+ }),
1887
+ onCommand: () => setTextAlignment(editor, "left"),
1888
+ className,
1889
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignLeftIcon, {})
1890
+ });
1891
+ }
1892
+
1893
+ //#endregion
1894
+ //#region src/ui/bubble-menu/align-right.tsx
1895
+ function BubbleMenuAlignRight({ className, children }) {
1896
+ const { editor } = useBubbleMenuContext();
1897
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
1898
+ name: "align-right",
1899
+ isActive: (0, __tiptap_react.useEditorState)({
1900
+ editor,
1901
+ selector: ({ editor: editor$1 }) => editor$1?.isActive({ alignment: "right" }) ?? false
1902
+ }),
1903
+ onCommand: () => setTextAlignment(editor, "right"),
1904
+ className,
1905
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlignRightIcon, {})
1906
+ });
1907
+ }
1908
+
1909
+ //#endregion
1910
+ //#region src/ui/bubble-menu/create-mark-bubble-item.tsx
1911
+ function createMarkBubbleItem(config) {
1912
+ function MarkBubbleItem({ className, children }) {
1913
+ const { editor } = useBubbleMenuContext();
1914
+ const isActive = (0, __tiptap_react.useEditorState)({
1915
+ editor,
1916
+ selector: ({ editor: editor$1 }) => {
1917
+ if (config.activeParams) return editor$1?.isActive(config.activeName, config.activeParams) ?? false;
1918
+ return editor$1?.isActive(config.activeName) ?? false;
1919
+ }
1920
+ });
1921
+ const handleCommand = () => {
1922
+ const chain = editor.chain().focus();
1923
+ const method = chain[config.command];
1924
+ if (method) method.call(chain).run();
1925
+ };
1926
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuItem, {
1927
+ name: config.name,
1928
+ isActive,
1929
+ onCommand: handleCommand,
1930
+ className,
1931
+ children: children ?? config.icon
1932
+ });
1933
+ }
1934
+ MarkBubbleItem.displayName = `BubbleMenu${config.name.charAt(0).toUpperCase() + config.name.slice(1)}`;
1935
+ return MarkBubbleItem;
1936
+ }
1937
+
1938
+ //#endregion
1939
+ //#region src/ui/bubble-menu/bold.tsx
1940
+ const BubbleMenuBold = createMarkBubbleItem({
1941
+ name: "bold",
1942
+ activeName: "bold",
1943
+ command: "toggleBold",
1944
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.BoldIcon, {})
1945
+ });
1946
+
1947
+ //#endregion
1948
+ //#region src/ui/bubble-menu/code.tsx
1949
+ const BubbleMenuCode = createMarkBubbleItem({
1950
+ name: "code",
1951
+ activeName: "code",
1952
+ command: "toggleCode",
1953
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CodeIcon, {})
1954
+ });
1955
+
1956
+ //#endregion
1957
+ //#region src/ui/bubble-menu/group.tsx
1958
+ function BubbleMenuItemGroup({ className, children }) {
1959
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("fieldset", {
1960
+ className,
1961
+ "data-re-bubble-menu-group": "",
1962
+ children
1963
+ });
1964
+ }
1965
+
1966
+ //#endregion
1967
+ //#region src/ui/bubble-menu/italic.tsx
1968
+ const BubbleMenuItalic = createMarkBubbleItem({
1969
+ name: "italic",
1970
+ activeName: "italic",
1971
+ command: "toggleItalic",
1972
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ItalicIcon, {})
1973
+ });
1974
+
1975
+ //#endregion
1976
+ //#region src/ui/bubble-menu/utils.ts
1977
+ const SAFE_PROTOCOLS = new Set([
1978
+ "http:",
1979
+ "https:",
1980
+ "mailto:",
1981
+ "tel:"
1982
+ ]);
1983
+ /**
1984
+ * Basic URL validation and auto-prefixing.
1985
+ * Rejects dangerous schemes (javascript:, data:, vbscript:, etc.).
1986
+ * Returns the valid URL string or null.
1987
+ */
1988
+ function getUrlFromString(str) {
1989
+ if (str === "#") return str;
1990
+ try {
1991
+ const url = new URL(str);
1992
+ if (SAFE_PROTOCOLS.has(url.protocol)) return str;
1993
+ return null;
1994
+ } catch {}
1995
+ try {
1996
+ if (str.includes(".") && !str.includes(" ")) return new URL(`https://${str}`).toString();
1997
+ } catch {}
1998
+ return null;
1999
+ }
2000
+
2001
+ //#endregion
2002
+ //#region src/ui/bubble-menu/link-selector.tsx
2003
+ function BubbleMenuLinkSelector({ className, showToggle = true, validateUrl, onLinkApply, onLinkRemove, children }) {
2004
+ const { editor } = useBubbleMenuContext();
2005
+ const [isOpen, setIsOpen] = react.useState(false);
2006
+ const editorState = (0, __tiptap_react.useEditorState)({
2007
+ editor,
2008
+ selector: ({ editor: editor$1 }) => ({
2009
+ isLinkActive: editor$1?.isActive("link") ?? false,
2010
+ hasLink: Boolean(editor$1?.getAttributes("link").href),
2011
+ currentHref: editor$1?.getAttributes("link").href || ""
2012
+ })
2013
+ });
2014
+ react.useEffect(() => {
2015
+ const subscription = editorEventBus.on("bubble-menu:add-link", () => {
2016
+ setIsOpen(true);
2017
+ });
2018
+ return () => {
2019
+ setIsOpen(false);
2020
+ subscription.unsubscribe();
2021
+ };
2022
+ }, []);
2023
+ if (!editorState) return null;
2024
+ const handleOpenLink = () => {
2025
+ setIsOpen(!isOpen);
2026
+ };
2027
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2028
+ "data-re-link-selector": "",
2029
+ ...isOpen ? { "data-open": "" } : {},
2030
+ ...editorState.hasLink ? { "data-has-link": "" } : {},
2031
+ className,
2032
+ children: [showToggle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2033
+ type: "button",
2034
+ "aria-expanded": isOpen,
2035
+ "aria-haspopup": "true",
2036
+ "aria-label": "Add link",
2037
+ "aria-pressed": editorState.isLinkActive && editorState.hasLink,
2038
+ "data-re-link-selector-trigger": "",
2039
+ onClick: handleOpenLink,
2040
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.LinkIcon, {})
2041
+ }), isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkForm, {
2042
+ editor,
2043
+ currentHref: editorState.currentHref,
2044
+ validateUrl,
2045
+ onLinkApply,
2046
+ onLinkRemove,
2047
+ setIsOpen,
2048
+ children
2049
+ })]
2050
+ });
2051
+ }
2052
+ function LinkForm({ editor, currentHref, validateUrl, onLinkApply, onLinkRemove, setIsOpen, children }) {
2053
+ const inputRef = react.useRef(null);
2054
+ const formRef = react.useRef(null);
2055
+ const displayHref = currentHref === "#" ? "" : currentHref;
2056
+ const [inputValue, setInputValue] = react.useState(displayHref);
2057
+ react.useEffect(() => {
2058
+ const timeoutId = setTimeout(() => {
2059
+ inputRef.current?.focus();
2060
+ }, 0);
2061
+ return () => clearTimeout(timeoutId);
2062
+ }, []);
2063
+ react.useEffect(() => {
2064
+ const handleKeyDown = (event) => {
2065
+ if (event.key === "Escape") {
2066
+ if (editor.getAttributes("link").href === "#") editor.chain().unsetLink().run();
2067
+ setIsOpen(false);
2068
+ }
2069
+ };
2070
+ const handleClickOutside = (event) => {
2071
+ if (formRef.current && !formRef.current.contains(event.target)) {
2072
+ const form = formRef.current;
2073
+ const submitEvent = new Event("submit", {
2074
+ bubbles: true,
2075
+ cancelable: true
2076
+ });
2077
+ form.dispatchEvent(submitEvent);
2078
+ setIsOpen(false);
2079
+ }
2080
+ };
2081
+ document.addEventListener("mousedown", handleClickOutside);
2082
+ window.addEventListener("keydown", handleKeyDown);
2083
+ return () => {
2084
+ window.removeEventListener("keydown", handleKeyDown);
2085
+ document.removeEventListener("mousedown", handleClickOutside);
2086
+ };
2087
+ }, [editor, setIsOpen]);
2088
+ function handleSubmit(e) {
2089
+ e.preventDefault();
2090
+ const value = inputValue.trim();
2091
+ if (value === "") {
2092
+ setLinkHref(editor, "");
2093
+ setIsOpen(false);
2094
+ focusEditor(editor);
2095
+ onLinkRemove?.();
2096
+ return;
2097
+ }
2098
+ const finalValue = (validateUrl ?? getUrlFromString)(value);
2099
+ if (!finalValue) {
2100
+ setLinkHref(editor, "");
2101
+ setIsOpen(false);
2102
+ focusEditor(editor);
2103
+ onLinkRemove?.();
2104
+ return;
2105
+ }
2106
+ setLinkHref(editor, finalValue);
2107
+ setIsOpen(false);
2108
+ focusEditor(editor);
2109
+ onLinkApply?.(finalValue);
2110
+ }
2111
+ function handleUnlink(e) {
2112
+ e.stopPropagation();
2113
+ setLinkHref(editor, "");
2114
+ setIsOpen(false);
2115
+ focusEditor(editor);
2116
+ onLinkRemove?.();
2117
+ }
2118
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2119
+ ref: formRef,
2120
+ "data-re-link-selector-form": "",
2121
+ onMouseDown: (e) => e.stopPropagation(),
2122
+ onClick: (e) => e.stopPropagation(),
2123
+ onKeyDown: (e) => e.stopPropagation(),
2124
+ onSubmit: handleSubmit,
2125
+ children: [
2126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2127
+ ref: inputRef,
2128
+ "data-re-link-selector-input": "",
2129
+ value: inputValue,
2130
+ onFocus: (e) => e.stopPropagation(),
2131
+ onChange: (e) => setInputValue(e.target.value),
2132
+ placeholder: "Paste a link",
2133
+ type: "text"
2134
+ }),
2135
+ children,
2136
+ displayHref ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2137
+ type: "button",
2138
+ "aria-label": "Remove link",
2139
+ "data-re-link-selector-unlink": "",
2140
+ onClick: handleUnlink,
2141
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.UnlinkIcon, {})
2142
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2143
+ type: "submit",
2144
+ "aria-label": "Apply link",
2145
+ "data-re-link-selector-apply": "",
2146
+ onMouseDown: (e) => e.stopPropagation(),
2147
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
2148
+ })
2149
+ ]
2150
+ });
2151
+ }
2152
+ function setLinkHref(editor, href) {
2153
+ if (href.length === 0) {
2154
+ editor.chain().unsetLink().run();
2155
+ return;
2156
+ }
2157
+ const { from, to } = editor.state.selection;
2158
+ if (from === to) {
2159
+ editor.chain().extendMarkRange("link").setLink({ href }).setTextSelection({
2160
+ from,
2161
+ to
2162
+ }).run();
2163
+ return;
2164
+ }
2165
+ editor.chain().setLink({ href }).run();
2166
+ }
2167
+ function focusEditor(editor) {
2168
+ setTimeout(() => {
2169
+ editor.commands.focus();
2170
+ }, 0);
2171
+ }
2172
+
2173
+ //#endregion
2174
+ //#region src/ui/bubble-menu/node-selector.tsx
2175
+ const NodeSelectorContext = react.createContext(null);
2176
+ function useNodeSelectorContext() {
2177
+ const context = react.useContext(NodeSelectorContext);
2178
+ if (!context) throw new Error("NodeSelector compound components must be used within <NodeSelector.Root>");
2179
+ return context;
2180
+ }
2181
+ function NodeSelectorRoot({ omit = [], open: controlledOpen, onOpenChange, className, children }) {
2182
+ const { editor } = useBubbleMenuContext();
2183
+ const [uncontrolledOpen, setUncontrolledOpen] = react.useState(false);
2184
+ const isControlled = controlledOpen !== void 0;
2185
+ const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
2186
+ const setIsOpen = react.useCallback((value) => {
2187
+ if (!isControlled) setUncontrolledOpen(value);
2188
+ onOpenChange?.(value);
2189
+ }, [isControlled, onOpenChange]);
2190
+ const editorState = (0, __tiptap_react.useEditorState)({
2191
+ editor,
2192
+ selector: ({ editor: editor$1 }) => ({
2193
+ isParagraphActive: (editor$1?.isActive("paragraph") ?? false) && !editor$1?.isActive("bulletList") && !editor$1?.isActive("orderedList"),
2194
+ isHeading1Active: editor$1?.isActive("heading", { level: 1 }) ?? false,
2195
+ isHeading2Active: editor$1?.isActive("heading", { level: 2 }) ?? false,
2196
+ isHeading3Active: editor$1?.isActive("heading", { level: 3 }) ?? false,
2197
+ isBulletListActive: editor$1?.isActive("bulletList") ?? false,
2198
+ isOrderedListActive: editor$1?.isActive("orderedList") ?? false,
2199
+ isBlockquoteActive: editor$1?.isActive("blockquote") ?? false,
2200
+ isCodeBlockActive: editor$1?.isActive("codeBlock") ?? false
2201
+ })
2202
+ });
2203
+ const allItems = react.useMemo(() => [
2204
+ {
2205
+ name: "Text",
2206
+ icon: lucide_react.TextIcon,
2207
+ command: () => editor.chain().focus().clearNodes().toggleNode("paragraph", "paragraph").run(),
2208
+ isActive: editorState?.isParagraphActive ?? false
2209
+ },
2210
+ {
2211
+ name: "Title",
2212
+ icon: lucide_react.Heading1,
2213
+ command: () => editor.chain().focus().clearNodes().toggleHeading({ level: 1 }).run(),
2214
+ isActive: editorState?.isHeading1Active ?? false
2215
+ },
2216
+ {
2217
+ name: "Subtitle",
2218
+ icon: lucide_react.Heading2,
2219
+ command: () => editor.chain().focus().clearNodes().toggleHeading({ level: 2 }).run(),
2220
+ isActive: editorState?.isHeading2Active ?? false
2221
+ },
2222
+ {
2223
+ name: "Heading",
2224
+ icon: lucide_react.Heading3,
2225
+ command: () => editor.chain().focus().clearNodes().toggleHeading({ level: 3 }).run(),
2226
+ isActive: editorState?.isHeading3Active ?? false
2227
+ },
2228
+ {
2229
+ name: "Bullet List",
2230
+ icon: lucide_react.List,
2231
+ command: () => editor.chain().focus().clearNodes().toggleBulletList().run(),
2232
+ isActive: editorState?.isBulletListActive ?? false
2233
+ },
2234
+ {
2235
+ name: "Numbered List",
2236
+ icon: lucide_react.ListOrdered,
2237
+ command: () => editor.chain().focus().clearNodes().toggleOrderedList().run(),
2238
+ isActive: editorState?.isOrderedListActive ?? false
2239
+ },
2240
+ {
2241
+ name: "Quote",
2242
+ icon: lucide_react.TextQuote,
2243
+ command: () => editor.chain().focus().clearNodes().toggleNode("paragraph", "paragraph").toggleBlockquote().run(),
2244
+ isActive: editorState?.isBlockquoteActive ?? false
2245
+ },
2246
+ {
2247
+ name: "Code",
2248
+ icon: lucide_react.Code,
2249
+ command: () => editor.chain().focus().clearNodes().toggleCodeBlock().run(),
2250
+ isActive: editorState?.isCodeBlockActive ?? false
2251
+ }
2252
+ ], [editor, editorState]);
2253
+ const items = react.useMemo(() => allItems.filter((item) => !omit.includes(item.name)), [allItems, omit]);
2254
+ const activeItem = react.useMemo(() => items.find((item) => item.isActive) ?? { name: "Multiple" }, [items]);
2255
+ const contextValue = react.useMemo(() => ({
2256
+ items,
2257
+ activeItem,
2258
+ isOpen,
2259
+ setIsOpen
2260
+ }), [
2261
+ items,
2262
+ activeItem,
2263
+ isOpen,
2264
+ setIsOpen
2265
+ ]);
2266
+ if (!editorState || items.length === 0) return null;
2267
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectorContext.Provider, {
2268
+ value: contextValue,
2269
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__radix_ui_react_popover.Root, {
2270
+ open: isOpen,
2271
+ onOpenChange: setIsOpen,
2272
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2273
+ "data-re-node-selector": "",
2274
+ ...isOpen ? { "data-open": "" } : {},
2275
+ className,
2276
+ children
2277
+ })
2278
+ })
2279
+ });
2280
+ }
2281
+ function NodeSelectorTrigger({ className, children }) {
2282
+ const { activeItem, isOpen, setIsOpen } = useNodeSelectorContext();
2283
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__radix_ui_react_popover.Trigger, {
2284
+ "data-re-node-selector-trigger": "",
2285
+ className,
2286
+ onClick: () => setIsOpen(!isOpen),
2287
+ children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: activeItem.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {})] })
2288
+ });
2289
+ }
2290
+ function NodeSelectorContent({ className, align = "start", children }) {
2291
+ const { items, setIsOpen } = useNodeSelectorContext();
2292
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__radix_ui_react_popover.Content, {
2293
+ align,
2294
+ "data-re-node-selector-content": "",
2295
+ className,
2296
+ children: children ? children(items, () => setIsOpen(false)) : items.map((item) => {
2297
+ const Icon = item.icon;
2298
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2299
+ type: "button",
2300
+ "data-re-node-selector-item": "",
2301
+ ...item.isActive ? { "data-active": "" } : {},
2302
+ onClick: () => {
2303
+ item.command();
2304
+ setIsOpen(false);
2305
+ },
2306
+ children: [
2307
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {}),
2308
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.name }),
2309
+ item.isActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {})
2310
+ ]
2311
+ }, item.name);
2312
+ })
2313
+ });
2314
+ }
2315
+ function BubbleMenuNodeSelector({ omit = [], className, triggerContent, open, onOpenChange }) {
2316
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(NodeSelectorRoot, {
2317
+ omit,
2318
+ open,
2319
+ onOpenChange,
2320
+ className,
2321
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectorTrigger, { children: triggerContent }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectorContent, {})]
2322
+ });
2323
+ }
2324
+
2325
+ //#endregion
2326
+ //#region src/ui/bubble-menu/root.tsx
2327
+ function BubbleMenuRoot({ excludeNodes = [], placement = "bottom", offset = 8, onHide, className, children }) {
2328
+ const { editor } = (0, __tiptap_react.useCurrentEditor)();
2329
+ if (!editor) return null;
2330
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__tiptap_react_menus.BubbleMenu, {
2331
+ editor,
2332
+ "data-re-bubble-menu": "",
2333
+ shouldShow: ({ editor: editor$1, view }) => {
2334
+ for (const node of excludeNodes) if (editor$1.isActive(node)) return false;
2335
+ if (view.dom.classList.contains("dragging")) return false;
2336
+ return editor$1.view.state.selection.content().size > 0;
2337
+ },
2338
+ options: {
2339
+ placement,
2340
+ offset,
2341
+ onHide
2342
+ },
2343
+ className,
2344
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BubbleMenuContext.Provider, {
2345
+ value: { editor },
2346
+ children
2347
+ })
2348
+ });
2349
+ }
2350
+
2351
+ //#endregion
2352
+ //#region src/ui/bubble-menu/separator.tsx
2353
+ function BubbleMenuSeparator({ className }) {
2354
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("hr", {
2355
+ className,
2356
+ "data-re-bubble-menu-separator": ""
2357
+ });
2358
+ }
2359
+
2360
+ //#endregion
2361
+ //#region src/ui/bubble-menu/strike.tsx
2362
+ const BubbleMenuStrike = createMarkBubbleItem({
2363
+ name: "strike",
2364
+ activeName: "strike",
2365
+ command: "toggleStrike",
2366
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.StrikethroughIcon, {})
2367
+ });
2368
+
2369
+ //#endregion
2370
+ //#region src/ui/bubble-menu/underline.tsx
2371
+ const BubbleMenuUnderline = createMarkBubbleItem({
2372
+ name: "underline",
2373
+ activeName: "underline",
2374
+ command: "toggleUnderline",
2375
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.UnderlineIcon, {})
2376
+ });
2377
+
2378
+ //#endregion
2379
+ //#region src/ui/bubble-menu/uppercase.tsx
2380
+ const BubbleMenuUppercase = createMarkBubbleItem({
2381
+ name: "uppercase",
2382
+ activeName: "uppercase",
2383
+ command: "toggleUppercase",
2384
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CaseUpperIcon, {})
2385
+ });
2386
+
2387
+ //#endregion
2388
+ //#region src/ui/bubble-menu/index.ts
2389
+ const BubbleMenu = {
2390
+ Root: BubbleMenuRoot,
2391
+ ItemGroup: BubbleMenuItemGroup,
2392
+ Separator: BubbleMenuSeparator,
2393
+ Item: BubbleMenuItem,
2394
+ Bold: BubbleMenuBold,
2395
+ Italic: BubbleMenuItalic,
2396
+ Underline: BubbleMenuUnderline,
2397
+ Strike: BubbleMenuStrike,
2398
+ Code: BubbleMenuCode,
2399
+ Uppercase: BubbleMenuUppercase,
2400
+ AlignLeft: BubbleMenuAlignLeft,
2401
+ AlignCenter: BubbleMenuAlignCenter,
2402
+ AlignRight: BubbleMenuAlignRight,
2403
+ NodeSelector: Object.assign(BubbleMenuNodeSelector, {
2404
+ Root: NodeSelectorRoot,
2405
+ Trigger: NodeSelectorTrigger,
2406
+ Content: NodeSelectorContent
2407
+ }),
2408
+ LinkSelector: BubbleMenuLinkSelector
2409
+ };
2410
+
1586
2411
  //#endregion
1587
2412
  exports.AlignmentAttribute = AlignmentAttribute;
1588
2413
  exports.Body = Body;
1589
2414
  exports.Bold = Bold;
2415
+ exports.BubbleMenu = BubbleMenu;
2416
+ exports.BubbleMenuAlignCenter = BubbleMenuAlignCenter;
2417
+ exports.BubbleMenuAlignLeft = BubbleMenuAlignLeft;
2418
+ exports.BubbleMenuAlignRight = BubbleMenuAlignRight;
2419
+ exports.BubbleMenuBold = BubbleMenuBold;
2420
+ exports.BubbleMenuCode = BubbleMenuCode;
2421
+ exports.BubbleMenuItalic = BubbleMenuItalic;
2422
+ exports.BubbleMenuItem = BubbleMenuItem;
2423
+ exports.BubbleMenuItemGroup = BubbleMenuItemGroup;
2424
+ exports.BubbleMenuLinkSelector = BubbleMenuLinkSelector;
2425
+ exports.BubbleMenuNodeSelector = BubbleMenuNodeSelector;
2426
+ exports.BubbleMenuRoot = BubbleMenuRoot;
2427
+ exports.BubbleMenuSeparator = BubbleMenuSeparator;
2428
+ exports.BubbleMenuStrike = BubbleMenuStrike;
2429
+ exports.BubbleMenuUnderline = BubbleMenuUnderline;
2430
+ exports.BubbleMenuUppercase = BubbleMenuUppercase;
1590
2431
  exports.Button = Button;
1591
2432
  exports.COLUMN_PARENT_TYPES = COLUMN_PARENT_TYPES;
1592
2433
  exports.ClassAttribute = ClassAttribute;
@@ -1597,6 +2438,9 @@ exports.EmailNode = EmailNode;
1597
2438
  exports.FourColumns = FourColumns;
1598
2439
  exports.MAX_COLUMNS_DEPTH = MAX_COLUMNS_DEPTH;
1599
2440
  exports.MaxNesting = MaxNesting;
2441
+ exports.NodeSelectorContent = NodeSelectorContent;
2442
+ exports.NodeSelectorRoot = NodeSelectorRoot;
2443
+ exports.NodeSelectorTrigger = NodeSelectorTrigger;
1600
2444
  exports.Placeholder = Placeholder;
1601
2445
  exports.PreservedStyle = PreservedStyle;
1602
2446
  exports.PreviewText = PreviewText;
@@ -1609,5 +2453,9 @@ exports.TableHeader = TableHeader;
1609
2453
  exports.TableRow = TableRow;
1610
2454
  exports.ThreeColumns = ThreeColumns;
1611
2455
  exports.TwoColumns = TwoColumns;
2456
+ exports.Uppercase = Uppercase;
2457
+ exports.coreExtensions = coreExtensions;
2458
+ exports.editorEventBus = editorEventBus;
1612
2459
  exports.getColumnsDepth = getColumnsDepth;
1613
- exports.processStylesForUnlink = processStylesForUnlink;
2460
+ exports.processStylesForUnlink = processStylesForUnlink;
2461
+ exports.setTextAlignment = setTextAlignment;