@react-email/editor 0.0.0-experimental.4 → 0.0.0-experimental.40

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.
Files changed (74) hide show
  1. package/dist/core/index.cjs +9 -0
  2. package/dist/core/index.d.cts +2 -0
  3. package/dist/core/index.d.mts +3 -0
  4. package/dist/core/index.mjs +4 -0
  5. package/dist/create-paste-handler-B8BtjBk3.d.cts +14 -0
  6. package/dist/create-paste-handler-B8BtjBk3.d.cts.map +1 -0
  7. package/dist/create-paste-handler-CGR738bC.d.mts +14 -0
  8. package/dist/create-paste-handler-CGR738bC.d.mts.map +1 -0
  9. package/dist/event-bus-CHEzOS_O.mjs +329 -0
  10. package/dist/event-bus-CHEzOS_O.mjs.map +1 -0
  11. package/dist/event-bus-fb8U7hrl.cjs +450 -0
  12. package/dist/extension-DyY8_bh4.mjs +1110 -0
  13. package/dist/extension-DyY8_bh4.mjs.map +1 -0
  14. package/dist/extension-w5VaUeSw.cjs +1235 -0
  15. package/dist/extensions/index.cjs +51 -0
  16. package/dist/extensions/index.d.cts +399 -0
  17. package/dist/extensions/index.d.cts.map +1 -0
  18. package/dist/extensions/index.d.mts +400 -0
  19. package/dist/extensions/index.d.mts.map +1 -0
  20. package/dist/extensions/index.mjs +5 -0
  21. package/dist/extensions-BvfmaKCn.mjs +2088 -0
  22. package/dist/extensions-BvfmaKCn.mjs.map +1 -0
  23. package/dist/extensions-CkjPj2JO.cjs +2369 -0
  24. package/dist/global-content-D_WYaFgX.mjs +78 -0
  25. package/dist/global-content-D_WYaFgX.mjs.map +1 -0
  26. package/dist/global-content-bJgotqmA.cjs +89 -0
  27. package/dist/index-C4KcMQ0R.d.cts +161 -0
  28. package/dist/index-C4KcMQ0R.d.cts.map +1 -0
  29. package/dist/index-CxX7W63O.d.mts +161 -0
  30. package/dist/index-CxX7W63O.d.mts.map +1 -0
  31. package/dist/index.cjs +74 -0
  32. package/dist/index.css +832 -0
  33. package/dist/index.css.map +1 -0
  34. package/dist/index.d.cts +33 -0
  35. package/dist/index.d.cts.map +1 -0
  36. package/dist/index.d.mts +31 -277
  37. package/dist/index.d.mts.map +1 -1
  38. package/dist/index.mjs +64 -1377
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/plugins/index.cjs +23 -0
  41. package/dist/plugins/index.d.cts +191 -0
  42. package/dist/plugins/index.d.cts.map +1 -0
  43. package/dist/plugins/index.d.mts +191 -0
  44. package/dist/plugins/index.d.mts.map +1 -0
  45. package/dist/plugins/index.mjs +3 -0
  46. package/dist/root-CkYaJZpj.mjs +2316 -0
  47. package/dist/root-CkYaJZpj.mjs.map +1 -0
  48. package/dist/root-Gu08xybW.cjs +2832 -0
  49. package/dist/set-text-alignment-Cv72txmv.cjs +24 -0
  50. package/dist/set-text-alignment-OA8IMWmO.mjs +19 -0
  51. package/dist/set-text-alignment-OA8IMWmO.mjs.map +1 -0
  52. package/dist/styles-C-cCyJCn.cjs +211 -0
  53. package/dist/styles-_TMw3YxC.mjs +194 -0
  54. package/dist/styles-_TMw3YxC.mjs.map +1 -0
  55. package/dist/ui/bubble-menu/bubble-menu.css +285 -0
  56. package/dist/ui/index.cjs +147 -0
  57. package/dist/ui/index.d.cts +939 -0
  58. package/dist/ui/index.d.cts.map +1 -0
  59. package/dist/ui/index.d.mts +939 -0
  60. package/dist/ui/index.d.mts.map +1 -0
  61. package/dist/ui/index.mjs +60 -0
  62. package/dist/ui/index.mjs.map +1 -0
  63. package/dist/ui/slash-command/slash-command.css +44 -0
  64. package/dist/ui/themes/default.css +830 -0
  65. package/dist/utils/index.cjs +3 -0
  66. package/dist/utils/index.d.cts +7 -0
  67. package/dist/utils/index.d.cts.map +1 -0
  68. package/dist/utils/index.d.mts +7 -0
  69. package/dist/utils/index.d.mts.map +1 -0
  70. package/dist/utils/index.mjs +3 -0
  71. package/package.json +109 -21
  72. package/dist/index.d.ts +0 -279
  73. package/dist/index.d.ts.map +0 -1
  74. package/dist/index.js +0 -1436
@@ -0,0 +1,9 @@
1
+ const require_event_bus = require('../event-bus-fb8U7hrl.cjs');
2
+ const require_extensions = require('../extensions-CkjPj2JO.cjs');
3
+
4
+ exports.EmailMark = require_extensions.EmailMark;
5
+ exports.EmailNode = require_event_bus.EmailNode;
6
+ exports.composeReactEmail = require_extensions.composeReactEmail;
7
+ exports.editorEventBus = require_event_bus.editorEventBus;
8
+ exports.isDocumentVisuallyEmpty = require_extensions.isDocumentVisuallyEmpty;
9
+ exports.useEditor = require_extensions.useEditor;
@@ -0,0 +1,2 @@
1
+ import { a as EmailMark, c as SerializedMark, d as EditorEventHandler, f as EditorEventMap, h as editorEventBus, i as NodeRendererComponent, l as composeReactEmail, m as EditorEventSubscription, n as EmailNode, o as EmailMarkConfig, p as EditorEventName, r as EmailNodeConfig, s as MarkRendererComponent, t as useEditor, u as isDocumentVisuallyEmpty } from "../index-C4KcMQ0R.cjs";
2
+ export { EditorEventHandler, EditorEventMap, EditorEventName, EditorEventSubscription, EmailMark, EmailMarkConfig, EmailNode, EmailNodeConfig, MarkRendererComponent, NodeRendererComponent, SerializedMark, composeReactEmail, editorEventBus, isDocumentVisuallyEmpty, useEditor };
@@ -0,0 +1,3 @@
1
+ import { a as EmailMark, c as SerializedMark, d as EditorEventHandler, f as EditorEventMap, h as editorEventBus, i as NodeRendererComponent, l as composeReactEmail, m as EditorEventSubscription, n as EmailNode, o as EmailMarkConfig, p as EditorEventName, r as EmailNodeConfig, s as MarkRendererComponent, t as useEditor, u as isDocumentVisuallyEmpty } from "../index-CxX7W63O.mjs";
2
+ import "../create-paste-handler-CGR738bC.mjs";
3
+ export { EditorEventHandler, EditorEventMap, EditorEventName, EditorEventSubscription, EmailMark, EmailMarkConfig, EmailNode, EmailNodeConfig, MarkRendererComponent, NodeRendererComponent, SerializedMark, composeReactEmail, editorEventBus, isDocumentVisuallyEmpty, useEditor };
@@ -0,0 +1,4 @@
1
+ import { H as EmailMark, O as useEditor, V as composeReactEmail, k as isDocumentVisuallyEmpty } from "../extensions-BvfmaKCn.mjs";
2
+ import { h as EmailNode, t as editorEventBus } from "../event-bus-CHEzOS_O.mjs";
3
+
4
+ export { EmailMark, EmailNode, composeReactEmail, editorEventBus, isDocumentVisuallyEmpty, useEditor };
@@ -0,0 +1,14 @@
1
+ import { Extensions } from "@tiptap/core";
2
+ import { EditorView } from "@tiptap/pm/view";
3
+
4
+ //#region src/core/create-paste-handler.d.ts
5
+ type PasteHandler = (payload: string | File, view: EditorView) => boolean;
6
+ type UploadImageHandler = (file: File, view: EditorView, pos: number, preserveAttributes?: {
7
+ width?: string;
8
+ height?: string;
9
+ alignment?: string;
10
+ href?: string;
11
+ }) => void | Promise<void>;
12
+ //#endregion
13
+ export { UploadImageHandler as n, PasteHandler as t };
14
+ //# sourceMappingURL=create-paste-handler-B8BtjBk3.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-paste-handler-B8BtjBk3.d.cts","names":[],"sources":["../src/core/create-paste-handler.ts"],"sourcesContent":[],"mappings":";;;;KAMY,YAAA,sBACQ,YACZ;AAFI,KAKA,kBAAA,GAJQ,CAAA,IACZ,EAIA,IAJA,EAAA,IAAU,EAKV,UALU,EAAA,GAAA,EAAA,MAAA,EAAA,kBAaC,CAbD,EAAA;EAGN,KAAA,CAAA,EAAA,MAAA;EACJ,MAAA,CAAA,EAAA,MAAA;EACA,SAAA,CAAA,EAAA,MAAA;EAQI,IAAA,CAAA,EAAA,MAAA;CAAO,EAAA,GAAA,IAAA,GAAP,OAAO,CAAA,IAAA,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { Extensions } from "@tiptap/core";
2
+ import { EditorView } from "@tiptap/pm/view";
3
+
4
+ //#region src/core/create-paste-handler.d.ts
5
+ type PasteHandler = (payload: string | File, view: EditorView) => boolean;
6
+ type UploadImageHandler = (file: File, view: EditorView, pos: number, preserveAttributes?: {
7
+ width?: string;
8
+ height?: string;
9
+ alignment?: string;
10
+ href?: string;
11
+ }) => void | Promise<void>;
12
+ //#endregion
13
+ export { UploadImageHandler as n, PasteHandler as t };
14
+ //# sourceMappingURL=create-paste-handler-CGR738bC.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-paste-handler-CGR738bC.d.mts","names":[],"sources":["../src/core/create-paste-handler.ts"],"sourcesContent":[],"mappings":";;;;KAMY,YAAA,sBACQ,YACZ;AAFI,KAKA,kBAAA,GAJQ,CAAA,IACZ,EAIA,IAJA,EAAA,IAAU,EAKV,UALU,EAAA,GAAA,EAAA,MAAA,EAAA,kBAaC,CAbD,EAAA;EAGN,KAAA,CAAA,EAAA,MAAA;EACJ,MAAA,CAAA,EAAA,MAAA;EACA,SAAA,CAAA,EAAA,MAAA;EAQI,IAAA,CAAA,EAAA,MAAA;CAAO,EAAA,GAAA,IAAA,GAAP,OAAO,CAAA,IAAA,CAAA"}
@@ -0,0 +1,329 @@
1
+ import { n as inlineCssToJs } from "./styles-_TMw3YxC.mjs";
2
+ import { Column, Row } from "@react-email/components";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { Node, mergeAttributes } from "@tiptap/core";
5
+ import { TextSelection } from "@tiptap/pm/state";
6
+
7
+ //#region src/core/serializer/email-node.ts
8
+ var EmailNode = class EmailNode extends Node {
9
+ constructor(config) {
10
+ super(config);
11
+ }
12
+ /**
13
+ * Create a new Node instance
14
+ * @param config - Node configuration object or a function that returns a configuration object
15
+ */
16
+ static create(config) {
17
+ return new EmailNode(typeof config === "function" ? config() : config);
18
+ }
19
+ static from(node, renderToReactEmail) {
20
+ const customNode = EmailNode.create({});
21
+ Object.assign(customNode, { ...node });
22
+ customNode.config = {
23
+ ...node.config,
24
+ renderToReactEmail
25
+ };
26
+ return customNode;
27
+ }
28
+ configure(options) {
29
+ return super.configure(options);
30
+ }
31
+ extend(extendedConfig) {
32
+ const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
33
+ return super.extend(resolvedConfig);
34
+ }
35
+ };
36
+
37
+ //#endregion
38
+ //#region src/utils/attribute-helpers.ts
39
+ /**
40
+ * Creates TipTap attribute definitions for a list of HTML attributes.
41
+ * Each attribute will have the same pattern:
42
+ * - default: null
43
+ * - parseHTML: extracts the attribute from the element
44
+ * - renderHTML: conditionally renders the attribute if it has a value
45
+ *
46
+ * @param attributeNames - Array of HTML attribute names to create definitions for
47
+ * @returns Object with TipTap attribute definitions
48
+ *
49
+ * @example
50
+ * const attrs = createStandardAttributes(['class', 'id', 'title']);
51
+ * // Returns:
52
+ * // {
53
+ * // class: {
54
+ * // default: null,
55
+ * // parseHTML: (element) => element.getAttribute('class'),
56
+ * // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
57
+ * // },
58
+ * // ...
59
+ * // }
60
+ */
61
+ function createStandardAttributes(attributeNames) {
62
+ return Object.fromEntries(attributeNames.map((attr) => [attr, {
63
+ default: null,
64
+ parseHTML: (element) => element.getAttribute(attr),
65
+ renderHTML: (attributes) => {
66
+ if (!attributes[attr]) return {};
67
+ return { [attr]: attributes[attr] };
68
+ }
69
+ }]));
70
+ }
71
+ /**
72
+ * Common HTML attributes used across multiple extensions.
73
+ * These preserve attributes during HTML import and editing for better
74
+ * fidelity when importing existing email templates.
75
+ */
76
+ const COMMON_HTML_ATTRIBUTES = [
77
+ "id",
78
+ "class",
79
+ "title",
80
+ "lang",
81
+ "dir",
82
+ "data-id"
83
+ ];
84
+ /**
85
+ * Layout-specific HTML attributes used for positioning and sizing.
86
+ */
87
+ const LAYOUT_ATTRIBUTES = [
88
+ "align",
89
+ "width",
90
+ "height"
91
+ ];
92
+ /**
93
+ * Table-specific HTML attributes used for table layout and styling.
94
+ */
95
+ const TABLE_ATTRIBUTES = [
96
+ "border",
97
+ "cellpadding",
98
+ "cellspacing"
99
+ ];
100
+ /**
101
+ * Table cell-specific HTML attributes.
102
+ */
103
+ const TABLE_CELL_ATTRIBUTES = [
104
+ "valign",
105
+ "bgcolor",
106
+ "colspan",
107
+ "rowspan"
108
+ ];
109
+ /**
110
+ * Table header cell-specific HTML attributes.
111
+ * These are additional attributes that only apply to <th> elements.
112
+ */
113
+ const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
114
+
115
+ //#endregion
116
+ //#region src/extensions/columns.tsx
117
+ const COLUMN_PARENT_TYPES = [
118
+ "twoColumns",
119
+ "threeColumns",
120
+ "fourColumns"
121
+ ];
122
+ const COLUMN_PARENT_SET = new Set(COLUMN_PARENT_TYPES);
123
+ const MAX_COLUMNS_DEPTH = 3;
124
+ function getColumnsDepth(doc, from) {
125
+ const $from = doc.resolve(from);
126
+ let depth = 0;
127
+ for (let d = $from.depth; d > 0; d--) if (COLUMN_PARENT_SET.has($from.node(d).type.name)) depth++;
128
+ return depth;
129
+ }
130
+ const VARIANTS = [
131
+ {
132
+ name: "twoColumns",
133
+ columnCount: 2,
134
+ content: "columnsColumn columnsColumn",
135
+ dataType: "two-columns"
136
+ },
137
+ {
138
+ name: "threeColumns",
139
+ columnCount: 3,
140
+ content: "columnsColumn columnsColumn columnsColumn",
141
+ dataType: "three-columns"
142
+ },
143
+ {
144
+ name: "fourColumns",
145
+ columnCount: 4,
146
+ content: "columnsColumn{4}",
147
+ dataType: "four-columns"
148
+ }
149
+ ];
150
+ const NODE_TYPE_MAP = {
151
+ 2: "twoColumns",
152
+ 3: "threeColumns",
153
+ 4: "fourColumns"
154
+ };
155
+ function createColumnsNode(config, includeCommands) {
156
+ return EmailNode.create({
157
+ name: config.name,
158
+ group: "block",
159
+ content: config.content,
160
+ isolating: true,
161
+ defining: true,
162
+ addAttributes() {
163
+ return createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]);
164
+ },
165
+ parseHTML() {
166
+ return [{ tag: `div[data-type="${config.dataType}"]` }];
167
+ },
168
+ renderHTML({ HTMLAttributes }) {
169
+ return [
170
+ "div",
171
+ mergeAttributes({
172
+ "data-type": config.dataType,
173
+ class: "node-columns"
174
+ }, HTMLAttributes),
175
+ 0
176
+ ];
177
+ },
178
+ ...includeCommands && { addCommands() {
179
+ return { insertColumns: (count) => ({ commands, state }) => {
180
+ if (getColumnsDepth(state.doc, state.selection.from) >= MAX_COLUMNS_DEPTH) return false;
181
+ const nodeType = NODE_TYPE_MAP[count];
182
+ const children = Array.from({ length: count }, () => ({
183
+ type: "columnsColumn",
184
+ content: [{
185
+ type: "paragraph",
186
+ content: []
187
+ }]
188
+ }));
189
+ return commands.insertContent({
190
+ type: nodeType,
191
+ content: children
192
+ });
193
+ } };
194
+ } },
195
+ renderToReactEmail({ children, node, style }) {
196
+ const inlineStyles = inlineCssToJs(node.attrs?.style);
197
+ return /* @__PURE__ */ jsx(Row, {
198
+ className: node.attrs?.class || void 0,
199
+ style: {
200
+ ...style,
201
+ ...inlineStyles
202
+ },
203
+ children
204
+ });
205
+ }
206
+ });
207
+ }
208
+ const TwoColumns = createColumnsNode(VARIANTS[0], true);
209
+ const ThreeColumns = createColumnsNode(VARIANTS[1], false);
210
+ const FourColumns = createColumnsNode(VARIANTS[2], false);
211
+ const ColumnsColumn = EmailNode.create({
212
+ name: "columnsColumn",
213
+ group: "columnsColumn",
214
+ content: "block+",
215
+ isolating: true,
216
+ addAttributes() {
217
+ return { ...createStandardAttributes([...LAYOUT_ATTRIBUTES, ...COMMON_HTML_ATTRIBUTES]) };
218
+ },
219
+ parseHTML() {
220
+ return [{ tag: "div[data-type=\"column\"]" }];
221
+ },
222
+ renderHTML({ HTMLAttributes }) {
223
+ return [
224
+ "div",
225
+ mergeAttributes({
226
+ "data-type": "column",
227
+ class: "node-column"
228
+ }, HTMLAttributes),
229
+ 0
230
+ ];
231
+ },
232
+ addKeyboardShortcuts() {
233
+ return {
234
+ Backspace: ({ editor }) => {
235
+ const { state } = editor;
236
+ const { selection } = state;
237
+ const { empty, $from } = selection;
238
+ if (!empty) return false;
239
+ for (let depth = $from.depth; depth >= 1; depth--) {
240
+ if ($from.pos !== $from.start(depth)) break;
241
+ const indexInParent = $from.index(depth - 1);
242
+ if (indexInParent === 0) continue;
243
+ const prevNode = $from.node(depth - 1).child(indexInParent - 1);
244
+ if (COLUMN_PARENT_SET.has(prevNode.type.name)) {
245
+ const deleteFrom = $from.before(depth) - prevNode.nodeSize;
246
+ const deleteTo = $from.before(depth);
247
+ editor.view.dispatch(state.tr.delete(deleteFrom, deleteTo));
248
+ return true;
249
+ }
250
+ break;
251
+ }
252
+ return false;
253
+ },
254
+ "Mod-a": ({ editor }) => {
255
+ const { state } = editor;
256
+ const { $from } = state.selection;
257
+ for (let d = $from.depth; d > 0; d--) {
258
+ if ($from.node(d).type.name !== "columnsColumn") continue;
259
+ const columnStart = $from.start(d);
260
+ const columnEnd = $from.end(d);
261
+ const { from, to } = state.selection;
262
+ if (from === columnStart && to === columnEnd) return false;
263
+ editor.view.dispatch(state.tr.setSelection(TextSelection.create(state.doc, columnStart, columnEnd)));
264
+ return true;
265
+ }
266
+ return false;
267
+ }
268
+ };
269
+ },
270
+ renderToReactEmail({ children, node, style }) {
271
+ const inlineStyles = inlineCssToJs(node.attrs?.style);
272
+ const width = node.attrs?.width;
273
+ return /* @__PURE__ */ jsx(Column, {
274
+ className: node.attrs?.class || void 0,
275
+ style: {
276
+ ...style,
277
+ ...inlineStyles,
278
+ ...width ? { width } : {}
279
+ },
280
+ children
281
+ });
282
+ }
283
+ });
284
+
285
+ //#endregion
286
+ //#region src/core/event-bus.ts
287
+ const EVENT_PREFIX = "@react-email/editor:";
288
+ var EditorEventBus = class {
289
+ prefixEventName(eventName) {
290
+ return `${EVENT_PREFIX}${String(eventName)}`;
291
+ }
292
+ dispatch(eventName, payload, options) {
293
+ const target = options?.target ?? window;
294
+ const prefixedEventName = this.prefixEventName(eventName);
295
+ const event = new CustomEvent(prefixedEventName, {
296
+ detail: payload,
297
+ bubbles: false,
298
+ cancelable: false
299
+ });
300
+ target.dispatchEvent(event);
301
+ }
302
+ on(eventName, handler, options) {
303
+ const target = options?.target ?? window;
304
+ const prefixedEventName = this.prefixEventName(eventName);
305
+ const abortController = new AbortController();
306
+ const wrappedHandler = (event) => {
307
+ const customEvent = event;
308
+ const result = handler(customEvent.detail);
309
+ if (result instanceof Promise) result.catch((error) => {
310
+ console.error(`Error in async event handler for ${prefixedEventName}:`, {
311
+ event: customEvent.detail,
312
+ error
313
+ });
314
+ });
315
+ };
316
+ target.addEventListener(prefixedEventName, wrappedHandler, {
317
+ ...options,
318
+ signal: abortController.signal
319
+ });
320
+ return { unsubscribe: () => {
321
+ abortController.abort();
322
+ } };
323
+ }
324
+ };
325
+ const editorEventBus = new EditorEventBus();
326
+
327
+ //#endregion
328
+ export { MAX_COLUMNS_DEPTH as a, getColumnsDepth as c, TABLE_ATTRIBUTES as d, TABLE_CELL_ATTRIBUTES as f, EmailNode as h, FourColumns as i, COMMON_HTML_ATTRIBUTES as l, createStandardAttributes as m, COLUMN_PARENT_TYPES as n, ThreeColumns as o, TABLE_HEADER_ATTRIBUTES as p, ColumnsColumn as r, TwoColumns as s, editorEventBus as t, LAYOUT_ATTRIBUTES as u };
329
+ //# sourceMappingURL=event-bus-CHEzOS_O.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus-CHEzOS_O.mjs","names":[],"sources":["../src/core/serializer/email-node.ts","../src/utils/attribute-helpers.ts","../src/extensions/columns.tsx","../src/core/event-bus.ts"],"sourcesContent":["import {\n type Editor,\n type JSONContent,\n Node,\n type NodeConfig,\n type NodeType,\n} from '@tiptap/core';\n\nexport type NodeRendererComponent = (props: {\n node: JSONContent;\n style: React.CSSProperties;\n children?: React.ReactNode;\n\n extension: EmailNode<any, any>;\n}) => React.ReactNode;\n\nexport interface EmailNodeConfig<Options, Storage>\n extends NodeConfig<Options, Storage> {\n renderToReactEmail: NodeRendererComponent;\n}\n\ntype ConfigParameter<Options, Storage> = Partial<\n Omit<EmailNodeConfig<Options, Storage>, 'renderToReactEmail'>\n> &\n Pick<EmailNodeConfig<Options, Storage>, 'renderToReactEmail'> &\n ThisType<{\n name: string;\n options: Options;\n storage: Storage;\n editor: Editor;\n type: NodeType;\n parent: (...args: any[]) => any;\n }>;\n\nexport class EmailNode<\n Options = Record<string, never>,\n Storage = Record<string, never>,\n> extends Node<Options, Storage> {\n declare config: EmailNodeConfig<Options, Storage>;\n\n // biome-ignore lint/complexity/noUselessConstructor: This is only meant to change the types for config, hence why we keep it\n constructor(config: ConfigParameter<Options, Storage>) {\n super(config);\n }\n\n /**\n * Create a new Node instance\n * @param config - Node configuration object or a function that returns a configuration object\n */\n static create<O = Record<string, never>, S = Record<string, never>>(\n config: ConfigParameter<O, S> | (() => ConfigParameter<O, S>),\n ) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config;\n return new EmailNode<O, S>(resolvedConfig);\n }\n\n static from<O, S>(\n node: Node<O, S>,\n renderToReactEmail: NodeRendererComponent,\n ): EmailNode<O, S> {\n const customNode = EmailNode.create({} as ConfigParameter<O, S>);\n // This only makes a shallow copy, so if there's nested objects here mutating things will be dangerous\n Object.assign(customNode, { ...node });\n customNode.config = { ...node.config, renderToReactEmail };\n return customNode;\n }\n\n // Subclass return types for configure/extend; safe at runtime. TipTap's Node typings cause TS2416 when returning EmailNode.\n // @ts-expect-error - EmailNode is a valid Node subclass; base typings don't support subclass return types\n configure(options?: Partial<Options>) {\n return super.configure(options) as EmailNode<Options, Storage>;\n }\n\n // @ts-expect-error - same as configure: extend returns EmailNode for chaining; base typings are incompatible\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig extends NodeConfig<\n ExtendedOptions,\n ExtendedStorage\n > = EmailNodeConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string;\n options: ExtendedOptions;\n storage: ExtendedStorage;\n editor: Editor;\n type: NodeType;\n }>),\n ): EmailNode<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig =\n typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig;\n return super.extend(resolvedConfig) as EmailNode<\n ExtendedOptions,\n ExtendedStorage\n >;\n }\n}\n","/**\n * Creates TipTap attribute definitions for a list of HTML attributes.\n * Each attribute will have the same pattern:\n * - default: null\n * - parseHTML: extracts the attribute from the element\n * - renderHTML: conditionally renders the attribute if it has a value\n *\n * @param attributeNames - Array of HTML attribute names to create definitions for\n * @returns Object with TipTap attribute definitions\n *\n * @example\n * const attrs = createStandardAttributes(['class', 'id', 'title']);\n * // Returns:\n * // {\n * // class: {\n * // default: null,\n * // parseHTML: (element) => element.getAttribute('class'),\n * // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}\n * // },\n * // ...\n * // }\n */\nexport function createStandardAttributes(attributeNames: readonly string[]) {\n return Object.fromEntries(\n attributeNames.map((attr) => [\n attr,\n {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute(attr),\n renderHTML: (attributes: Record<string, unknown>) => {\n if (!attributes[attr]) {\n return {};\n }\n\n return {\n [attr]: attributes[attr],\n };\n },\n },\n ]),\n );\n}\n\n/**\n * Common HTML attributes used across multiple extensions.\n * These preserve attributes during HTML import and editing for better\n * fidelity when importing existing email templates.\n */\nexport const COMMON_HTML_ATTRIBUTES = [\n 'id',\n 'class',\n 'title',\n 'lang',\n 'dir',\n 'data-id',\n] as const;\n\n/**\n * Layout-specific HTML attributes used for positioning and sizing.\n */\nexport const LAYOUT_ATTRIBUTES = ['align', 'width', 'height'] as const;\n\n/**\n * Table-specific HTML attributes used for table layout and styling.\n */\nexport const TABLE_ATTRIBUTES = [\n 'border',\n 'cellpadding',\n 'cellspacing',\n] as const;\n\n/**\n * Table cell-specific HTML attributes.\n */\nexport const TABLE_CELL_ATTRIBUTES = [\n 'valign',\n 'bgcolor',\n 'colspan',\n 'rowspan',\n] as const;\n\n/**\n * Table header cell-specific HTML attributes.\n * These are additional attributes that only apply to <th> elements.\n */\nexport const TABLE_HEADER_ATTRIBUTES = [\n ...TABLE_CELL_ATTRIBUTES,\n 'scope',\n] as const;\n","import { Column, Row } from '@react-email/components';\nimport { type CommandProps, mergeAttributes } from '@tiptap/core';\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model';\nimport { TextSelection } from '@tiptap/pm/state';\nimport { EmailNode } from '../core/serializer/email-node';\nimport {\n COMMON_HTML_ATTRIBUTES,\n createStandardAttributes,\n LAYOUT_ATTRIBUTES,\n} from '../utils/attribute-helpers';\nimport { inlineCssToJs } from '../utils/styles';\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n columns: {\n insertColumns: (count: 2 | 3 | 4) => ReturnType;\n };\n }\n}\n\nexport const COLUMN_PARENT_TYPES = [\n 'twoColumns',\n 'threeColumns',\n 'fourColumns',\n] as const;\n\nconst COLUMN_PARENT_SET = new Set<string>(COLUMN_PARENT_TYPES);\n\nexport const MAX_COLUMNS_DEPTH = 3;\n\nexport function getColumnsDepth(doc: ProseMirrorNode, from: number): number {\n const $from = doc.resolve(from);\n let depth = 0;\n for (let d = $from.depth; d > 0; d--) {\n if (COLUMN_PARENT_SET.has($from.node(d).type.name)) {\n depth++;\n }\n }\n return depth;\n}\n\ninterface ColumnsVariantConfig {\n name: (typeof COLUMN_PARENT_TYPES)[number];\n columnCount: number;\n content: string;\n dataType: string;\n}\n\nconst VARIANTS: ColumnsVariantConfig[] = [\n {\n name: 'twoColumns',\n columnCount: 2,\n content: 'columnsColumn columnsColumn',\n dataType: 'two-columns',\n },\n {\n name: 'threeColumns',\n columnCount: 3,\n content: 'columnsColumn columnsColumn columnsColumn',\n dataType: 'three-columns',\n },\n {\n name: 'fourColumns',\n columnCount: 4,\n content: 'columnsColumn{4}',\n dataType: 'four-columns',\n },\n];\n\nconst NODE_TYPE_MAP: Record<number, (typeof COLUMN_PARENT_TYPES)[number]> = {\n 2: 'twoColumns',\n 3: 'threeColumns',\n 4: 'fourColumns',\n};\n\nfunction createColumnsNode(\n config: ColumnsVariantConfig,\n includeCommands: boolean,\n) {\n return EmailNode.create({\n name: config.name,\n group: 'block',\n content: config.content,\n isolating: true,\n defining: true,\n\n addAttributes() {\n return createStandardAttributes([\n ...LAYOUT_ATTRIBUTES,\n ...COMMON_HTML_ATTRIBUTES,\n ]);\n },\n\n parseHTML() {\n return [{ tag: `div[data-type=\"${config.dataType}\"]` }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'div',\n mergeAttributes(\n { 'data-type': config.dataType, class: 'node-columns' },\n HTMLAttributes,\n ),\n 0,\n ];\n },\n\n ...(includeCommands && {\n addCommands() {\n return {\n insertColumns:\n (count: 2 | 3 | 4) =>\n ({\n commands,\n state,\n }: CommandProps & {\n state: { doc: ProseMirrorNode; selection: { from: number } };\n }) => {\n if (\n getColumnsDepth(state.doc, state.selection.from) >=\n MAX_COLUMNS_DEPTH\n ) {\n return false;\n }\n const nodeType = NODE_TYPE_MAP[count];\n const children = Array.from({ length: count }, () => ({\n type: 'columnsColumn',\n content: [{ type: 'paragraph', content: [] }],\n }));\n return commands.insertContent({\n type: nodeType,\n content: children,\n });\n },\n };\n },\n }),\n\n renderToReactEmail({ children, node, style }) {\n const inlineStyles = inlineCssToJs(node.attrs?.style);\n return (\n <Row\n className={node.attrs?.class || undefined}\n style={{ ...style, ...inlineStyles }}\n >\n {children}\n </Row>\n );\n },\n });\n}\n\nexport const TwoColumns = createColumnsNode(VARIANTS[0], true);\nexport const ThreeColumns = createColumnsNode(VARIANTS[1], false);\nexport const FourColumns = createColumnsNode(VARIANTS[2], false);\n\nexport const ColumnsColumn = EmailNode.create({\n name: 'columnsColumn',\n group: 'columnsColumn',\n content: 'block+',\n isolating: true,\n\n addAttributes() {\n return {\n ...createStandardAttributes([\n ...LAYOUT_ATTRIBUTES,\n ...COMMON_HTML_ATTRIBUTES,\n ]),\n };\n },\n\n parseHTML() {\n return [{ tag: 'div[data-type=\"column\"]' }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'div',\n mergeAttributes(\n { 'data-type': 'column', class: 'node-column' },\n HTMLAttributes,\n ),\n 0,\n ];\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: ({ editor }) => {\n const { state } = editor;\n const { selection } = state;\n const { empty, $from } = selection;\n\n if (!empty) return false;\n\n for (let depth = $from.depth; depth >= 1; depth--) {\n if ($from.pos !== $from.start(depth)) break;\n\n const indexInParent = $from.index(depth - 1);\n\n if (indexInParent === 0) continue;\n\n const parent = $from.node(depth - 1);\n const prevNode = parent.child(indexInParent - 1);\n\n if (COLUMN_PARENT_SET.has(prevNode.type.name)) {\n const deleteFrom = $from.before(depth) - prevNode.nodeSize;\n const deleteTo = $from.before(depth);\n editor.view.dispatch(state.tr.delete(deleteFrom, deleteTo));\n return true;\n }\n\n break;\n }\n\n return false;\n },\n 'Mod-a': ({ editor }) => {\n const { state } = editor;\n const { $from } = state.selection;\n\n for (let d = $from.depth; d > 0; d--) {\n if ($from.node(d).type.name !== 'columnsColumn') {\n continue;\n }\n\n const columnStart = $from.start(d);\n const columnEnd = $from.end(d);\n const { from, to } = state.selection;\n\n if (from === columnStart && to === columnEnd) {\n return false;\n }\n\n editor.view.dispatch(\n state.tr.setSelection(\n TextSelection.create(state.doc, columnStart, columnEnd),\n ),\n );\n return true;\n }\n\n return false;\n },\n };\n },\n\n renderToReactEmail({ children, node, style }) {\n const inlineStyles = inlineCssToJs(node.attrs?.style);\n const width = node.attrs?.width;\n return (\n <Column\n className={node.attrs?.class || undefined}\n style={{\n ...style,\n ...inlineStyles,\n ...(width ? { width } : {}),\n }}\n >\n {children}\n </Column>\n );\n },\n});\n","const EVENT_PREFIX = '@react-email/editor:';\n\n/**\n * Base event map interface for the editor event bus.\n *\n * Components extend this via TypeScript module augmentation:\n * ```ts\n * declare module '@react-email/editor' {\n * interface EditorEventMap {\n * 'my-component:custom-event': { data: string };\n * }\n * }\n * ```\n */\nexport interface EditorEventMap {\n 'bubble-menu:add-link': undefined;\n}\n\nexport type EditorEventName = keyof EditorEventMap;\n\nexport type EditorEventHandler<T extends EditorEventName> = (\n payload: EditorEventMap[T],\n) => void | Promise<void>;\n\nexport interface EditorEventSubscription {\n unsubscribe: () => void;\n}\n\nclass EditorEventBus {\n private prefixEventName(eventName: EditorEventName): string {\n return `${EVENT_PREFIX}${String(eventName)}`;\n }\n\n dispatch<T extends EditorEventName>(\n eventName: T,\n payload: EditorEventMap[T],\n options?: { target?: EventTarget },\n ): void {\n const target = options?.target ?? window;\n const prefixedEventName = this.prefixEventName(eventName);\n const event = new CustomEvent(prefixedEventName, {\n detail: payload,\n bubbles: false,\n cancelable: false,\n });\n target.dispatchEvent(event);\n }\n\n on<T extends EditorEventName>(\n eventName: T,\n handler: EditorEventHandler<T>,\n options?: AddEventListenerOptions & { target?: EventTarget },\n ): EditorEventSubscription {\n const target = options?.target ?? window;\n const prefixedEventName = this.prefixEventName(eventName);\n const abortController = new AbortController();\n\n const wrappedHandler = (event: Event) => {\n const customEvent = event as CustomEvent<EditorEventMap[T]>;\n const result = handler(customEvent.detail);\n\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(\n `Error in async event handler for ${prefixedEventName}:`,\n { event: customEvent.detail, error },\n );\n });\n }\n };\n\n target.addEventListener(prefixedEventName, wrappedHandler, {\n ...options,\n signal: abortController.signal,\n });\n\n return {\n unsubscribe: () => {\n abortController.abort();\n },\n };\n }\n}\n\nexport const editorEventBus = new EditorEventBus();\n"],"mappings":"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"}