@react-email/editor 0.0.0-experimental.21 → 0.0.0-experimental.24

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 (45) hide show
  1. package/dist/columns-CUxUEHje.mjs +497 -0
  2. package/dist/columns-CUxUEHje.mjs.map +1 -0
  3. package/dist/columns-ZSaLdkg9.cjs +630 -0
  4. package/dist/core/index.cjs +8 -0
  5. package/dist/core/index.d.cts +2 -0
  6. package/dist/core/index.d.mts +2 -0
  7. package/dist/core/index.mjs +4 -0
  8. package/dist/core-BjmRceVw.mjs +1999 -0
  9. package/dist/core-BjmRceVw.mjs.map +1 -0
  10. package/dist/core-iuG1UrYN.cjs +2250 -0
  11. package/dist/extensions/index.cjs +46 -0
  12. package/dist/extensions/index.d.cts +389 -0
  13. package/dist/extensions/index.d.cts.map +1 -0
  14. package/dist/extensions/index.d.mts +389 -0
  15. package/dist/extensions/index.d.mts.map +1 -0
  16. package/dist/extensions/index.mjs +4 -0
  17. package/dist/index-CfslA7KT.d.cts +130 -0
  18. package/dist/index-CfslA7KT.d.cts.map +1 -0
  19. package/dist/index-hbHRR7oB.d.mts +130 -0
  20. package/dist/index-hbHRR7oB.d.mts.map +1 -0
  21. package/dist/set-text-alignment-Bx3bPteH.cjs +24 -0
  22. package/dist/set-text-alignment-DZvgnbvz.mjs +19 -0
  23. package/dist/set-text-alignment-DZvgnbvz.mjs.map +1 -0
  24. package/dist/ui/index.cjs +1646 -0
  25. package/dist/ui/index.d.cts +668 -0
  26. package/dist/ui/index.d.cts.map +1 -0
  27. package/dist/ui/index.d.mts +668 -0
  28. package/dist/ui/index.d.mts.map +1 -0
  29. package/dist/ui/index.mjs +1584 -0
  30. package/dist/ui/index.mjs.map +1 -0
  31. package/dist/ui/themes/default.css +20 -1
  32. package/dist/utils/index.cjs +3 -0
  33. package/dist/utils/index.d.cts +7 -0
  34. package/dist/utils/index.d.cts.map +1 -0
  35. package/dist/utils/index.d.mts +7 -0
  36. package/dist/utils/index.d.mts.map +1 -0
  37. package/dist/utils/index.mjs +3 -0
  38. package/package.json +41 -13
  39. package/dist/index.cjs +0 -4205
  40. package/dist/index.d.cts +0 -1164
  41. package/dist/index.d.cts.map +0 -1
  42. package/dist/index.d.mts +0 -1164
  43. package/dist/index.d.mts.map +0 -1
  44. package/dist/index.mjs +0 -4047
  45. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,2250 @@
1
+ const require_columns = require('./columns-ZSaLdkg9.cjs');
2
+ let _react_email_components = require("@react-email/components");
3
+ _react_email_components = require_columns.__toESM(_react_email_components);
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ let _tiptap_core = require("@tiptap/core");
6
+ let _tiptap_extensions = require("@tiptap/extensions");
7
+ let _tiptap_react = require("@tiptap/react");
8
+ let react = require("react");
9
+ react = require_columns.__toESM(react);
10
+ let _tiptap_starter_kit = require("@tiptap/starter-kit");
11
+ _tiptap_starter_kit = require_columns.__toESM(_tiptap_starter_kit);
12
+ let _tiptap_extension_blockquote = require("@tiptap/extension-blockquote");
13
+ _tiptap_extension_blockquote = require_columns.__toESM(_tiptap_extension_blockquote);
14
+ let _tiptap_extension_bold = require("@tiptap/extension-bold");
15
+ _tiptap_extension_bold = require_columns.__toESM(_tiptap_extension_bold);
16
+ let _tiptap_extension_bullet_list = require("@tiptap/extension-bullet-list");
17
+ _tiptap_extension_bullet_list = require_columns.__toESM(_tiptap_extension_bullet_list);
18
+ let _tiptap_extension_code = require("@tiptap/extension-code");
19
+ _tiptap_extension_code = require_columns.__toESM(_tiptap_extension_code);
20
+ let _tiptap_extension_code_block = require("@tiptap/extension-code-block");
21
+ _tiptap_extension_code_block = require_columns.__toESM(_tiptap_extension_code_block);
22
+ let _tiptap_pm_state = require("@tiptap/pm/state");
23
+ let _tiptap_pm_view = require("@tiptap/pm/view");
24
+ let hast_util_from_html = require("hast-util-from-html");
25
+ let prismjs = require("prismjs");
26
+ prismjs = require_columns.__toESM(prismjs);
27
+ let _tiptap_extension_horizontal_rule = require("@tiptap/extension-horizontal-rule");
28
+ _tiptap_extension_horizontal_rule = require_columns.__toESM(_tiptap_extension_horizontal_rule);
29
+ let _tiptap_extension_hard_break = require("@tiptap/extension-hard-break");
30
+ _tiptap_extension_hard_break = require_columns.__toESM(_tiptap_extension_hard_break);
31
+ let _tiptap_extension_heading = require("@tiptap/extension-heading");
32
+ let _tiptap_extension_italic = require("@tiptap/extension-italic");
33
+ _tiptap_extension_italic = require_columns.__toESM(_tiptap_extension_italic);
34
+ let _tiptap_extension_link = require("@tiptap/extension-link");
35
+ _tiptap_extension_link = require_columns.__toESM(_tiptap_extension_link);
36
+ let _tiptap_extension_list_item = require("@tiptap/extension-list-item");
37
+ _tiptap_extension_list_item = require_columns.__toESM(_tiptap_extension_list_item);
38
+ let _tiptap_extension_ordered_list = require("@tiptap/extension-ordered-list");
39
+ _tiptap_extension_ordered_list = require_columns.__toESM(_tiptap_extension_ordered_list);
40
+ let _tiptap_extension_paragraph = require("@tiptap/extension-paragraph");
41
+ _tiptap_extension_paragraph = require_columns.__toESM(_tiptap_extension_paragraph);
42
+ let _tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
43
+ _tiptap_extension_placeholder = require_columns.__toESM(_tiptap_extension_placeholder);
44
+ let _tiptap_extension_strike = require("@tiptap/extension-strike");
45
+ _tiptap_extension_strike = require_columns.__toESM(_tiptap_extension_strike);
46
+ let _tiptap_extension_superscript = require("@tiptap/extension-superscript");
47
+ _tiptap_extension_superscript = require_columns.__toESM(_tiptap_extension_superscript);
48
+ let _tiptap_extension_underline = require("@tiptap/extension-underline");
49
+ _tiptap_extension_underline = require_columns.__toESM(_tiptap_extension_underline);
50
+ let _tiptap_html = require("@tiptap/html");
51
+
52
+ //#region src/core/is-document-visually-empty.ts
53
+ function isDocumentVisuallyEmpty(doc) {
54
+ let nonGlobalNodeCount = 0;
55
+ let firstNonGlobalNode = null;
56
+ for (let index = 0; index < doc.childCount; index += 1) {
57
+ const node = doc.child(index);
58
+ if (node.type.name === "globalContent") continue;
59
+ nonGlobalNodeCount += 1;
60
+ if (firstNonGlobalNode === null) firstNonGlobalNode = {
61
+ type: node.type,
62
+ textContent: node.textContent,
63
+ childCount: node.content.childCount
64
+ };
65
+ }
66
+ if (nonGlobalNodeCount === 0) return true;
67
+ if (nonGlobalNodeCount !== 1) return false;
68
+ return firstNonGlobalNode?.type.name === "paragraph" && firstNonGlobalNode.textContent.trim().length === 0 && firstNonGlobalNode.childCount === 0;
69
+ }
70
+
71
+ //#endregion
72
+ //#region src/core/serializer/default-base-template.tsx
73
+ function DefaultBaseTemplate({ children, previewText }) {
74
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_email_components.Html, { children: [
75
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_email_components.Head, { children: [
76
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
77
+ content: "width=device-width",
78
+ name: "viewport"
79
+ }),
80
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
81
+ content: "IE=edge",
82
+ httpEquiv: "X-UA-Compatible"
83
+ }),
84
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", { name: "x-apple-disable-message-reformatting" }),
85
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
86
+ content: "telephone=no,address=no,email=no,date=no,url=no",
87
+ name: "format-detection"
88
+ })
89
+ ] }),
90
+ previewText && previewText !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Preview, { children: previewText }),
91
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Body, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
92
+ width: "100%",
93
+ align: "center",
94
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
95
+ style: { width: "100%" },
96
+ children
97
+ })
98
+ }) })
99
+ ] });
100
+ }
101
+
102
+ //#endregion
103
+ //#region src/core/serializer/email-mark.ts
104
+ var EmailMark = class EmailMark extends _tiptap_core.Mark {
105
+ constructor(config) {
106
+ super(config);
107
+ }
108
+ /**
109
+ * Create a new Mark instance
110
+ * @param config - Mark configuration object or a function that returns a configuration object
111
+ */
112
+ static create(config) {
113
+ return new EmailMark(typeof config === "function" ? config() : config);
114
+ }
115
+ static from(mark, renderToReactEmail) {
116
+ const customMark = EmailMark.create({});
117
+ Object.assign(customMark, { ...mark });
118
+ customMark.config = {
119
+ ...mark.config,
120
+ renderToReactEmail
121
+ };
122
+ return customMark;
123
+ }
124
+ configure(options) {
125
+ return super.configure(options);
126
+ }
127
+ extend(extendedConfig) {
128
+ const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
129
+ return super.extend(resolvedConfig);
130
+ }
131
+ };
132
+
133
+ //#endregion
134
+ //#region src/core/serializer/compose-react-email.tsx
135
+ const MARK_ORDER = {
136
+ preservedStyle: 0,
137
+ italic: 1,
138
+ strike: 2,
139
+ underline: 3,
140
+ link: 4,
141
+ bold: 5,
142
+ code: 6
143
+ };
144
+ const NODES_WITH_INCREMENTED_CHILD_DEPTH = new Set(["bulletList", "orderedList"]);
145
+ function getOrderedMarks(marks) {
146
+ if (!marks) return [];
147
+ return [...marks].sort((a, b) => (MARK_ORDER[a.type] ?? Number.MAX_SAFE_INTEGER) - (MARK_ORDER[b.type] ?? Number.MAX_SAFE_INTEGER));
148
+ }
149
+ const composeReactEmail = async ({ editor, preview }) => {
150
+ const data = editor.getJSON();
151
+ const extensions = editor.extensionManager.extensions;
152
+ const serializerPlugin = extensions.map((ext) => ext.options?.serializerPlugin).filter((p) => Boolean(p)).at(-1);
153
+ const emailNodeComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof require_columns.EmailNode).map((extension) => [extension.name, extension.config.renderToReactEmail]));
154
+ const emailMarkComponentRegistry = Object.fromEntries(extensions.filter((ext) => ext instanceof EmailMark).map((extension) => [extension.name, extension.config.renderToReactEmail]));
155
+ function renderMark(mark, node, children, depth) {
156
+ const markStyle = serializerPlugin?.getNodeStyles({
157
+ type: mark.type,
158
+ attrs: mark.attrs ?? {}
159
+ }, depth, editor) ?? {};
160
+ const markRenderer = emailMarkComponentRegistry[mark.type];
161
+ if (markRenderer) return markRenderer({
162
+ mark,
163
+ node,
164
+ style: markStyle,
165
+ children
166
+ });
167
+ return children;
168
+ }
169
+ function parseContent(content, depth = 0) {
170
+ if (!content) return;
171
+ return content.map((node, index) => {
172
+ const style = serializerPlugin?.getNodeStyles(node, depth, editor) ?? {};
173
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
174
+ if (node.type && emailNodeComponentRegistry[node.type]) {
175
+ const Component = emailNodeComponentRegistry[node.type];
176
+ const childDepth = NODES_WITH_INCREMENTED_CHILD_DEPTH.has(node.type) ? depth + 1 : depth;
177
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
178
+ node: node.type === "table" && inlineStyles.width && !node.attrs?.width ? {
179
+ ...node,
180
+ attrs: {
181
+ ...node.attrs,
182
+ width: inlineStyles.width
183
+ }
184
+ } : node,
185
+ style,
186
+ children: parseContent(node.content, childDepth)
187
+ }, index);
188
+ }
189
+ switch (node.type) {
190
+ case "text": {
191
+ let wrappedText = node.text;
192
+ getOrderedMarks(node.marks).forEach((mark) => {
193
+ wrappedText = renderMark(mark, node, wrappedText, depth);
194
+ });
195
+ const textAttributes = node.marks?.find((mark) => mark.type === "textStyle")?.attrs;
196
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
197
+ style: {
198
+ ...textAttributes,
199
+ ...style
200
+ },
201
+ children: wrappedText
202
+ }, index);
203
+ }
204
+ default: return null;
205
+ }
206
+ });
207
+ }
208
+ const unformattedHtml = await (0, _react_email_components.render)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(serializerPlugin?.BaseTemplate ?? DefaultBaseTemplate, {
209
+ previewText: preview,
210
+ editor,
211
+ children: parseContent(data.content)
212
+ }));
213
+ const [prettyHtml, text] = await Promise.all([(0, _react_email_components.pretty)(unformattedHtml), (0, _react_email_components.toPlainText)(unformattedHtml)]);
214
+ return {
215
+ html: prettyHtml,
216
+ text
217
+ };
218
+ };
219
+
220
+ //#endregion
221
+ //#region src/extensions/alignment-attribute.tsx
222
+ const AlignmentAttribute = _tiptap_core.Extension.create({
223
+ name: "alignmentAttribute",
224
+ addOptions() {
225
+ return {
226
+ types: [],
227
+ alignments: [
228
+ "left",
229
+ "center",
230
+ "right",
231
+ "justify"
232
+ ]
233
+ };
234
+ },
235
+ addGlobalAttributes() {
236
+ return [{
237
+ types: this.options.types,
238
+ attributes: { alignment: {
239
+ parseHTML: (element) => {
240
+ const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
241
+ if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
242
+ return null;
243
+ },
244
+ renderHTML: (attributes) => {
245
+ if (attributes.alignment === "left") return {};
246
+ return { alignment: attributes.alignment };
247
+ }
248
+ } }
249
+ }];
250
+ },
251
+ addCommands() {
252
+ return { setAlignment: (alignment) => ({ commands }) => {
253
+ if (!this.options.alignments.includes(alignment)) return false;
254
+ return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
255
+ } };
256
+ },
257
+ addKeyboardShortcuts() {
258
+ return {
259
+ Enter: () => {
260
+ const { from } = this.editor.state.selection;
261
+ const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
262
+ if (currentAlignment) requestAnimationFrame(() => {
263
+ this.editor.commands.setAlignment(currentAlignment);
264
+ });
265
+ return false;
266
+ },
267
+ "Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
268
+ "Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
269
+ "Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
270
+ "Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
271
+ };
272
+ }
273
+ });
274
+
275
+ //#endregion
276
+ //#region src/utils/get-text-alignment.ts
277
+ function getTextAlignment(alignment) {
278
+ switch (alignment) {
279
+ case "left": return { textAlign: "left" };
280
+ case "center": return { textAlign: "center" };
281
+ case "right": return { textAlign: "right" };
282
+ default: return {};
283
+ }
284
+ }
285
+
286
+ //#endregion
287
+ //#region src/extensions/blockquote.tsx
288
+ const Blockquote = require_columns.EmailNode.from(_tiptap_extension_blockquote.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("blockquote", {
289
+ className: node.attrs?.class || void 0,
290
+ style: {
291
+ ...style,
292
+ ...require_columns.inlineCssToJs(node.attrs?.style),
293
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
294
+ },
295
+ children
296
+ }));
297
+
298
+ //#endregion
299
+ //#region src/extensions/body.tsx
300
+ const Body = require_columns.EmailNode.create({
301
+ name: "body",
302
+ group: "block",
303
+ content: "block+",
304
+ defining: true,
305
+ isolating: true,
306
+ addAttributes() {
307
+ return { ...require_columns.createStandardAttributes([...require_columns.COMMON_HTML_ATTRIBUTES, ...require_columns.LAYOUT_ATTRIBUTES]) };
308
+ },
309
+ parseHTML() {
310
+ return [{
311
+ tag: "body",
312
+ getAttrs: (node) => {
313
+ if (typeof node === "string") return false;
314
+ const element = node;
315
+ const attrs = {};
316
+ Array.from(element.attributes).forEach((attr) => {
317
+ attrs[attr.name] = attr.value;
318
+ });
319
+ return attrs;
320
+ }
321
+ }];
322
+ },
323
+ renderHTML({ HTMLAttributes }) {
324
+ return [
325
+ "div",
326
+ (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
327
+ 0
328
+ ];
329
+ },
330
+ renderToReactEmail({ children, node, style }) {
331
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
332
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
333
+ className: node.attrs?.class || void 0,
334
+ style: {
335
+ ...style,
336
+ ...inlineStyles
337
+ },
338
+ children
339
+ });
340
+ }
341
+ });
342
+
343
+ //#endregion
344
+ //#region src/extensions/bold.tsx
345
+ const BoldWithoutFontWeightInference = _tiptap_extension_bold.default.extend({ parseHTML() {
346
+ return [
347
+ { tag: "strong" },
348
+ {
349
+ tag: "b",
350
+ getAttrs: (node) => node.style.fontWeight !== "normal" && null
351
+ },
352
+ {
353
+ style: "font-weight=400",
354
+ clearMark: (mark) => mark.type.name === this.name
355
+ }
356
+ ];
357
+ } });
358
+ const Bold = EmailMark.from(BoldWithoutFontWeightInference, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
359
+ style,
360
+ children
361
+ }));
362
+
363
+ //#endregion
364
+ //#region src/extensions/bullet-list.tsx
365
+ const BulletList = require_columns.EmailNode.from(_tiptap_extension_bullet_list.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
366
+ className: node.attrs?.class || void 0,
367
+ style: {
368
+ ...style,
369
+ ...require_columns.inlineCssToJs(node.attrs?.style)
370
+ },
371
+ children
372
+ }));
373
+
374
+ //#endregion
375
+ //#region src/extensions/button.tsx
376
+ const Button = require_columns.EmailNode.create({
377
+ name: "button",
378
+ group: "block",
379
+ content: "inline*",
380
+ defining: true,
381
+ draggable: true,
382
+ marks: "bold",
383
+ addAttributes() {
384
+ return {
385
+ class: { default: "button" },
386
+ href: { default: "#" },
387
+ alignment: { default: "left" }
388
+ };
389
+ },
390
+ parseHTML() {
391
+ return [{
392
+ tag: "a[data-id=\"react-email-button\"]",
393
+ getAttrs: (node) => {
394
+ if (typeof node === "string") return false;
395
+ const element = node;
396
+ const attrs = {};
397
+ Array.from(element.attributes).forEach((attr) => {
398
+ attrs[attr.name] = attr.value;
399
+ });
400
+ return attrs;
401
+ }
402
+ }];
403
+ },
404
+ renderHTML({ HTMLAttributes }) {
405
+ return [
406
+ "div",
407
+ (0, _tiptap_core.mergeAttributes)({ class: `align-${HTMLAttributes?.alignment}` }),
408
+ [
409
+ "a",
410
+ (0, _tiptap_core.mergeAttributes)({
411
+ class: `node-button ${HTMLAttributes?.class}`,
412
+ style: HTMLAttributes?.style,
413
+ "data-id": "react-email-button",
414
+ "data-href": HTMLAttributes?.href
415
+ }),
416
+ 0
417
+ ]
418
+ ];
419
+ },
420
+ addCommands() {
421
+ return {
422
+ updateButton: (attributes) => ({ commands }) => {
423
+ return commands.updateAttributes("button", attributes);
424
+ },
425
+ setButton: () => ({ commands }) => {
426
+ return commands.insertContent({
427
+ type: "button",
428
+ content: [{
429
+ type: "text",
430
+ text: "Button"
431
+ }]
432
+ });
433
+ }
434
+ };
435
+ },
436
+ renderToReactEmail({ children, node, style }) {
437
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
438
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Row, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Column, {
439
+ align: node.attrs?.align || node.attrs?.alignment,
440
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Button, {
441
+ className: node.attrs?.class || void 0,
442
+ href: node.attrs?.href,
443
+ style: {
444
+ ...style,
445
+ ...inlineStyles
446
+ },
447
+ children
448
+ })
449
+ }) });
450
+ }
451
+ });
452
+
453
+ //#endregion
454
+ //#region src/extensions/class-attribute.tsx
455
+ const ClassAttribute = _tiptap_core.Extension.create({
456
+ name: "classAttribute",
457
+ addOptions() {
458
+ return {
459
+ types: [],
460
+ class: []
461
+ };
462
+ },
463
+ addGlobalAttributes() {
464
+ return [{
465
+ types: this.options.types,
466
+ attributes: { class: {
467
+ default: "",
468
+ parseHTML: (element) => element.className || "",
469
+ renderHTML: (attributes) => {
470
+ return attributes.class ? { class: attributes.class } : {};
471
+ }
472
+ } }
473
+ }];
474
+ },
475
+ addCommands() {
476
+ return {
477
+ unsetClass: () => ({ commands }) => {
478
+ return this.options.types.every((type) => commands.resetAttributes(type, "class"));
479
+ },
480
+ setClass: (classList) => ({ commands }) => {
481
+ return this.options.types.every((type) => commands.updateAttributes(type, { class: classList }));
482
+ }
483
+ };
484
+ },
485
+ addKeyboardShortcuts() {
486
+ return { Enter: ({ editor }) => {
487
+ requestAnimationFrame(() => {
488
+ editor.commands.resetAttributes("paragraph", "class");
489
+ });
490
+ return false;
491
+ } };
492
+ }
493
+ });
494
+
495
+ //#endregion
496
+ //#region src/extensions/code.tsx
497
+ const Code = EmailMark.from(_tiptap_extension_code.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
498
+ style: {
499
+ ...style,
500
+ ...require_columns.inlineCssToJs(node.attrs?.style)
501
+ },
502
+ children
503
+ }));
504
+
505
+ //#endregion
506
+ //#region src/utils/prism-utils.ts
507
+ const publicURL = "/styles/prism";
508
+ function loadPrismTheme(theme) {
509
+ const link = document.createElement("link");
510
+ link.rel = "stylesheet";
511
+ link.href = `${publicURL}/prism-${theme}.css`;
512
+ link.setAttribute("data-prism-theme", "");
513
+ document.head.appendChild(link);
514
+ }
515
+ function removePrismTheme() {
516
+ const existingTheme = document.querySelectorAll("link[rel=\"stylesheet\"][data-prism-theme]");
517
+ if (existingTheme.length > 0) existingTheme.forEach((cssLinkTag) => {
518
+ cssLinkTag.remove();
519
+ });
520
+ }
521
+ function hasPrismThemeLoaded(theme) {
522
+ return !!document.querySelector(`link[rel="stylesheet"][data-prism-theme][href="${publicURL}/prism-${theme}.css"]`);
523
+ }
524
+
525
+ //#endregion
526
+ //#region src/extensions/prism-plugin.ts
527
+ const PRISM_LANGUAGE_LOADED_META = "prismLanguageLoaded";
528
+ function parseNodes(nodes, className = []) {
529
+ return nodes.flatMap((node) => {
530
+ const classes = [...className, ...node.properties ? node.properties.className : []];
531
+ if (node.children) return parseNodes(node.children, classes);
532
+ return {
533
+ text: node.value ?? "",
534
+ classes
535
+ };
536
+ });
537
+ }
538
+ function getHighlightNodes(html) {
539
+ return (0, hast_util_from_html.fromHtml)(html, { fragment: true }).children;
540
+ }
541
+ function registeredLang(aliasOrLanguage) {
542
+ const allSupportLang = Object.keys(prismjs.default.languages).filter((id) => typeof prismjs.default.languages[id] === "object");
543
+ return Boolean(allSupportLang.find((x) => x === aliasOrLanguage));
544
+ }
545
+ function getDecorations({ doc, name, defaultLanguage, defaultTheme, loadingLanguages, onLanguageLoaded }) {
546
+ const decorations = [];
547
+ (0, _tiptap_core.findChildren)(doc, (node) => node.type.name === name).forEach((block) => {
548
+ let from = block.pos + 1;
549
+ const language = block.node.attrs.language || defaultLanguage;
550
+ const theme = block.node.attrs.theme || defaultTheme;
551
+ let html = "";
552
+ try {
553
+ if (!registeredLang(language) && !loadingLanguages.has(language)) {
554
+ loadingLanguages.add(language);
555
+ import(`prismjs/components/prism-${language}`).then(() => {
556
+ loadingLanguages.delete(language);
557
+ onLanguageLoaded(language);
558
+ }).catch(() => {
559
+ loadingLanguages.delete(language);
560
+ });
561
+ }
562
+ if (!hasPrismThemeLoaded(theme)) loadPrismTheme(theme);
563
+ html = prismjs.default.highlight(block.node.textContent, prismjs.default.languages[language], language);
564
+ } catch {
565
+ html = prismjs.default.highlight(block.node.textContent, prismjs.default.languages.javascript, "js");
566
+ }
567
+ parseNodes(getHighlightNodes(html)).forEach((node) => {
568
+ const to = from + node.text.length;
569
+ if (node.classes.length) {
570
+ const decoration = _tiptap_pm_view.Decoration.inline(from, to, { class: node.classes.join(" ") });
571
+ decorations.push(decoration);
572
+ }
573
+ from = to;
574
+ });
575
+ });
576
+ return _tiptap_pm_view.DecorationSet.create(doc, decorations);
577
+ }
578
+ function PrismPlugin({ name, defaultLanguage, defaultTheme }) {
579
+ if (!defaultLanguage) throw Error("You must specify the defaultLanguage parameter");
580
+ const loadingLanguages = /* @__PURE__ */ new Set();
581
+ let pluginView = null;
582
+ const onLanguageLoaded = (language) => {
583
+ if (pluginView) pluginView.dispatch(pluginView.state.tr.setMeta(PRISM_LANGUAGE_LOADED_META, language));
584
+ };
585
+ const prismjsPlugin = new _tiptap_pm_state.Plugin({
586
+ key: new _tiptap_pm_state.PluginKey("prism"),
587
+ view(view) {
588
+ pluginView = view;
589
+ return { destroy() {
590
+ pluginView = null;
591
+ } };
592
+ },
593
+ state: {
594
+ init: (_, { doc }) => {
595
+ return getDecorations({
596
+ doc,
597
+ name,
598
+ defaultLanguage,
599
+ defaultTheme,
600
+ loadingLanguages,
601
+ onLanguageLoaded
602
+ });
603
+ },
604
+ apply: (transaction, decorationSet, oldState, newState) => {
605
+ const oldNodeName = oldState.selection.$head.parent.type.name;
606
+ const newNodeName = newState.selection.$head.parent.type.name;
607
+ const oldNodes = (0, _tiptap_core.findChildren)(oldState.doc, (node) => node.type.name === name);
608
+ const newNodes = (0, _tiptap_core.findChildren)(newState.doc, (node) => node.type.name === name);
609
+ if (transaction.getMeta(PRISM_LANGUAGE_LOADED_META) || transaction.docChanged && ([oldNodeName, newNodeName].includes(name) || newNodes.length !== oldNodes.length || transaction.steps.some((step) => {
610
+ const rangeStep = step;
611
+ return rangeStep.from !== void 0 && rangeStep.to !== void 0 && oldNodes.some((node) => {
612
+ return node.pos >= rangeStep.from && node.pos + node.node.nodeSize <= rangeStep.to;
613
+ });
614
+ }))) return getDecorations({
615
+ doc: transaction.doc,
616
+ name,
617
+ defaultLanguage,
618
+ defaultTheme,
619
+ loadingLanguages,
620
+ onLanguageLoaded
621
+ });
622
+ return decorationSet.map(transaction.mapping, transaction.doc);
623
+ }
624
+ },
625
+ props: { decorations(state) {
626
+ return prismjsPlugin.getState(state);
627
+ } },
628
+ destroy() {
629
+ pluginView = null;
630
+ removePrismTheme();
631
+ }
632
+ });
633
+ return prismjsPlugin;
634
+ }
635
+
636
+ //#endregion
637
+ //#region src/extensions/code-block.tsx
638
+ const CodeBlockPrism = require_columns.EmailNode.from(_tiptap_extension_code_block.default.extend({
639
+ addOptions() {
640
+ return {
641
+ languageClassPrefix: "language-",
642
+ exitOnTripleEnter: false,
643
+ exitOnArrowDown: false,
644
+ enableTabIndentation: true,
645
+ tabSize: 2,
646
+ defaultLanguage: "javascript",
647
+ defaultTheme: "default",
648
+ HTMLAttributes: {}
649
+ };
650
+ },
651
+ addAttributes() {
652
+ return {
653
+ ...this.parent?.(),
654
+ language: {
655
+ default: this.options.defaultLanguage,
656
+ parseHTML: (element) => {
657
+ if (!element) return null;
658
+ const { languageClassPrefix } = this.options;
659
+ if (!languageClassPrefix) return null;
660
+ const language = [...element.firstElementChild?.classList || []].filter((className) => className.startsWith(languageClassPrefix || "")).map((className) => className.replace(languageClassPrefix, ""))[0];
661
+ if (!language) return null;
662
+ return language;
663
+ },
664
+ rendered: false
665
+ },
666
+ theme: {
667
+ default: this.options.defaultTheme,
668
+ rendered: false
669
+ }
670
+ };
671
+ },
672
+ renderHTML({ node, HTMLAttributes }) {
673
+ return [
674
+ "pre",
675
+ (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language}` : null }, { "data-theme": node.attrs.theme }),
676
+ [
677
+ "code",
678
+ { class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language} node-codeTag` : "node-codeTag" },
679
+ 0
680
+ ]
681
+ ];
682
+ },
683
+ addKeyboardShortcuts() {
684
+ return {
685
+ ...this.parent?.(),
686
+ "Mod-a": ({ editor }) => {
687
+ const { state } = editor;
688
+ const { selection } = state;
689
+ const { $from } = selection;
690
+ for (let depth = $from.depth; depth >= 1; depth--) if ($from.node(depth).type.name === this.name) {
691
+ const blockStart = $from.start(depth);
692
+ const blockEnd = $from.end(depth);
693
+ if (selection.from === blockStart && selection.to === blockEnd) return false;
694
+ const tr = state.tr.setSelection(_tiptap_pm_state.TextSelection.create(state.doc, blockStart, blockEnd));
695
+ editor.view.dispatch(tr);
696
+ return true;
697
+ }
698
+ return false;
699
+ }
700
+ };
701
+ },
702
+ addProseMirrorPlugins() {
703
+ return [...this.parent?.() || [], PrismPlugin({
704
+ name: this.name,
705
+ defaultLanguage: this.options.defaultLanguage,
706
+ defaultTheme: this.options.defaultTheme
707
+ })];
708
+ }
709
+ }), ({ node, style }) => {
710
+ const language = node.attrs?.language ? `${node.attrs.language}` : "javascript";
711
+ const userTheme = _react_email_components[node.attrs?.theme];
712
+ const theme = userTheme ? {
713
+ ...userTheme,
714
+ base: {
715
+ ...userTheme.base,
716
+ borderRadius: "0.125rem",
717
+ padding: "0.75rem 1rem"
718
+ }
719
+ } : { base: {
720
+ color: "#1e293b",
721
+ background: "#f1f5f9",
722
+ lineHeight: "1.5",
723
+ fontFamily: "\"Fira Code\", \"Fira Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace",
724
+ padding: "0.75rem 1rem",
725
+ borderRadius: "0.125rem"
726
+ } };
727
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.CodeBlock, {
728
+ code: node.content?.[0]?.text ?? "",
729
+ language,
730
+ theme,
731
+ style: {
732
+ width: "auto",
733
+ ...style
734
+ }
735
+ });
736
+ });
737
+
738
+ //#endregion
739
+ //#region src/extensions/div.tsx
740
+ const Div = require_columns.EmailNode.create({
741
+ name: "div",
742
+ group: "block",
743
+ content: "block+",
744
+ defining: true,
745
+ isolating: true,
746
+ parseHTML() {
747
+ return [{
748
+ tag: "div:not([data-type])",
749
+ getAttrs: (node) => {
750
+ if (typeof node === "string") return false;
751
+ const element = node;
752
+ const attrs = {};
753
+ Array.from(element.attributes).forEach((attr) => {
754
+ attrs[attr.name] = attr.value;
755
+ });
756
+ return attrs;
757
+ }
758
+ }];
759
+ },
760
+ renderHTML({ HTMLAttributes }) {
761
+ return [
762
+ "div",
763
+ (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
764
+ 0
765
+ ];
766
+ },
767
+ addAttributes() {
768
+ return { ...require_columns.createStandardAttributes([...require_columns.COMMON_HTML_ATTRIBUTES, ...require_columns.LAYOUT_ATTRIBUTES]) };
769
+ },
770
+ renderToReactEmail({ children, node, style }) {
771
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
772
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
773
+ className: node.attrs?.class || void 0,
774
+ style: {
775
+ ...style,
776
+ ...inlineStyles
777
+ },
778
+ children
779
+ });
780
+ }
781
+ });
782
+
783
+ //#endregion
784
+ //#region src/extensions/divider.tsx
785
+ const Divider = require_columns.EmailNode.from(_tiptap_extension_horizontal_rule.default.extend({
786
+ addAttributes() {
787
+ return { class: { default: "divider" } };
788
+ },
789
+ addInputRules() {
790
+ return [new _tiptap_core.InputRule({
791
+ find: /^(?:---|—-|___\s|\*\*\*\s)$/,
792
+ handler: ({ state, range }) => {
793
+ const attributes = {};
794
+ const { tr } = state;
795
+ const start = range.from;
796
+ const end = range.to;
797
+ tr.insert(start - 1, this.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
798
+ }
799
+ })];
800
+ },
801
+ addNodeView() {
802
+ return (0, _tiptap_react.ReactNodeViewRenderer)((props) => {
803
+ const node = props.node;
804
+ const { class: className, ...rest } = node.attrs;
805
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react.NodeViewWrapper, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Hr, {
806
+ ...rest,
807
+ className: "node-hr",
808
+ style: require_columns.inlineCssToJs(node.attrs.style)
809
+ }) });
810
+ });
811
+ }
812
+ }), ({ node, style }) => {
813
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Hr, {
814
+ className: node.attrs?.class || void 0,
815
+ style: {
816
+ ...style,
817
+ ...require_columns.inlineCssToJs(node.attrs?.style)
818
+ }
819
+ });
820
+ });
821
+
822
+ //#endregion
823
+ //#region src/extensions/global-content.ts
824
+ const GLOBAL_CONTENT_NODE_TYPE = "globalContent";
825
+ let cachedGlobalPosition = null;
826
+ function findGlobalContentPositions(doc) {
827
+ const positions = [];
828
+ doc.descendants((node, position) => {
829
+ if (node.type.name === GLOBAL_CONTENT_NODE_TYPE) positions.push(position);
830
+ });
831
+ return positions;
832
+ }
833
+ function getCachedGlobalContentPosition(doc) {
834
+ if (cachedGlobalPosition != null) try {
835
+ if (doc.nodeAt(cachedGlobalPosition)?.type.name === GLOBAL_CONTENT_NODE_TYPE) return cachedGlobalPosition;
836
+ } catch {
837
+ cachedGlobalPosition = null;
838
+ }
839
+ cachedGlobalPosition = findGlobalContentPositions(doc)[0] ?? null;
840
+ return cachedGlobalPosition;
841
+ }
842
+ function getGlobalContent(key, editor) {
843
+ const position = getCachedGlobalContentPosition(editor.state.doc);
844
+ if (cachedGlobalPosition == null) return null;
845
+ return editor.state.doc.nodeAt(position)?.attrs.data[key] ?? null;
846
+ }
847
+ const GlobalContent = _tiptap_core.Node.create({
848
+ name: GLOBAL_CONTENT_NODE_TYPE,
849
+ addOptions() {
850
+ return {
851
+ key: GLOBAL_CONTENT_NODE_TYPE,
852
+ data: {}
853
+ };
854
+ },
855
+ group: "block",
856
+ selectable: false,
857
+ draggable: false,
858
+ atom: true,
859
+ addAttributes() {
860
+ return { data: { default: this.options.data } };
861
+ },
862
+ parseHTML() {
863
+ return [{ tag: `div[data-type="${this.name}"]` }];
864
+ },
865
+ renderHTML({ HTMLAttributes }) {
866
+ return ["div", (0, _tiptap_core.mergeAttributes)(HTMLAttributes, {
867
+ "data-type": this.name,
868
+ style: "width: 100%; height: 1px; visibility: hidden;"
869
+ })];
870
+ },
871
+ addCommands() {
872
+ return { setGlobalContent: (key, value) => ({ tr, dispatch }) => {
873
+ const ensureGlobalPosition = () => {
874
+ const positions = findGlobalContentPositions(tr.doc);
875
+ for (let i = positions.length - 1; i > 0; i--) tr.delete(positions[i], positions[i] + 1);
876
+ const pos = positions[0] ?? -1;
877
+ if (pos >= 0) cachedGlobalPosition = pos;
878
+ else {
879
+ cachedGlobalPosition = 0;
880
+ tr.insert(0, this.type.create());
881
+ }
882
+ };
883
+ if (dispatch) {
884
+ ensureGlobalPosition();
885
+ if (cachedGlobalPosition == null) return false;
886
+ tr.setNodeAttribute(cachedGlobalPosition, "data", {
887
+ ...tr.doc.nodeAt(cachedGlobalPosition)?.attrs.data,
888
+ [key]: value
889
+ });
890
+ }
891
+ return true;
892
+ } };
893
+ }
894
+ });
895
+
896
+ //#endregion
897
+ //#region src/extensions/hard-break.tsx
898
+ const HardBreak = require_columns.EmailNode.from(_tiptap_extension_hard_break.default, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}));
899
+
900
+ //#endregion
901
+ //#region src/extensions/heading.tsx
902
+ const Heading = require_columns.EmailNode.from(_tiptap_extension_heading.Heading.extend({ addNodeView() {
903
+ return (0, _tiptap_react.ReactNodeViewRenderer)(({ node }) => {
904
+ const level = node.attrs.level ?? 1;
905
+ const { class: className, ...rest } = node.attrs;
906
+ const attrs = {
907
+ ...rest,
908
+ className: `node-h${level} ${className}`,
909
+ style: require_columns.inlineCssToJs(node.attrs.style)
910
+ };
911
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react.NodeViewWrapper, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Heading, {
912
+ as: `h${level}`,
913
+ ...attrs,
914
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tiptap_react.NodeViewContent, {})
915
+ }) });
916
+ });
917
+ } }), ({ children, node, style }) => {
918
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Heading, {
919
+ as: `h${node.attrs?.level ?? 1}`,
920
+ className: node.attrs?.class || void 0,
921
+ style: {
922
+ ...style,
923
+ ...require_columns.inlineCssToJs(node.attrs?.style),
924
+ ...getTextAlignment(node.attrs?.align ?? node.attrs?.alignment)
925
+ },
926
+ children
927
+ });
928
+ });
929
+
930
+ //#endregion
931
+ //#region src/extensions/italic.tsx
932
+ const Italic = EmailMark.from(_tiptap_extension_italic.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", {
933
+ style,
934
+ children
935
+ }));
936
+
937
+ //#endregion
938
+ //#region src/extensions/preserved-style.tsx
939
+ const PreservedStyle = EmailMark.create({
940
+ name: "preservedStyle",
941
+ addAttributes() {
942
+ return { style: {
943
+ default: null,
944
+ parseHTML: (element) => element.getAttribute("style"),
945
+ renderHTML: (attributes) => {
946
+ if (!attributes.style) return {};
947
+ return { style: attributes.style };
948
+ }
949
+ } };
950
+ },
951
+ parseHTML() {
952
+ return [{
953
+ tag: "span[style]",
954
+ getAttrs: (element) => {
955
+ if (typeof element === "string") return false;
956
+ const style = element.getAttribute("style");
957
+ if (style && hasPreservableStyles(style)) return { style };
958
+ return false;
959
+ }
960
+ }];
961
+ },
962
+ renderHTML({ HTMLAttributes }) {
963
+ return [
964
+ "span",
965
+ (0, _tiptap_core.mergeAttributes)(HTMLAttributes),
966
+ 0
967
+ ];
968
+ },
969
+ renderToReactEmail({ children, mark }) {
970
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
971
+ style: mark.attrs?.style ? require_columns.inlineCssToJs(mark.attrs.style) : void 0,
972
+ children
973
+ });
974
+ }
975
+ });
976
+ const LINK_INDICATOR_STYLES = [
977
+ "color",
978
+ "text-decoration",
979
+ "text-decoration-line",
980
+ "text-decoration-color",
981
+ "text-decoration-style"
982
+ ];
983
+ function parseStyleString(styleString) {
984
+ const temp = document.createElement("div");
985
+ temp.style.cssText = styleString;
986
+ return temp.style;
987
+ }
988
+ function hasBackground(style) {
989
+ const bgColor = style.backgroundColor;
990
+ const bg = style.background;
991
+ if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") return true;
992
+ if (bg && bg !== "transparent" && bg !== "none" && bg !== "rgba(0, 0, 0, 0)") return true;
993
+ return false;
994
+ }
995
+ function hasPreservableStyles(styleString) {
996
+ return processStylesForUnlink(styleString) !== null;
997
+ }
998
+ /**
999
+ * Processes styles when unlinking:
1000
+ * - Has background (button-like): preserve all styles
1001
+ * - No background: strip link-indicator styles (color, text-decoration), keep the rest
1002
+ */
1003
+ function processStylesForUnlink(styleString) {
1004
+ if (!styleString) return null;
1005
+ const style = parseStyleString(styleString);
1006
+ if (hasBackground(style)) return styleString;
1007
+ const filtered = [];
1008
+ for (let i = 0; i < style.length; i++) {
1009
+ const prop = style[i];
1010
+ if (LINK_INDICATOR_STYLES.includes(prop)) continue;
1011
+ const value = style.getPropertyValue(prop);
1012
+ if (value) filtered.push(`${prop}: ${value}`);
1013
+ }
1014
+ return filtered.length > 0 ? filtered.join("; ") : null;
1015
+ }
1016
+
1017
+ //#endregion
1018
+ //#region src/extensions/link.tsx
1019
+ const Link = EmailMark.from(_tiptap_extension_link.default, ({ children, mark, style }) => {
1020
+ const linkMarkStyle = mark.attrs?.style ? require_columns.inlineCssToJs(mark.attrs.style) : {};
1021
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Link, {
1022
+ href: mark.attrs?.href ?? void 0,
1023
+ rel: mark.attrs?.rel ?? void 0,
1024
+ style: {
1025
+ ...style,
1026
+ ...linkMarkStyle
1027
+ },
1028
+ target: mark.attrs?.target ?? void 0,
1029
+ ...mark.attrs?.["ses:no-track"] ? { "ses:no-track": mark.attrs["ses:no-track"] } : {},
1030
+ children
1031
+ });
1032
+ }).extend({
1033
+ parseHTML() {
1034
+ return [{
1035
+ tag: "a[target]:not([data-id=\"react-email-button\"])",
1036
+ getAttrs: (node) => {
1037
+ if (typeof node === "string") return false;
1038
+ const element = node;
1039
+ const attrs = {};
1040
+ Array.from(element.attributes).forEach((attr) => {
1041
+ attrs[attr.name] = attr.value;
1042
+ });
1043
+ return attrs;
1044
+ }
1045
+ }, {
1046
+ tag: "a[href]:not([data-id=\"react-email-button\"])",
1047
+ getAttrs: (node) => {
1048
+ if (typeof node === "string") return false;
1049
+ const element = node;
1050
+ const attrs = {};
1051
+ Array.from(element.attributes).forEach((attr) => {
1052
+ attrs[attr.name] = attr.value;
1053
+ });
1054
+ return attrs;
1055
+ }
1056
+ }];
1057
+ },
1058
+ addAttributes() {
1059
+ return {
1060
+ ...this.parent?.(),
1061
+ "ses:no-track": {
1062
+ default: null,
1063
+ parseHTML: (element) => element.getAttribute("ses:no-track")
1064
+ }
1065
+ };
1066
+ },
1067
+ addCommands() {
1068
+ return {
1069
+ ...this.parent?.(),
1070
+ unsetLink: () => ({ state, chain }) => {
1071
+ const { from } = state.selection;
1072
+ const linkStyle = state.doc.resolve(from).marks().find((m) => m.type.name === "link")?.attrs?.style ?? null;
1073
+ const preservedStyle = processStylesForUnlink(linkStyle);
1074
+ const shouldRemoveUnderline = preservedStyle !== linkStyle;
1075
+ if (preservedStyle) {
1076
+ const cmd = chain().extendMarkRange("link").unsetMark("link").setMark("preservedStyle", { style: preservedStyle });
1077
+ return shouldRemoveUnderline ? cmd.unsetMark("underline").run() : cmd.run();
1078
+ }
1079
+ return chain().extendMarkRange("link").unsetMark("link").unsetMark("underline").run();
1080
+ }
1081
+ };
1082
+ },
1083
+ addKeyboardShortcuts() {
1084
+ return { "Mod-k": () => {
1085
+ require_columns.editorEventBus.dispatch("bubble-menu:add-link", void 0);
1086
+ return this.editor.chain().focus().toggleLink({ href: "" }).run();
1087
+ } };
1088
+ }
1089
+ });
1090
+
1091
+ //#endregion
1092
+ //#region src/extensions/list-item.tsx
1093
+ const ListItem = require_columns.EmailNode.from(_tiptap_extension_list_item.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
1094
+ className: node.attrs?.class || void 0,
1095
+ style: {
1096
+ ...style,
1097
+ ...require_columns.inlineCssToJs(node.attrs?.style),
1098
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
1099
+ },
1100
+ children
1101
+ }));
1102
+
1103
+ //#endregion
1104
+ //#region src/extensions/max-nesting.ts
1105
+ const MaxNesting = _tiptap_core.Extension.create({
1106
+ name: "maxNesting",
1107
+ addOptions() {
1108
+ return {
1109
+ maxDepth: 3,
1110
+ nodeTypes: void 0
1111
+ };
1112
+ },
1113
+ addProseMirrorPlugins() {
1114
+ const { maxDepth, nodeTypes } = this.options;
1115
+ if (typeof maxDepth !== "number" || maxDepth < 1) throw new Error("maxDepth must be a positive number");
1116
+ return [new _tiptap_pm_state.Plugin({
1117
+ key: new _tiptap_pm_state.PluginKey("maxNesting"),
1118
+ appendTransaction(transactions, _oldState, newState) {
1119
+ if (!transactions.some((tr$1) => tr$1.docChanged)) return null;
1120
+ const rangesToLift = [];
1121
+ newState.doc.descendants((node, pos) => {
1122
+ let depth = 0;
1123
+ let currentPos = pos;
1124
+ let currentNode = node;
1125
+ while (currentNode && depth <= maxDepth) {
1126
+ if (!nodeTypes || nodeTypes.includes(currentNode.type.name)) depth++;
1127
+ const $pos = newState.doc.resolve(currentPos);
1128
+ if ($pos.depth === 0) break;
1129
+ currentPos = $pos.before($pos.depth);
1130
+ currentNode = newState.doc.nodeAt(currentPos);
1131
+ }
1132
+ if (depth > maxDepth) {
1133
+ const $pos = newState.doc.resolve(pos);
1134
+ if ($pos.depth > 0) {
1135
+ const range = $pos.blockRange();
1136
+ if (range && "canReplace" in newState.schema.nodes.doc && typeof newState.schema.nodes.doc.canReplace === "function" && newState.schema.nodes.doc.canReplace(range.start - 1, range.end + 1, newState.doc.slice(range.start, range.end).content)) rangesToLift.push({
1137
+ range,
1138
+ target: range.start - 1
1139
+ });
1140
+ }
1141
+ }
1142
+ });
1143
+ if (rangesToLift.length === 0) return null;
1144
+ const tr = newState.tr;
1145
+ for (let i = rangesToLift.length - 1; i >= 0; i--) {
1146
+ const { range, target } = rangesToLift[i];
1147
+ tr.lift(range, target);
1148
+ }
1149
+ return tr;
1150
+ },
1151
+ filterTransaction(tr) {
1152
+ if (!tr.docChanged) return true;
1153
+ let wouldCreateDeepNesting = false;
1154
+ const newDoc = tr.doc;
1155
+ newDoc.descendants((node, pos) => {
1156
+ if (wouldCreateDeepNesting) return false;
1157
+ let depth = 0;
1158
+ let currentPos = pos;
1159
+ let currentNode = node;
1160
+ while (currentNode && depth <= maxDepth) {
1161
+ if (!nodeTypes || nodeTypes.includes(currentNode.type.name)) depth++;
1162
+ const $pos = newDoc.resolve(currentPos);
1163
+ if ($pos.depth === 0) break;
1164
+ currentPos = $pos.before($pos.depth);
1165
+ currentNode = newDoc.nodeAt(currentPos);
1166
+ }
1167
+ if (depth > maxDepth) {
1168
+ wouldCreateDeepNesting = true;
1169
+ return false;
1170
+ }
1171
+ });
1172
+ return !wouldCreateDeepNesting;
1173
+ }
1174
+ })];
1175
+ }
1176
+ });
1177
+
1178
+ //#endregion
1179
+ //#region src/extensions/ordered-list.tsx
1180
+ const OrderedList = require_columns.EmailNode.from(_tiptap_extension_ordered_list.default, ({ children, node, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
1181
+ className: node.attrs?.class || void 0,
1182
+ start: node.attrs?.start,
1183
+ style: {
1184
+ ...style,
1185
+ ...require_columns.inlineCssToJs(node.attrs?.style)
1186
+ },
1187
+ children
1188
+ }));
1189
+
1190
+ //#endregion
1191
+ //#region src/extensions/paragraph.tsx
1192
+ const Paragraph = require_columns.EmailNode.from(_tiptap_extension_paragraph.default, ({ children, node, style }) => {
1193
+ const isEmpty = !node.content || node.content.length === 0;
1194
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1195
+ className: node.attrs?.class || void 0,
1196
+ style: {
1197
+ ...style,
1198
+ ...require_columns.inlineCssToJs(node.attrs?.style),
1199
+ ...getTextAlignment(node.attrs?.align || node.attrs?.alignment)
1200
+ },
1201
+ children: isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("br", {}) : children
1202
+ });
1203
+ });
1204
+
1205
+ //#endregion
1206
+ //#region src/extensions/placeholder.ts
1207
+ const Placeholder = _tiptap_extension_placeholder.default.configure({
1208
+ placeholder: ({ node }) => {
1209
+ if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
1210
+ return "Press '/' for commands";
1211
+ },
1212
+ includeChildren: true
1213
+ });
1214
+
1215
+ //#endregion
1216
+ //#region src/extensions/preview-text.ts
1217
+ const PreviewText = _tiptap_core.Node.create({
1218
+ name: "previewText",
1219
+ group: "block",
1220
+ selectable: false,
1221
+ draggable: false,
1222
+ atom: true,
1223
+ addOptions() {
1224
+ return { HTMLAttributes: {} };
1225
+ },
1226
+ addStorage() {
1227
+ return { previewText: null };
1228
+ },
1229
+ renderHTML() {
1230
+ return ["div", { style: "display: none" }];
1231
+ },
1232
+ parseHTML() {
1233
+ return [{
1234
+ tag: "div[data-skip-in-text=\"true\"]",
1235
+ getAttrs: (node) => {
1236
+ if (typeof node === "string") return false;
1237
+ const element = node;
1238
+ let directText = "";
1239
+ for (const child of element.childNodes) if (child.nodeType === 3) directText += child.textContent || "";
1240
+ const cleanText = directText.trim();
1241
+ if (cleanText) this.storage.previewText = cleanText;
1242
+ return false;
1243
+ }
1244
+ }, {
1245
+ tag: "span.preheader",
1246
+ getAttrs: (node) => {
1247
+ if (typeof node === "string") return false;
1248
+ const preheaderText = node.textContent?.trim();
1249
+ if (preheaderText) this.storage.previewText = preheaderText;
1250
+ return false;
1251
+ }
1252
+ }];
1253
+ }
1254
+ });
1255
+
1256
+ //#endregion
1257
+ //#region src/extensions/section.tsx
1258
+ const Section$1 = require_columns.EmailNode.create({
1259
+ name: "section",
1260
+ group: "block",
1261
+ content: "block+",
1262
+ isolating: true,
1263
+ defining: true,
1264
+ parseHTML() {
1265
+ return [{ tag: "section[data-type=\"section\"]" }];
1266
+ },
1267
+ renderHTML({ HTMLAttributes }) {
1268
+ return [
1269
+ "section",
1270
+ (0, _tiptap_core.mergeAttributes)({
1271
+ "data-type": "section",
1272
+ class: "node-section"
1273
+ }, HTMLAttributes),
1274
+ 0
1275
+ ];
1276
+ },
1277
+ addCommands() {
1278
+ return { insertSection: () => ({ commands }) => {
1279
+ return commands.insertContent({
1280
+ type: this.name,
1281
+ content: [{
1282
+ type: "paragraph",
1283
+ content: []
1284
+ }]
1285
+ });
1286
+ } };
1287
+ },
1288
+ renderToReactEmail({ children, node, style }) {
1289
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
1290
+ const textAlign = node.attrs?.align || node.attrs?.alignment;
1291
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
1292
+ className: node.attrs?.class || void 0,
1293
+ align: textAlign,
1294
+ style: {
1295
+ ...style,
1296
+ ...inlineStyles,
1297
+ ...getTextAlignment(textAlign)
1298
+ },
1299
+ children
1300
+ });
1301
+ }
1302
+ });
1303
+
1304
+ //#endregion
1305
+ //#region src/extensions/strike.tsx
1306
+ const Strike = EmailMark.from(_tiptap_extension_strike.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("s", {
1307
+ style,
1308
+ children
1309
+ }));
1310
+
1311
+ //#endregion
1312
+ //#region src/extensions/style-attribute.tsx
1313
+ const StyleAttribute = _tiptap_core.Extension.create({
1314
+ name: "styleAttribute",
1315
+ priority: 101,
1316
+ addOptions() {
1317
+ return {
1318
+ types: [],
1319
+ style: []
1320
+ };
1321
+ },
1322
+ addGlobalAttributes() {
1323
+ return [{
1324
+ types: this.options.types,
1325
+ attributes: { style: {
1326
+ default: "",
1327
+ parseHTML: (element) => element.getAttribute("style") || "",
1328
+ renderHTML: (attributes) => {
1329
+ return { style: attributes.style ?? "" };
1330
+ }
1331
+ } }
1332
+ }];
1333
+ },
1334
+ addCommands() {
1335
+ return {
1336
+ unsetStyle: () => ({ commands }) => {
1337
+ return this.options.types.every((type) => commands.resetAttributes(type, "style"));
1338
+ },
1339
+ setStyle: (style) => ({ commands }) => {
1340
+ return this.options.types.every((type) => commands.updateAttributes(type, { style }));
1341
+ }
1342
+ };
1343
+ },
1344
+ addKeyboardShortcuts() {
1345
+ return { Enter: ({ editor }) => {
1346
+ const { state } = editor.view;
1347
+ const { selection } = state;
1348
+ const { $from } = selection;
1349
+ const textBefore = $from.nodeBefore?.text || "";
1350
+ if (textBefore.includes("{{") || textBefore.includes("{{{")) return false;
1351
+ requestAnimationFrame(() => {
1352
+ editor.commands.resetAttributes("paragraph", "style");
1353
+ });
1354
+ return false;
1355
+ } };
1356
+ }
1357
+ });
1358
+
1359
+ //#endregion
1360
+ //#region src/extensions/sup.tsx
1361
+ const SupBase = _tiptap_extension_superscript.default.extend({
1362
+ name: "sup",
1363
+ addCommands() {
1364
+ return {
1365
+ ...this.parent?.(),
1366
+ setSup: () => ({ commands }) => {
1367
+ return commands.setMark(this.name);
1368
+ },
1369
+ toggleSup: () => ({ commands }) => {
1370
+ return commands.toggleMark(this.name);
1371
+ },
1372
+ unsetSup: () => ({ commands }) => {
1373
+ return commands.unsetMark(this.name);
1374
+ }
1375
+ };
1376
+ }
1377
+ });
1378
+ const Sup = EmailMark.from(SupBase, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("sup", {
1379
+ style,
1380
+ children
1381
+ }));
1382
+
1383
+ //#endregion
1384
+ //#region src/extensions/table.tsx
1385
+ const Table = require_columns.EmailNode.create({
1386
+ name: "table",
1387
+ group: "block",
1388
+ content: "tableRow+",
1389
+ isolating: true,
1390
+ tableRole: "table",
1391
+ addAttributes() {
1392
+ return { ...require_columns.createStandardAttributes([
1393
+ ...require_columns.TABLE_ATTRIBUTES,
1394
+ ...require_columns.LAYOUT_ATTRIBUTES,
1395
+ ...require_columns.COMMON_HTML_ATTRIBUTES
1396
+ ]) };
1397
+ },
1398
+ parseHTML() {
1399
+ return [{
1400
+ tag: "table",
1401
+ getAttrs: (node) => {
1402
+ if (typeof node === "string") return false;
1403
+ const element = node;
1404
+ const attrs = {};
1405
+ Array.from(element.attributes).forEach((attr) => {
1406
+ attrs[attr.name] = attr.value;
1407
+ });
1408
+ return attrs;
1409
+ }
1410
+ }];
1411
+ },
1412
+ renderHTML({ HTMLAttributes }) {
1413
+ return [
1414
+ "table",
1415
+ (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
1416
+ [
1417
+ "tbody",
1418
+ {},
1419
+ 0
1420
+ ]
1421
+ ];
1422
+ },
1423
+ renderToReactEmail({ children, node, style }) {
1424
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
1425
+ const alignment = node.attrs?.align || node.attrs?.alignment;
1426
+ const width = node.attrs?.width;
1427
+ const centeringStyles = alignment === "center" ? {
1428
+ marginLeft: "auto",
1429
+ marginRight: "auto"
1430
+ } : {};
1431
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Section, {
1432
+ className: node.attrs?.class || void 0,
1433
+ align: alignment,
1434
+ style: require_columns.resolveConflictingStyles(style, {
1435
+ ...inlineStyles,
1436
+ ...centeringStyles
1437
+ }),
1438
+ ...width !== void 0 ? { width } : {},
1439
+ children
1440
+ });
1441
+ }
1442
+ });
1443
+ const TableRow = require_columns.EmailNode.create({
1444
+ name: "tableRow",
1445
+ group: "tableRow",
1446
+ content: "(tableCell | tableHeader)+",
1447
+ addAttributes() {
1448
+ return { ...require_columns.createStandardAttributes([
1449
+ ...require_columns.TABLE_CELL_ATTRIBUTES,
1450
+ ...require_columns.LAYOUT_ATTRIBUTES,
1451
+ ...require_columns.COMMON_HTML_ATTRIBUTES
1452
+ ]) };
1453
+ },
1454
+ parseHTML() {
1455
+ return [{
1456
+ tag: "tr",
1457
+ getAttrs: (node) => {
1458
+ if (typeof node === "string") return false;
1459
+ const element = node;
1460
+ const attrs = {};
1461
+ Array.from(element.attributes).forEach((attr) => {
1462
+ attrs[attr.name] = attr.value;
1463
+ });
1464
+ return attrs;
1465
+ }
1466
+ }];
1467
+ },
1468
+ renderHTML({ HTMLAttributes }) {
1469
+ return [
1470
+ "tr",
1471
+ HTMLAttributes,
1472
+ 0
1473
+ ];
1474
+ },
1475
+ renderToReactEmail({ children, node, style }) {
1476
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
1477
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
1478
+ className: node.attrs?.class || void 0,
1479
+ style: {
1480
+ ...style,
1481
+ ...inlineStyles
1482
+ },
1483
+ children
1484
+ });
1485
+ }
1486
+ });
1487
+ const TableCell = require_columns.EmailNode.create({
1488
+ name: "tableCell",
1489
+ group: "tableCell",
1490
+ content: "block+",
1491
+ isolating: true,
1492
+ addAttributes() {
1493
+ return { ...require_columns.createStandardAttributes([
1494
+ ...require_columns.TABLE_CELL_ATTRIBUTES,
1495
+ ...require_columns.LAYOUT_ATTRIBUTES,
1496
+ ...require_columns.COMMON_HTML_ATTRIBUTES
1497
+ ]) };
1498
+ },
1499
+ parseHTML() {
1500
+ return [{
1501
+ tag: "td",
1502
+ getAttrs: (node) => {
1503
+ if (typeof node === "string") return false;
1504
+ const element = node;
1505
+ const attrs = {};
1506
+ Array.from(element.attributes).forEach((attr) => {
1507
+ attrs[attr.name] = attr.value;
1508
+ });
1509
+ return attrs;
1510
+ }
1511
+ }];
1512
+ },
1513
+ renderHTML({ HTMLAttributes }) {
1514
+ return [
1515
+ "td",
1516
+ HTMLAttributes,
1517
+ 0
1518
+ ];
1519
+ },
1520
+ renderToReactEmail({ children, node, style }) {
1521
+ const inlineStyles = require_columns.inlineCssToJs(node.attrs?.style);
1522
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_email_components.Column, {
1523
+ className: node.attrs?.class || void 0,
1524
+ align: node.attrs?.align || node.attrs?.alignment,
1525
+ style: {
1526
+ ...style,
1527
+ ...inlineStyles
1528
+ },
1529
+ children
1530
+ });
1531
+ }
1532
+ });
1533
+ const TableHeader = _tiptap_core.Node.create({
1534
+ name: "tableHeader",
1535
+ group: "tableCell",
1536
+ content: "block+",
1537
+ isolating: true,
1538
+ addAttributes() {
1539
+ return { ...require_columns.createStandardAttributes([
1540
+ ...require_columns.TABLE_HEADER_ATTRIBUTES,
1541
+ ...require_columns.TABLE_CELL_ATTRIBUTES,
1542
+ ...require_columns.LAYOUT_ATTRIBUTES,
1543
+ ...require_columns.COMMON_HTML_ATTRIBUTES
1544
+ ]) };
1545
+ },
1546
+ parseHTML() {
1547
+ return [{
1548
+ tag: "th",
1549
+ getAttrs: (node) => {
1550
+ if (typeof node === "string") return false;
1551
+ const element = node;
1552
+ const attrs = {};
1553
+ Array.from(element.attributes).forEach((attr) => {
1554
+ attrs[attr.name] = attr.value;
1555
+ });
1556
+ return attrs;
1557
+ }
1558
+ }];
1559
+ },
1560
+ renderHTML({ HTMLAttributes }) {
1561
+ return [
1562
+ "th",
1563
+ HTMLAttributes,
1564
+ 0
1565
+ ];
1566
+ }
1567
+ });
1568
+
1569
+ //#endregion
1570
+ //#region src/extensions/underline.tsx
1571
+ const Underline = EmailMark.from(_tiptap_extension_underline.default, ({ children, style }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("u", {
1572
+ style,
1573
+ children
1574
+ }));
1575
+
1576
+ //#endregion
1577
+ //#region src/extensions/uppercase.tsx
1578
+ const Uppercase = EmailMark.create({
1579
+ name: "uppercase",
1580
+ addOptions() {
1581
+ return { HTMLAttributes: {} };
1582
+ },
1583
+ parseHTML() {
1584
+ return [{
1585
+ tag: "span",
1586
+ getAttrs: (node) => {
1587
+ if (node.style.textTransform === "uppercase") return {};
1588
+ return false;
1589
+ }
1590
+ }];
1591
+ },
1592
+ renderHTML({ HTMLAttributes }) {
1593
+ return [
1594
+ "span",
1595
+ (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { style: "text-transform: uppercase" }),
1596
+ 0
1597
+ ];
1598
+ },
1599
+ renderToReactEmail({ children, style }) {
1600
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1601
+ style: {
1602
+ ...style,
1603
+ textTransform: "uppercase"
1604
+ },
1605
+ children
1606
+ });
1607
+ },
1608
+ addCommands() {
1609
+ return {
1610
+ setUppercase: () => ({ commands }) => {
1611
+ return commands.setMark(this.name);
1612
+ },
1613
+ toggleUppercase: () => ({ commands }) => {
1614
+ return commands.toggleMark(this.name);
1615
+ },
1616
+ unsetUppercase: () => ({ commands }) => {
1617
+ return commands.unsetMark(this.name);
1618
+ }
1619
+ };
1620
+ }
1621
+ });
1622
+
1623
+ //#endregion
1624
+ //#region src/extensions/index.ts
1625
+ const starterKitExtensions = {
1626
+ CodeBlockPrism,
1627
+ Code,
1628
+ TwoColumns: require_columns.TwoColumns,
1629
+ ThreeColumns: require_columns.ThreeColumns,
1630
+ FourColumns: require_columns.FourColumns,
1631
+ ColumnsColumn: require_columns.ColumnsColumn,
1632
+ Paragraph,
1633
+ BulletList,
1634
+ OrderedList,
1635
+ Blockquote,
1636
+ ListItem,
1637
+ HardBreak,
1638
+ Italic,
1639
+ Placeholder,
1640
+ PreviewText,
1641
+ Bold,
1642
+ Strike,
1643
+ Heading,
1644
+ Divider,
1645
+ Link,
1646
+ Sup,
1647
+ Underline,
1648
+ Uppercase,
1649
+ PreservedStyle,
1650
+ Table,
1651
+ TableRow,
1652
+ TableCell,
1653
+ TableHeader,
1654
+ Body,
1655
+ Div,
1656
+ Button,
1657
+ Section: Section$1,
1658
+ GlobalContent,
1659
+ AlignmentAttribute,
1660
+ StyleAttribute,
1661
+ ClassAttribute,
1662
+ MaxNesting
1663
+ };
1664
+ const StarterKit = _tiptap_core.Extension.create({
1665
+ name: "reactEmailStarterKit",
1666
+ addOptions() {
1667
+ return {
1668
+ TiptapStarterKit: {},
1669
+ CodeBlockPrism: {
1670
+ defaultLanguage: "javascript",
1671
+ HTMLAttributes: { class: "prism node-codeBlock" }
1672
+ },
1673
+ Code: { HTMLAttributes: {
1674
+ class: "node-inlineCode",
1675
+ spellcheck: "false"
1676
+ } },
1677
+ TwoColumns: {},
1678
+ ThreeColumns: {},
1679
+ FourColumns: {},
1680
+ ColumnsColumn: {},
1681
+ Paragraph: { HTMLAttributes: { class: "node-paragraph" } },
1682
+ BulletList: { HTMLAttributes: { class: "node-bulletList" } },
1683
+ OrderedList: { HTMLAttributes: { class: "node-orderedList" } },
1684
+ Blockquote: { HTMLAttributes: { class: "node-blockquote" } },
1685
+ ListItem: {},
1686
+ HardBreak: {},
1687
+ Italic: {},
1688
+ Placeholder: {},
1689
+ PreviewText: {},
1690
+ Bold: {},
1691
+ Strike: {},
1692
+ Heading: {},
1693
+ Divider: {},
1694
+ Link: {},
1695
+ Sup: {},
1696
+ Underline: {},
1697
+ Uppercase: {},
1698
+ PreservedStyle: {},
1699
+ Table: {},
1700
+ TableRow: {},
1701
+ TableCell: {},
1702
+ TableHeader: {},
1703
+ Body: {},
1704
+ Div: {},
1705
+ Button: {},
1706
+ Section: {},
1707
+ GlobalContent: {},
1708
+ AlignmentAttribute: { types: [
1709
+ "heading",
1710
+ "paragraph",
1711
+ "image",
1712
+ "blockquote",
1713
+ "codeBlock",
1714
+ "bulletList",
1715
+ "orderedList",
1716
+ "listItem",
1717
+ "button",
1718
+ "youtube",
1719
+ "twitter",
1720
+ "table",
1721
+ "tableRow",
1722
+ "tableCell",
1723
+ "tableHeader",
1724
+ "columnsColumn"
1725
+ ] },
1726
+ StyleAttribute: { types: [
1727
+ "heading",
1728
+ "paragraph",
1729
+ "image",
1730
+ "blockquote",
1731
+ "codeBlock",
1732
+ "bulletList",
1733
+ "orderedList",
1734
+ "listItem",
1735
+ "button",
1736
+ "youtube",
1737
+ "twitter",
1738
+ "horizontalRule",
1739
+ "footer",
1740
+ "section",
1741
+ "div",
1742
+ "body",
1743
+ "table",
1744
+ "tableRow",
1745
+ "tableCell",
1746
+ "tableHeader",
1747
+ "columnsColumn",
1748
+ "link"
1749
+ ] },
1750
+ ClassAttribute: { types: [
1751
+ "heading",
1752
+ "paragraph",
1753
+ "image",
1754
+ "blockquote",
1755
+ "bulletList",
1756
+ "orderedList",
1757
+ "listItem",
1758
+ "button",
1759
+ "youtube",
1760
+ "twitter",
1761
+ "horizontalRule",
1762
+ "footer",
1763
+ "section",
1764
+ "div",
1765
+ "body",
1766
+ "table",
1767
+ "tableRow",
1768
+ "tableCell",
1769
+ "tableHeader",
1770
+ "columnsColumn",
1771
+ "link"
1772
+ ] },
1773
+ MaxNesting: {
1774
+ maxDepth: 50,
1775
+ nodeTypes: [
1776
+ "section",
1777
+ "bulletList",
1778
+ "orderedList"
1779
+ ]
1780
+ }
1781
+ };
1782
+ },
1783
+ addExtensions() {
1784
+ const extensions = [];
1785
+ if (this.options.TiptapStarterKit !== false) extensions.push(_tiptap_starter_kit.default.configure({
1786
+ undoRedo: false,
1787
+ heading: false,
1788
+ link: false,
1789
+ underline: false,
1790
+ trailingNode: false,
1791
+ bold: false,
1792
+ italic: false,
1793
+ strike: false,
1794
+ code: false,
1795
+ paragraph: false,
1796
+ bulletList: false,
1797
+ orderedList: false,
1798
+ listItem: false,
1799
+ blockquote: false,
1800
+ hardBreak: false,
1801
+ gapcursor: false,
1802
+ codeBlock: false,
1803
+ horizontalRule: false,
1804
+ dropcursor: {
1805
+ color: "#61a8f8",
1806
+ class: "rounded-full animate-[fade-in_300ms_ease-in-out] !z-40",
1807
+ width: 4
1808
+ },
1809
+ ...this.options.TiptapStarterKit
1810
+ }));
1811
+ for (const [name, extension] of Object.entries(starterKitExtensions)) {
1812
+ const key = name;
1813
+ const extensionOptions = this.options[key];
1814
+ if (extensionOptions !== false) extensions.push(extension.configure(extensionOptions));
1815
+ }
1816
+ return extensions;
1817
+ }
1818
+ });
1819
+
1820
+ //#endregion
1821
+ //#region src/core/create-drop-handler.ts
1822
+ function createDropHandler({ onPaste, onUploadImage }) {
1823
+ return (view, event, _slice, moved) => {
1824
+ if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
1825
+ event.preventDefault();
1826
+ const file = event.dataTransfer.files[0];
1827
+ if (onPaste?.(file, view)) return true;
1828
+ if (file.type.includes("image/") && onUploadImage) {
1829
+ onUploadImage(file, view, (view.posAtCoords({
1830
+ left: event.clientX,
1831
+ top: event.clientY
1832
+ })?.pos || 0) - 1);
1833
+ return true;
1834
+ }
1835
+ }
1836
+ return false;
1837
+ };
1838
+ }
1839
+
1840
+ //#endregion
1841
+ //#region src/utils/paste-sanitizer.ts
1842
+ /**
1843
+ * Sanitizes pasted HTML.
1844
+ * - From editor (has node-* classes): pass through as-is
1845
+ * - From external: strip all styles/classes, keep only semantic HTML
1846
+ */
1847
+ /**
1848
+ * Detects content from the Resend editor by checking for node-* class names.
1849
+ */
1850
+ const EDITOR_CLASS_PATTERN = /class="[^"]*node-/;
1851
+ /**
1852
+ * Attributes to preserve on specific elements for EXTERNAL content.
1853
+ * Only functional attributes - NO style or class.
1854
+ */
1855
+ const PRESERVED_ATTRIBUTES = {
1856
+ a: [
1857
+ "href",
1858
+ "target",
1859
+ "rel"
1860
+ ],
1861
+ img: [
1862
+ "src",
1863
+ "alt",
1864
+ "width",
1865
+ "height"
1866
+ ],
1867
+ td: ["colspan", "rowspan"],
1868
+ th: [
1869
+ "colspan",
1870
+ "rowspan",
1871
+ "scope"
1872
+ ],
1873
+ table: [
1874
+ "border",
1875
+ "cellpadding",
1876
+ "cellspacing"
1877
+ ],
1878
+ "*": ["id"]
1879
+ };
1880
+ function isFromEditor(html) {
1881
+ return EDITOR_CLASS_PATTERN.test(html);
1882
+ }
1883
+ function sanitizePastedHtml(html) {
1884
+ if (isFromEditor(html)) return html;
1885
+ const doc = new DOMParser().parseFromString(html, "text/html");
1886
+ sanitizeNode(doc.body);
1887
+ return doc.body.innerHTML;
1888
+ }
1889
+ function sanitizeNode(node) {
1890
+ if (node.nodeType === Node.ELEMENT_NODE) sanitizeElement(node);
1891
+ for (const child of Array.from(node.childNodes)) sanitizeNode(child);
1892
+ }
1893
+ function sanitizeElement(el) {
1894
+ const allowedForTag = PRESERVED_ATTRIBUTES[el.tagName.toLowerCase()] || [];
1895
+ const allowedGlobal = PRESERVED_ATTRIBUTES["*"] || [];
1896
+ const allowed = new Set([...allowedForTag, ...allowedGlobal]);
1897
+ const attributesToRemove = [];
1898
+ for (const attr of Array.from(el.attributes)) {
1899
+ if (attr.name.startsWith("data-")) {
1900
+ attributesToRemove.push(attr.name);
1901
+ continue;
1902
+ }
1903
+ if (!allowed.has(attr.name)) attributesToRemove.push(attr.name);
1904
+ }
1905
+ for (const attr of attributesToRemove) el.removeAttribute(attr);
1906
+ }
1907
+
1908
+ //#endregion
1909
+ //#region src/core/create-paste-handler.ts
1910
+ function createPasteHandler({ onPaste, onUploadImage, extensions }) {
1911
+ return (view, event, slice) => {
1912
+ const text = event.clipboardData?.getData("text/plain");
1913
+ if (text && onPaste?.(text, view)) {
1914
+ event.preventDefault();
1915
+ return true;
1916
+ }
1917
+ if (event.clipboardData?.files?.[0]) {
1918
+ const file = event.clipboardData.files[0];
1919
+ if (onPaste?.(file, view)) {
1920
+ event.preventDefault();
1921
+ return true;
1922
+ }
1923
+ if (file.type.includes("image/") && onUploadImage) {
1924
+ const pos = view.state.selection.from;
1925
+ onUploadImage(file, view, pos);
1926
+ return true;
1927
+ }
1928
+ }
1929
+ /**
1930
+ * If the coming content has a single child, we can assume
1931
+ * it's a plain text and doesn't need to be parsed and
1932
+ * be introduced in a new line
1933
+ */
1934
+ if (slice.content.childCount === 1) return false;
1935
+ if (event.clipboardData?.getData?.("text/html")) {
1936
+ event.preventDefault();
1937
+ const jsonContent = (0, _tiptap_html.generateJSON)(sanitizePastedHtml(event.clipboardData.getData("text/html")), extensions);
1938
+ const node = view.state.schema.nodeFromJSON(jsonContent);
1939
+ const transaction = view.state.tr.replaceSelectionWith(node, false);
1940
+ view.dispatch(transaction);
1941
+ return true;
1942
+ }
1943
+ return false;
1944
+ };
1945
+ }
1946
+
1947
+ //#endregion
1948
+ //#region src/core/use-editor.ts
1949
+ const COLLABORATION_EXTENSION_NAMES = new Set(["liveblocksExtension", "collaboration"]);
1950
+ function hasCollaborationExtension(exts) {
1951
+ return exts.some((ext) => COLLABORATION_EXTENSION_NAMES.has(ext.name));
1952
+ }
1953
+ function useEditor({ content, extensions = [], onUpdate, onPaste, onUploadImage, onReady, editable = true, ...rest }) {
1954
+ const [contentError, setContentError] = react.useState(null);
1955
+ const isCollaborative = hasCollaborationExtension(extensions);
1956
+ const effectiveExtensions = react.useMemo(() => [
1957
+ StarterKit,
1958
+ ...isCollaborative ? [] : [_tiptap_extensions.UndoRedo],
1959
+ ...extensions
1960
+ ], [extensions, isCollaborative]);
1961
+ const editor = (0, _tiptap_react.useEditor)({
1962
+ content: isCollaborative ? void 0 : content,
1963
+ extensions: effectiveExtensions,
1964
+ editable,
1965
+ immediatelyRender: false,
1966
+ enableContentCheck: true,
1967
+ onContentError({ editor: editor$1, error, disableCollaboration }) {
1968
+ disableCollaboration();
1969
+ setContentError(error);
1970
+ console.error(error);
1971
+ editor$1.setEditable(false);
1972
+ },
1973
+ onCreate({ editor: editor$1 }) {
1974
+ onReady?.(editor$1);
1975
+ },
1976
+ onUpdate({ editor: editor$1, transaction }) {
1977
+ onUpdate?.(editor$1, transaction);
1978
+ },
1979
+ editorProps: {
1980
+ handleDOMEvents: { click: (view, event) => {
1981
+ if (!view.editable) {
1982
+ if (event.target.closest("a")) {
1983
+ event.preventDefault();
1984
+ return true;
1985
+ }
1986
+ }
1987
+ return false;
1988
+ } },
1989
+ handlePaste: createPasteHandler({
1990
+ onPaste,
1991
+ onUploadImage,
1992
+ extensions: effectiveExtensions
1993
+ }),
1994
+ handleDrop: createDropHandler({
1995
+ onPaste,
1996
+ onUploadImage
1997
+ })
1998
+ },
1999
+ ...rest
2000
+ });
2001
+ return {
2002
+ editor,
2003
+ isEditorEmpty: (0, _tiptap_react.useEditorState)({
2004
+ editor,
2005
+ selector: (context) => {
2006
+ if (!context.editor) return true;
2007
+ return isDocumentVisuallyEmpty(context.editor.state.doc);
2008
+ }
2009
+ }) ?? true,
2010
+ extensions: effectiveExtensions,
2011
+ contentError,
2012
+ isCollaborative
2013
+ };
2014
+ }
2015
+
2016
+ //#endregion
2017
+ Object.defineProperty(exports, 'AlignmentAttribute', {
2018
+ enumerable: true,
2019
+ get: function () {
2020
+ return AlignmentAttribute;
2021
+ }
2022
+ });
2023
+ Object.defineProperty(exports, 'Blockquote', {
2024
+ enumerable: true,
2025
+ get: function () {
2026
+ return Blockquote;
2027
+ }
2028
+ });
2029
+ Object.defineProperty(exports, 'Body', {
2030
+ enumerable: true,
2031
+ get: function () {
2032
+ return Body;
2033
+ }
2034
+ });
2035
+ Object.defineProperty(exports, 'Bold', {
2036
+ enumerable: true,
2037
+ get: function () {
2038
+ return Bold;
2039
+ }
2040
+ });
2041
+ Object.defineProperty(exports, 'BulletList', {
2042
+ enumerable: true,
2043
+ get: function () {
2044
+ return BulletList;
2045
+ }
2046
+ });
2047
+ Object.defineProperty(exports, 'Button', {
2048
+ enumerable: true,
2049
+ get: function () {
2050
+ return Button;
2051
+ }
2052
+ });
2053
+ Object.defineProperty(exports, 'ClassAttribute', {
2054
+ enumerable: true,
2055
+ get: function () {
2056
+ return ClassAttribute;
2057
+ }
2058
+ });
2059
+ Object.defineProperty(exports, 'Code', {
2060
+ enumerable: true,
2061
+ get: function () {
2062
+ return Code;
2063
+ }
2064
+ });
2065
+ Object.defineProperty(exports, 'CodeBlockPrism', {
2066
+ enumerable: true,
2067
+ get: function () {
2068
+ return CodeBlockPrism;
2069
+ }
2070
+ });
2071
+ Object.defineProperty(exports, 'Div', {
2072
+ enumerable: true,
2073
+ get: function () {
2074
+ return Div;
2075
+ }
2076
+ });
2077
+ Object.defineProperty(exports, 'Divider', {
2078
+ enumerable: true,
2079
+ get: function () {
2080
+ return Divider;
2081
+ }
2082
+ });
2083
+ Object.defineProperty(exports, 'GlobalContent', {
2084
+ enumerable: true,
2085
+ get: function () {
2086
+ return GlobalContent;
2087
+ }
2088
+ });
2089
+ Object.defineProperty(exports, 'HardBreak', {
2090
+ enumerable: true,
2091
+ get: function () {
2092
+ return HardBreak;
2093
+ }
2094
+ });
2095
+ Object.defineProperty(exports, 'Heading', {
2096
+ enumerable: true,
2097
+ get: function () {
2098
+ return Heading;
2099
+ }
2100
+ });
2101
+ Object.defineProperty(exports, 'Italic', {
2102
+ enumerable: true,
2103
+ get: function () {
2104
+ return Italic;
2105
+ }
2106
+ });
2107
+ Object.defineProperty(exports, 'Link', {
2108
+ enumerable: true,
2109
+ get: function () {
2110
+ return Link;
2111
+ }
2112
+ });
2113
+ Object.defineProperty(exports, 'ListItem', {
2114
+ enumerable: true,
2115
+ get: function () {
2116
+ return ListItem;
2117
+ }
2118
+ });
2119
+ Object.defineProperty(exports, 'MaxNesting', {
2120
+ enumerable: true,
2121
+ get: function () {
2122
+ return MaxNesting;
2123
+ }
2124
+ });
2125
+ Object.defineProperty(exports, 'OrderedList', {
2126
+ enumerable: true,
2127
+ get: function () {
2128
+ return OrderedList;
2129
+ }
2130
+ });
2131
+ Object.defineProperty(exports, 'Paragraph', {
2132
+ enumerable: true,
2133
+ get: function () {
2134
+ return Paragraph;
2135
+ }
2136
+ });
2137
+ Object.defineProperty(exports, 'Placeholder', {
2138
+ enumerable: true,
2139
+ get: function () {
2140
+ return Placeholder;
2141
+ }
2142
+ });
2143
+ Object.defineProperty(exports, 'PreservedStyle', {
2144
+ enumerable: true,
2145
+ get: function () {
2146
+ return PreservedStyle;
2147
+ }
2148
+ });
2149
+ Object.defineProperty(exports, 'PreviewText', {
2150
+ enumerable: true,
2151
+ get: function () {
2152
+ return PreviewText;
2153
+ }
2154
+ });
2155
+ Object.defineProperty(exports, 'Section', {
2156
+ enumerable: true,
2157
+ get: function () {
2158
+ return Section$1;
2159
+ }
2160
+ });
2161
+ Object.defineProperty(exports, 'StarterKit', {
2162
+ enumerable: true,
2163
+ get: function () {
2164
+ return StarterKit;
2165
+ }
2166
+ });
2167
+ Object.defineProperty(exports, 'Strike', {
2168
+ enumerable: true,
2169
+ get: function () {
2170
+ return Strike;
2171
+ }
2172
+ });
2173
+ Object.defineProperty(exports, 'StyleAttribute', {
2174
+ enumerable: true,
2175
+ get: function () {
2176
+ return StyleAttribute;
2177
+ }
2178
+ });
2179
+ Object.defineProperty(exports, 'Sup', {
2180
+ enumerable: true,
2181
+ get: function () {
2182
+ return Sup;
2183
+ }
2184
+ });
2185
+ Object.defineProperty(exports, 'Table', {
2186
+ enumerable: true,
2187
+ get: function () {
2188
+ return Table;
2189
+ }
2190
+ });
2191
+ Object.defineProperty(exports, 'TableCell', {
2192
+ enumerable: true,
2193
+ get: function () {
2194
+ return TableCell;
2195
+ }
2196
+ });
2197
+ Object.defineProperty(exports, 'TableHeader', {
2198
+ enumerable: true,
2199
+ get: function () {
2200
+ return TableHeader;
2201
+ }
2202
+ });
2203
+ Object.defineProperty(exports, 'TableRow', {
2204
+ enumerable: true,
2205
+ get: function () {
2206
+ return TableRow;
2207
+ }
2208
+ });
2209
+ Object.defineProperty(exports, 'Underline', {
2210
+ enumerable: true,
2211
+ get: function () {
2212
+ return Underline;
2213
+ }
2214
+ });
2215
+ Object.defineProperty(exports, 'Uppercase', {
2216
+ enumerable: true,
2217
+ get: function () {
2218
+ return Uppercase;
2219
+ }
2220
+ });
2221
+ Object.defineProperty(exports, 'composeReactEmail', {
2222
+ enumerable: true,
2223
+ get: function () {
2224
+ return composeReactEmail;
2225
+ }
2226
+ });
2227
+ Object.defineProperty(exports, 'getGlobalContent', {
2228
+ enumerable: true,
2229
+ get: function () {
2230
+ return getGlobalContent;
2231
+ }
2232
+ });
2233
+ Object.defineProperty(exports, 'isDocumentVisuallyEmpty', {
2234
+ enumerable: true,
2235
+ get: function () {
2236
+ return isDocumentVisuallyEmpty;
2237
+ }
2238
+ });
2239
+ Object.defineProperty(exports, 'processStylesForUnlink', {
2240
+ enumerable: true,
2241
+ get: function () {
2242
+ return processStylesForUnlink;
2243
+ }
2244
+ });
2245
+ Object.defineProperty(exports, 'useEditor', {
2246
+ enumerable: true,
2247
+ get: function () {
2248
+ return useEditor;
2249
+ }
2250
+ });