@react-email/editor 0.0.0-experimental.2 → 0.0.0-experimental.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js DELETED
@@ -1,1399 +0,0 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
-
23
- //#endregion
24
- let __tiptap_core = require("@tiptap/core");
25
- __tiptap_core = __toESM(__tiptap_core);
26
- let react_jsx_runtime = require("react/jsx-runtime");
27
- react_jsx_runtime = __toESM(react_jsx_runtime);
28
- let __react_email_components = require("@react-email/components");
29
- __react_email_components = __toESM(__react_email_components);
30
- let __tiptap_extension_code_block = require("@tiptap/extension-code-block");
31
- __tiptap_extension_code_block = __toESM(__tiptap_extension_code_block);
32
- let __tiptap_pm_state = require("@tiptap/pm/state");
33
- __tiptap_pm_state = __toESM(__tiptap_pm_state);
34
- let __tiptap_pm_view = require("@tiptap/pm/view");
35
- __tiptap_pm_view = __toESM(__tiptap_pm_view);
36
- let hast_util_from_html = require("hast-util-from-html");
37
- hast_util_from_html = __toESM(hast_util_from_html);
38
- let prismjs = require("prismjs");
39
- prismjs = __toESM(prismjs);
40
- let __tiptap_extension_placeholder = require("@tiptap/extension-placeholder");
41
- __tiptap_extension_placeholder = __toESM(__tiptap_extension_placeholder);
42
-
43
- //#region src/core/email-node.ts
44
- var EmailNode = class EmailNode extends __tiptap_core.Node {
45
- constructor(config) {
46
- super(config);
47
- }
48
- /**
49
- * Create a new Node instance
50
- * @param config - Node configuration object or a function that returns a configuration object
51
- */
52
- static create(config) {
53
- return new EmailNode(typeof config === "function" ? config() : config);
54
- }
55
- static from(node, renderToReactEmail) {
56
- const customNode = EmailNode.create({});
57
- Object.assign(customNode, { ...node });
58
- customNode.config = {
59
- ...node.config,
60
- renderToReactEmail
61
- };
62
- return customNode;
63
- }
64
- configure(options) {
65
- return super.configure(options);
66
- }
67
- extend(extendedConfig) {
68
- const resolvedConfig = typeof extendedConfig === "function" ? extendedConfig() : extendedConfig;
69
- return super.extend(resolvedConfig);
70
- }
71
- };
72
-
73
- //#endregion
74
- //#region src/extensions/alignment-attribute.tsx
75
- const AlignmentAttribute = __tiptap_core.Extension.create({
76
- name: "alignmentAttribute",
77
- addOptions() {
78
- return {
79
- types: [],
80
- alignments: [
81
- "left",
82
- "center",
83
- "right",
84
- "justify"
85
- ]
86
- };
87
- },
88
- addGlobalAttributes() {
89
- return [{
90
- types: this.options.types,
91
- attributes: { alignment: {
92
- parseHTML: (element) => {
93
- const explicitAlign = element.getAttribute("align") || element.getAttribute("alignment") || element.style.textAlign;
94
- if (explicitAlign && this.options.alignments.includes(explicitAlign)) return explicitAlign;
95
- return null;
96
- },
97
- renderHTML: (attributes) => {
98
- if (attributes.alignment === "left") return {};
99
- return { alignment: attributes.alignment };
100
- }
101
- } }
102
- }];
103
- },
104
- addCommands() {
105
- return { setAlignment: (alignment) => ({ commands }) => {
106
- if (!this.options.alignments.includes(alignment)) return false;
107
- return this.options.types.every((type) => commands.updateAttributes(type, { alignment }));
108
- } };
109
- },
110
- addKeyboardShortcuts() {
111
- return {
112
- Enter: () => {
113
- const { from } = this.editor.state.selection;
114
- const currentAlignment = this.editor.state.doc.nodeAt(from)?.attrs?.alignment;
115
- if (currentAlignment) requestAnimationFrame(() => {
116
- this.editor.commands.setAlignment(currentAlignment);
117
- });
118
- return false;
119
- },
120
- "Mod-Shift-l": () => this.editor.commands.setAlignment("left"),
121
- "Mod-Shift-e": () => this.editor.commands.setAlignment("center"),
122
- "Mod-Shift-r": () => this.editor.commands.setAlignment("right"),
123
- "Mod-Shift-j": () => this.editor.commands.setAlignment("justify")
124
- };
125
- }
126
- });
127
-
128
- //#endregion
129
- //#region src/utils/attribute-helpers.ts
130
- /**
131
- * Creates TipTap attribute definitions for a list of HTML attributes.
132
- * Each attribute will have the same pattern:
133
- * - default: null
134
- * - parseHTML: extracts the attribute from the element
135
- * - renderHTML: conditionally renders the attribute if it has a value
136
- *
137
- * @param attributeNames - Array of HTML attribute names to create definitions for
138
- * @returns Object with TipTap attribute definitions
139
- *
140
- * @example
141
- * const attrs = createStandardAttributes(['class', 'id', 'title']);
142
- * // Returns:
143
- * // {
144
- * // class: {
145
- * // default: null,
146
- * // parseHTML: (element) => element.getAttribute('class'),
147
- * // renderHTML: (attributes) => attributes.class ? { class: attributes.class } : {}
148
- * // },
149
- * // ...
150
- * // }
151
- */
152
- function createStandardAttributes(attributeNames) {
153
- return Object.fromEntries(attributeNames.map((attr) => [attr, {
154
- default: null,
155
- parseHTML: (element) => element.getAttribute(attr),
156
- renderHTML: (attributes) => {
157
- if (!attributes[attr]) return {};
158
- return { [attr]: attributes[attr] };
159
- }
160
- }]));
161
- }
162
- /**
163
- * Common HTML attributes used across multiple extensions.
164
- * These preserve attributes during HTML import and editing for better
165
- * fidelity when importing existing email templates.
166
- */
167
- const COMMON_HTML_ATTRIBUTES = [
168
- "id",
169
- "class",
170
- "title",
171
- "lang",
172
- "dir",
173
- "data-id"
174
- ];
175
- /**
176
- * Layout-specific HTML attributes used for positioning and sizing.
177
- */
178
- const LAYOUT_ATTRIBUTES = [
179
- "align",
180
- "width",
181
- "height"
182
- ];
183
- /**
184
- * Table-specific HTML attributes used for table layout and styling.
185
- */
186
- const TABLE_ATTRIBUTES = [
187
- "border",
188
- "cellpadding",
189
- "cellspacing"
190
- ];
191
- /**
192
- * Table cell-specific HTML attributes.
193
- */
194
- const TABLE_CELL_ATTRIBUTES = [
195
- "valign",
196
- "bgcolor",
197
- "colspan",
198
- "rowspan"
199
- ];
200
- /**
201
- * Table header cell-specific HTML attributes.
202
- * These are additional attributes that only apply to <th> elements.
203
- */
204
- const TABLE_HEADER_ATTRIBUTES = [...TABLE_CELL_ATTRIBUTES, "scope"];
205
-
206
- //#endregion
207
- //#region src/utils/styles.ts
208
- const WHITE_SPACE_REGEX = /\s+/;
209
- const inlineCssToJs = (inlineStyle, options = {}) => {
210
- const styleObject = {};
211
- if (!inlineStyle || inlineStyle === "" || typeof inlineStyle === "object") return styleObject;
212
- inlineStyle.split(";").forEach((style) => {
213
- if (style.trim()) {
214
- const [key, value] = style.split(":");
215
- const valueTrimmed = value?.trim();
216
- if (!valueTrimmed) return;
217
- const formattedKey = key.trim().replace(/-\w/g, (match) => match[1].toUpperCase());
218
- styleObject[formattedKey] = options?.removeUnit ? valueTrimmed.replace(/px|%/g, "") : valueTrimmed;
219
- }
220
- });
221
- return styleObject;
222
- };
223
- /**
224
- * Expands CSS shorthand properties (margin, padding) into their longhand equivalents.
225
- * This prevents shorthand properties from overriding specific longhand properties in email clients.
226
- *
227
- * @param styles - Style object that may contain shorthand properties
228
- * @returns New style object with shorthand properties expanded to longhand
229
- *
230
- * @example
231
- * expandShorthandProperties({ margin: '0', paddingTop: '10px' })
232
- * // Returns: { marginTop: '0', marginRight: '0', marginBottom: '0', marginLeft: '0', paddingTop: '10px' }
233
- */
234
- function expandShorthandProperties(styles) {
235
- if (!styles || typeof styles !== "object") return {};
236
- const expanded = {};
237
- for (const key in styles) {
238
- const value = styles[key];
239
- if (value === void 0 || value === null || value === "") continue;
240
- switch (key) {
241
- case "margin": {
242
- const values = parseShorthandValue(value);
243
- expanded.marginTop = values.top;
244
- expanded.marginRight = values.right;
245
- expanded.marginBottom = values.bottom;
246
- expanded.marginLeft = values.left;
247
- break;
248
- }
249
- case "padding": {
250
- const values = parseShorthandValue(value);
251
- expanded.paddingTop = values.top;
252
- expanded.paddingRight = values.right;
253
- expanded.paddingBottom = values.bottom;
254
- expanded.paddingLeft = values.left;
255
- break;
256
- }
257
- case "border": {
258
- const values = convertBorderValue(value);
259
- expanded.borderStyle = values.style;
260
- expanded.borderWidth = values.width;
261
- expanded.borderColor = values.color;
262
- break;
263
- }
264
- case "borderTopLeftRadius":
265
- case "borderTopRightRadius":
266
- case "borderBottomLeftRadius":
267
- case "borderBottomRightRadius":
268
- expanded[key] = value;
269
- if (styles.borderTopLeftRadius && styles.borderTopRightRadius && styles.borderBottomLeftRadius && styles.borderBottomRightRadius) {
270
- const values = [
271
- styles.borderTopLeftRadius,
272
- styles.borderTopRightRadius,
273
- styles.borderBottomLeftRadius,
274
- styles.borderBottomRightRadius
275
- ];
276
- if (new Set(values).size === 1) expanded.borderRadius = values[0];
277
- }
278
- break;
279
- default: expanded[key] = value;
280
- }
281
- }
282
- return expanded;
283
- }
284
- /**
285
- * Parses CSS shorthand value (1-4 values) into individual side values.
286
- * Follows CSS specification for shorthand property value parsing.
287
- *
288
- * @param value - Shorthand value string (e.g., '0', '10px 20px', '5px 10px 15px 20px')
289
- * @returns Object with top, right, bottom, left values
290
- */
291
- function parseShorthandValue(value) {
292
- const stringValue = String(value).trim();
293
- const parts = stringValue.split(WHITE_SPACE_REGEX);
294
- const len = parts.length;
295
- if (len === 1) return {
296
- top: parts[0],
297
- right: parts[0],
298
- bottom: parts[0],
299
- left: parts[0]
300
- };
301
- if (len === 2) return {
302
- top: parts[0],
303
- right: parts[1],
304
- bottom: parts[0],
305
- left: parts[1]
306
- };
307
- if (len === 3) return {
308
- top: parts[0],
309
- right: parts[1],
310
- bottom: parts[2],
311
- left: parts[1]
312
- };
313
- if (len === 4) return {
314
- top: parts[0],
315
- right: parts[1],
316
- bottom: parts[2],
317
- left: parts[3]
318
- };
319
- return {
320
- top: stringValue,
321
- right: stringValue,
322
- bottom: stringValue,
323
- left: stringValue
324
- };
325
- }
326
- function convertBorderValue(value) {
327
- const stringValue = String(value).trim();
328
- const parts = stringValue.split(WHITE_SPACE_REGEX);
329
- switch (parts.length) {
330
- case 1: return {
331
- style: "solid",
332
- width: parts[0],
333
- color: "black"
334
- };
335
- case 2: return {
336
- style: parts[1],
337
- width: parts[0],
338
- color: "black"
339
- };
340
- case 3: return {
341
- style: parts[1],
342
- width: parts[0],
343
- color: parts[2]
344
- };
345
- case 4: return {
346
- style: parts[1],
347
- width: parts[0],
348
- color: parts[2]
349
- };
350
- default: return {
351
- style: "solid",
352
- width: stringValue,
353
- color: "black"
354
- };
355
- }
356
- }
357
- /**
358
- * Resolves conflicts between reset styles and inline styles by expanding
359
- * shorthand properties (margin, padding) to longhand before merging.
360
- * This prevents shorthand properties from overriding specific longhand properties.
361
- *
362
- * @param resetStyles - Base reset styles that may contain shorthand properties
363
- * @param inlineStyles - Inline styles that should override reset styles
364
- * @returns Merged styles with inline styles taking precedence
365
- */
366
- function resolveConflictingStyles(resetStyles, inlineStyles) {
367
- const expandedResetStyles = expandShorthandProperties(resetStyles);
368
- const expandedInlineStyles = expandShorthandProperties(inlineStyles);
369
- return {
370
- ...expandedResetStyles,
371
- ...expandedInlineStyles
372
- };
373
- }
374
-
375
- //#endregion
376
- //#region src/extensions/body.tsx
377
- const Body = EmailNode.create({
378
- name: "body",
379
- group: "block",
380
- content: "block+",
381
- defining: true,
382
- isolating: true,
383
- addAttributes() {
384
- return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
385
- },
386
- parseHTML() {
387
- return [{
388
- tag: "body",
389
- getAttrs: (node) => {
390
- if (typeof node === "string") return false;
391
- const element = node;
392
- const attrs = {};
393
- Array.from(element.attributes).forEach((attr) => {
394
- attrs[attr.name] = attr.value;
395
- });
396
- return attrs;
397
- }
398
- }];
399
- },
400
- renderHTML({ HTMLAttributes }) {
401
- return [
402
- "div",
403
- (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
404
- 0
405
- ];
406
- },
407
- renderToReactEmail({ children, node, styles }) {
408
- const inlineStyles = inlineCssToJs(node.attrs?.style);
409
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
410
- className: node.attrs?.class || void 0,
411
- style: {
412
- ...styles.reset,
413
- ...inlineStyles
414
- },
415
- children
416
- });
417
- }
418
- });
419
-
420
- //#endregion
421
- //#region src/extensions/bold.ts
422
- /**
423
- * Matches bold text via `**` as input.
424
- */
425
- const starInputRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))$/;
426
- /**
427
- * Matches bold text via `**` while pasting.
428
- */
429
- const starPasteRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))/g;
430
- /**
431
- * Matches bold text via `__` as input.
432
- */
433
- const underscoreInputRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))$/;
434
- /**
435
- * Matches bold text via `__` while pasting.
436
- */
437
- const underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
438
- /**
439
- * This extension allows you to mark text as bold.
440
- * @see https://tiptap.dev/api/marks/bold
441
- */
442
- const Bold = __tiptap_core.Mark.create({
443
- name: "bold",
444
- addOptions() {
445
- return { HTMLAttributes: {} };
446
- },
447
- parseHTML() {
448
- return [
449
- { tag: "strong" },
450
- {
451
- tag: "b",
452
- getAttrs: (node) => node.style.fontWeight !== "normal" && null
453
- },
454
- {
455
- style: "font-weight=400",
456
- clearMark: (mark) => mark.type.name === this.name
457
- }
458
- ];
459
- },
460
- renderHTML({ HTMLAttributes }) {
461
- return [
462
- "strong",
463
- (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
464
- 0
465
- ];
466
- },
467
- addCommands() {
468
- return {
469
- setBold: () => ({ commands }) => {
470
- return commands.setMark(this.name);
471
- },
472
- toggleBold: () => ({ commands }) => {
473
- return commands.toggleMark(this.name);
474
- },
475
- unsetBold: () => ({ commands }) => {
476
- return commands.unsetMark(this.name);
477
- }
478
- };
479
- },
480
- addKeyboardShortcuts() {
481
- return {
482
- "Mod-b": () => this.editor.commands.toggleBold(),
483
- "Mod-B": () => this.editor.commands.toggleBold()
484
- };
485
- },
486
- addInputRules() {
487
- return [(0, __tiptap_core.markInputRule)({
488
- find: starInputRegex,
489
- type: this.type
490
- }), (0, __tiptap_core.markInputRule)({
491
- find: underscoreInputRegex,
492
- type: this.type
493
- })];
494
- },
495
- addPasteRules() {
496
- return [(0, __tiptap_core.markPasteRule)({
497
- find: starPasteRegex,
498
- type: this.type
499
- }), (0, __tiptap_core.markPasteRule)({
500
- find: underscorePasteRegex,
501
- type: this.type
502
- })];
503
- }
504
- });
505
-
506
- //#endregion
507
- //#region src/extensions/button.tsx
508
- const Button = EmailNode.create({
509
- name: "button",
510
- group: "block",
511
- content: "inline*",
512
- defining: true,
513
- draggable: true,
514
- marks: "bold",
515
- addAttributes() {
516
- return {
517
- class: { default: "button" },
518
- href: { default: "#" },
519
- alignment: { default: "left" }
520
- };
521
- },
522
- parseHTML() {
523
- return [{
524
- tag: "a[data-id=\"react-email-button\"]",
525
- getAttrs: (node) => {
526
- if (typeof node === "string") return false;
527
- const element = node;
528
- const attrs = {};
529
- Array.from(element.attributes).forEach((attr) => {
530
- attrs[attr.name] = attr.value;
531
- });
532
- return attrs;
533
- }
534
- }];
535
- },
536
- renderHTML({ HTMLAttributes }) {
537
- return [
538
- "div",
539
- (0, __tiptap_core.mergeAttributes)({ class: `align-${HTMLAttributes?.alignment}` }),
540
- [
541
- "a",
542
- (0, __tiptap_core.mergeAttributes)({
543
- class: `node-button ${HTMLAttributes?.class}`,
544
- style: HTMLAttributes?.style,
545
- "data-id": "react-email-button",
546
- "data-href": HTMLAttributes?.href
547
- }),
548
- 0
549
- ]
550
- ];
551
- },
552
- addCommands() {
553
- return {
554
- updateButton: (attributes) => ({ commands }) => {
555
- return commands.updateAttributes("button", attributes);
556
- },
557
- setButton: () => ({ commands }) => {
558
- return commands.insertContent({
559
- type: "button",
560
- content: [{
561
- type: "text",
562
- text: "Button"
563
- }]
564
- });
565
- }
566
- };
567
- },
568
- renderToReactEmail({ children, node, styles }) {
569
- const inlineStyles = inlineCssToJs(node.attrs?.style);
570
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Row, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
571
- align: node.attrs?.align || node.attrs?.alignment,
572
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Button, {
573
- className: node.attrs?.class || void 0,
574
- href: node.attrs?.href,
575
- style: {
576
- ...styles.reset,
577
- ...styles.button,
578
- ...inlineStyles
579
- },
580
- children
581
- })
582
- }) });
583
- }
584
- });
585
-
586
- //#endregion
587
- //#region src/extensions/class-attribute.tsx
588
- const ClassAttribute = __tiptap_core.Extension.create({
589
- name: "classAttribute",
590
- addOptions() {
591
- return {
592
- types: [],
593
- class: []
594
- };
595
- },
596
- addGlobalAttributes() {
597
- return [{
598
- types: this.options.types,
599
- attributes: { class: {
600
- default: "",
601
- parseHTML: (element) => element.className || "",
602
- renderHTML: (attributes) => {
603
- return attributes.class ? { class: attributes.class } : {};
604
- }
605
- } }
606
- }];
607
- },
608
- addCommands() {
609
- return {
610
- unsetClass: () => ({ commands }) => {
611
- return this.options.types.every((type) => commands.resetAttributes(type, "class"));
612
- },
613
- setClass: (classList) => ({ commands }) => {
614
- return this.options.types.every((type) => commands.updateAttributes(type, { class: classList }));
615
- }
616
- };
617
- },
618
- addKeyboardShortcuts() {
619
- return { Enter: ({ editor }) => {
620
- requestAnimationFrame(() => {
621
- editor.commands.resetAttributes("paragraph", "class");
622
- });
623
- return false;
624
- } };
625
- }
626
- });
627
-
628
- //#endregion
629
- //#region src/utils/prism-utils.ts
630
- const publicURL = "/styles/prism";
631
- function loadPrismTheme(theme) {
632
- const link = document.createElement("link");
633
- link.rel = "stylesheet";
634
- link.href = `${publicURL}/prism-${theme}.css`;
635
- link.setAttribute("data-prism-theme", "");
636
- document.head.appendChild(link);
637
- }
638
- function removePrismTheme() {
639
- const existingTheme = document.querySelectorAll("link[rel=\"stylesheet\"][data-prism-theme]");
640
- if (existingTheme.length > 0) existingTheme.forEach((cssLinkTag) => {
641
- cssLinkTag.remove();
642
- });
643
- }
644
- function hasPrismThemeLoaded(theme) {
645
- return !!document.querySelector(`link[rel="stylesheet"][data-prism-theme][href="${publicURL}/prism-${theme}.css"]`);
646
- }
647
-
648
- //#endregion
649
- //#region src/extensions/prism-plugin.ts
650
- const PRISM_LANGUAGE_LOADED_META = "prismLanguageLoaded";
651
- function parseNodes(nodes, className = []) {
652
- return nodes.flatMap((node) => {
653
- const classes = [...className, ...node.properties ? node.properties.className : []];
654
- if (node.children) return parseNodes(node.children, classes);
655
- return {
656
- text: node.value ?? "",
657
- classes
658
- };
659
- });
660
- }
661
- function getHighlightNodes(html) {
662
- return (0, hast_util_from_html.fromHtml)(html, { fragment: true }).children;
663
- }
664
- function registeredLang(aliasOrLanguage) {
665
- const allSupportLang = Object.keys(prismjs.default.languages).filter((id) => typeof prismjs.default.languages[id] === "object");
666
- return Boolean(allSupportLang.find((x) => x === aliasOrLanguage));
667
- }
668
- function getDecorations({ doc, name, defaultLanguage, defaultTheme, loadingLanguages, onLanguageLoaded }) {
669
- const decorations = [];
670
- (0, __tiptap_core.findChildren)(doc, (node) => node.type.name === name).forEach((block) => {
671
- let from = block.pos + 1;
672
- const language = block.node.attrs.language || defaultLanguage;
673
- const theme = block.node.attrs.theme || defaultTheme;
674
- let html = "";
675
- try {
676
- if (!registeredLang(language) && !loadingLanguages.has(language)) {
677
- loadingLanguages.add(language);
678
- import(`prismjs/components/prism-${language}`).then(() => {
679
- loadingLanguages.delete(language);
680
- onLanguageLoaded(language);
681
- }).catch(() => {
682
- loadingLanguages.delete(language);
683
- });
684
- }
685
- if (!hasPrismThemeLoaded(theme)) loadPrismTheme(theme);
686
- html = prismjs.default.highlight(block.node.textContent, prismjs.default.languages[language], language);
687
- } catch {
688
- html = prismjs.default.highlight(block.node.textContent, prismjs.default.languages.javascript, "js");
689
- }
690
- parseNodes(getHighlightNodes(html)).forEach((node) => {
691
- const to = from + node.text.length;
692
- if (node.classes.length) {
693
- const decoration = __tiptap_pm_view.Decoration.inline(from, to, { class: node.classes.join(" ") });
694
- decorations.push(decoration);
695
- }
696
- from = to;
697
- });
698
- });
699
- return __tiptap_pm_view.DecorationSet.create(doc, decorations);
700
- }
701
- function PrismPlugin({ name, defaultLanguage, defaultTheme }) {
702
- if (!defaultLanguage) throw Error("You must specify the defaultLanguage parameter");
703
- const loadingLanguages = /* @__PURE__ */ new Set();
704
- let pluginView = null;
705
- const onLanguageLoaded = (language) => {
706
- if (pluginView) pluginView.dispatch(pluginView.state.tr.setMeta(PRISM_LANGUAGE_LOADED_META, language));
707
- };
708
- const prismjsPlugin = new __tiptap_pm_state.Plugin({
709
- key: new __tiptap_pm_state.PluginKey("prism"),
710
- view(view) {
711
- pluginView = view;
712
- return { destroy() {
713
- pluginView = null;
714
- } };
715
- },
716
- state: {
717
- init: (_, { doc }) => {
718
- return getDecorations({
719
- doc,
720
- name,
721
- defaultLanguage,
722
- defaultTheme,
723
- loadingLanguages,
724
- onLanguageLoaded
725
- });
726
- },
727
- apply: (transaction, decorationSet, oldState, newState) => {
728
- const oldNodeName = oldState.selection.$head.parent.type.name;
729
- const newNodeName = newState.selection.$head.parent.type.name;
730
- const oldNodes = (0, __tiptap_core.findChildren)(oldState.doc, (node) => node.type.name === name);
731
- const newNodes = (0, __tiptap_core.findChildren)(newState.doc, (node) => node.type.name === name);
732
- if (transaction.getMeta(PRISM_LANGUAGE_LOADED_META) || transaction.docChanged && ([oldNodeName, newNodeName].includes(name) || newNodes.length !== oldNodes.length || transaction.steps.some((step) => {
733
- const rangeStep = step;
734
- return rangeStep.from !== void 0 && rangeStep.to !== void 0 && oldNodes.some((node) => {
735
- return node.pos >= rangeStep.from && node.pos + node.node.nodeSize <= rangeStep.to;
736
- });
737
- }))) return getDecorations({
738
- doc: transaction.doc,
739
- name,
740
- defaultLanguage,
741
- defaultTheme,
742
- loadingLanguages,
743
- onLanguageLoaded
744
- });
745
- return decorationSet.map(transaction.mapping, transaction.doc);
746
- }
747
- },
748
- props: { decorations(state) {
749
- return prismjsPlugin.getState(state);
750
- } },
751
- destroy() {
752
- pluginView = null;
753
- removePrismTheme();
754
- }
755
- });
756
- return prismjsPlugin;
757
- }
758
-
759
- //#endregion
760
- //#region src/extensions/code-block.tsx
761
- const CodeBlockPrism = EmailNode.from(__tiptap_extension_code_block.default.extend({
762
- addOptions() {
763
- return {
764
- languageClassPrefix: "language-",
765
- exitOnTripleEnter: false,
766
- exitOnArrowDown: false,
767
- enableTabIndentation: true,
768
- tabSize: 2,
769
- defaultLanguage: "javascript",
770
- defaultTheme: "default",
771
- HTMLAttributes: {}
772
- };
773
- },
774
- addAttributes() {
775
- return {
776
- ...this.parent?.(),
777
- language: {
778
- default: this.options.defaultLanguage,
779
- parseHTML: (element) => {
780
- if (!element) return null;
781
- const { languageClassPrefix } = this.options;
782
- if (!languageClassPrefix) return null;
783
- const language = [...element.firstElementChild?.classList || []].filter((className) => className.startsWith(languageClassPrefix || "")).map((className) => className.replace(languageClassPrefix, ""))[0];
784
- if (!language) return null;
785
- return language;
786
- },
787
- rendered: false
788
- },
789
- theme: {
790
- default: this.options.defaultTheme,
791
- rendered: false
792
- }
793
- };
794
- },
795
- renderHTML({ node, HTMLAttributes }) {
796
- return [
797
- "pre",
798
- (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language}` : null }, { "data-theme": node.attrs.theme }),
799
- [
800
- "code",
801
- { class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language} node-codeTag` : "node-codeTag" },
802
- 0
803
- ]
804
- ];
805
- },
806
- addProseMirrorPlugins() {
807
- return [...this.parent?.() || [], PrismPlugin({
808
- name: this.name,
809
- defaultLanguage: this.options.defaultLanguage,
810
- defaultTheme: this.options.defaultTheme
811
- })];
812
- }
813
- }), ({ node, styles }) => {
814
- const language = node.attrs?.language ? `${node.attrs.language}` : "javascript";
815
- const userTheme = __react_email_components[node.attrs?.theme];
816
- const theme = userTheme ? {
817
- ...userTheme,
818
- base: {
819
- ...userTheme.base,
820
- borderRadius: "0.125rem",
821
- padding: "0.75rem 1rem"
822
- }
823
- } : { base: {
824
- color: "#1e293b",
825
- background: "#f1f5f9",
826
- lineHeight: "1.5",
827
- fontFamily: "\"Fira Code\", \"Fira Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace",
828
- padding: "0.75rem 1rem",
829
- borderRadius: "0.125rem"
830
- } };
831
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.CodeBlock, {
832
- code: node.content?.[0]?.text ?? "",
833
- language,
834
- theme,
835
- style: {
836
- width: "auto",
837
- ...styles.codeBlock
838
- }
839
- });
840
- });
841
-
842
- //#endregion
843
- //#region src/extensions/div.tsx
844
- const Div = EmailNode.create({
845
- name: "div",
846
- group: "block",
847
- content: "block+",
848
- defining: true,
849
- isolating: true,
850
- parseHTML() {
851
- return [{
852
- tag: "div:not([data-type])",
853
- getAttrs: (node) => {
854
- if (typeof node === "string") return false;
855
- const element = node;
856
- const attrs = {};
857
- Array.from(element.attributes).forEach((attr) => {
858
- attrs[attr.name] = attr.value;
859
- });
860
- return attrs;
861
- }
862
- }];
863
- },
864
- renderHTML({ HTMLAttributes }) {
865
- return [
866
- "div",
867
- (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
868
- 0
869
- ];
870
- },
871
- addAttributes() {
872
- return { ...createStandardAttributes([...COMMON_HTML_ATTRIBUTES, ...LAYOUT_ATTRIBUTES]) };
873
- },
874
- renderToReactEmail({ children, node, styles }) {
875
- const inlineStyles = inlineCssToJs(node.attrs?.style);
876
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
877
- className: node.attrs?.class || void 0,
878
- style: {
879
- ...styles.reset,
880
- ...inlineStyles
881
- },
882
- children
883
- });
884
- }
885
- });
886
-
887
- //#endregion
888
- //#region src/extensions/max-nesting.ts
889
- const MaxNesting = __tiptap_core.Extension.create({
890
- name: "maxNesting",
891
- addOptions() {
892
- return {
893
- maxDepth: 3,
894
- nodeTypes: void 0
895
- };
896
- },
897
- addProseMirrorPlugins() {
898
- const { maxDepth, nodeTypes } = this.options;
899
- if (typeof maxDepth !== "number" || maxDepth < 1) throw new Error("maxDepth must be a positive number");
900
- return [new __tiptap_pm_state.Plugin({
901
- key: new __tiptap_pm_state.PluginKey("maxNesting"),
902
- appendTransaction(transactions, _oldState, newState) {
903
- if (!transactions.some((tr$1) => tr$1.docChanged)) return null;
904
- const rangesToLift = [];
905
- newState.doc.descendants((node, pos) => {
906
- let depth = 0;
907
- let currentPos = pos;
908
- let currentNode = node;
909
- while (currentNode && depth <= maxDepth) {
910
- if (!nodeTypes || nodeTypes.includes(currentNode.type.name)) depth++;
911
- const $pos = newState.doc.resolve(currentPos);
912
- if ($pos.depth === 0) break;
913
- currentPos = $pos.before($pos.depth);
914
- currentNode = newState.doc.nodeAt(currentPos);
915
- }
916
- if (depth > maxDepth) {
917
- const $pos = newState.doc.resolve(pos);
918
- if ($pos.depth > 0) {
919
- const range = $pos.blockRange();
920
- 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({
921
- range,
922
- target: range.start - 1
923
- });
924
- }
925
- }
926
- });
927
- if (rangesToLift.length === 0) return null;
928
- const tr = newState.tr;
929
- for (let i = rangesToLift.length - 1; i >= 0; i--) {
930
- const { range, target } = rangesToLift[i];
931
- tr.lift(range, target);
932
- }
933
- return tr;
934
- },
935
- filterTransaction(tr) {
936
- if (!tr.docChanged) return true;
937
- let wouldCreateDeepNesting = false;
938
- const newDoc = tr.doc;
939
- newDoc.descendants((node, pos) => {
940
- if (wouldCreateDeepNesting) return false;
941
- let depth = 0;
942
- let currentPos = pos;
943
- let currentNode = node;
944
- while (currentNode && depth <= maxDepth) {
945
- if (!nodeTypes || nodeTypes.includes(currentNode.type.name)) depth++;
946
- const $pos = newDoc.resolve(currentPos);
947
- if ($pos.depth === 0) break;
948
- currentPos = $pos.before($pos.depth);
949
- currentNode = newDoc.nodeAt(currentPos);
950
- }
951
- if (depth > maxDepth) {
952
- wouldCreateDeepNesting = true;
953
- return false;
954
- }
955
- });
956
- return !wouldCreateDeepNesting;
957
- }
958
- })];
959
- }
960
- });
961
-
962
- //#endregion
963
- //#region src/extensions/placeholder.ts
964
- const createPlaceholderExtension = (options) => {
965
- return __tiptap_extension_placeholder.default.configure({
966
- placeholder: ({ node }) => {
967
- if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
968
- return "Press '/' for commands";
969
- },
970
- includeChildren: true,
971
- ...options
972
- });
973
- };
974
- const placeholder = createPlaceholderExtension();
975
-
976
- //#endregion
977
- //#region src/extensions/preserved-style.ts
978
- const PreservedStyle = __tiptap_core.Mark.create({
979
- name: "preservedStyle",
980
- addAttributes() {
981
- return { style: {
982
- default: null,
983
- parseHTML: (element) => element.getAttribute("style"),
984
- renderHTML: (attributes) => {
985
- if (!attributes.style) return {};
986
- return { style: attributes.style };
987
- }
988
- } };
989
- },
990
- parseHTML() {
991
- return [{
992
- tag: "span[style]",
993
- getAttrs: (element) => {
994
- if (typeof element === "string") return false;
995
- const style = element.getAttribute("style");
996
- if (style && hasPreservableStyles(style)) return { style };
997
- return false;
998
- }
999
- }];
1000
- },
1001
- renderHTML({ HTMLAttributes }) {
1002
- return [
1003
- "span",
1004
- (0, __tiptap_core.mergeAttributes)(HTMLAttributes),
1005
- 0
1006
- ];
1007
- }
1008
- });
1009
- const LINK_INDICATOR_STYLES = [
1010
- "color",
1011
- "text-decoration",
1012
- "text-decoration-line",
1013
- "text-decoration-color",
1014
- "text-decoration-style"
1015
- ];
1016
- function parseStyleString(styleString) {
1017
- const temp = document.createElement("div");
1018
- temp.style.cssText = styleString;
1019
- return temp.style;
1020
- }
1021
- function hasBackground(style) {
1022
- const bgColor = style.backgroundColor;
1023
- const bg = style.background;
1024
- if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") return true;
1025
- if (bg && bg !== "transparent" && bg !== "none" && bg !== "rgba(0, 0, 0, 0)") return true;
1026
- return false;
1027
- }
1028
- function hasPreservableStyles(styleString) {
1029
- return processStylesForUnlink(styleString) !== null;
1030
- }
1031
- /**
1032
- * Processes styles when unlinking:
1033
- * - Has background (button-like): preserve all styles
1034
- * - No background: strip link-indicator styles (color, text-decoration), keep the rest
1035
- */
1036
- function processStylesForUnlink(styleString) {
1037
- if (!styleString) return null;
1038
- const style = parseStyleString(styleString);
1039
- if (hasBackground(style)) return styleString;
1040
- const filtered = [];
1041
- for (let i = 0; i < style.length; i++) {
1042
- const prop = style[i];
1043
- if (LINK_INDICATOR_STYLES.includes(prop)) continue;
1044
- const value = style.getPropertyValue(prop);
1045
- if (value) filtered.push(`${prop}: ${value}`);
1046
- }
1047
- return filtered.length > 0 ? filtered.join("; ") : null;
1048
- }
1049
-
1050
- //#endregion
1051
- //#region src/utils/get-text-alignment.ts
1052
- function getTextAlignment(alignment) {
1053
- switch (alignment) {
1054
- case "left": return { textAlign: "left" };
1055
- case "center": return { textAlign: "center" };
1056
- case "right": return { textAlign: "right" };
1057
- default: return {};
1058
- }
1059
- }
1060
-
1061
- //#endregion
1062
- //#region src/extensions/section.tsx
1063
- const Section = EmailNode.create({
1064
- name: "section",
1065
- group: "block",
1066
- content: "block+",
1067
- isolating: true,
1068
- defining: true,
1069
- parseHTML() {
1070
- return [{ tag: "section[data-type=\"section\"]" }];
1071
- },
1072
- renderHTML({ HTMLAttributes }) {
1073
- return [
1074
- "section",
1075
- (0, __tiptap_core.mergeAttributes)({
1076
- "data-type": "section",
1077
- class: "node-section"
1078
- }, HTMLAttributes),
1079
- 0
1080
- ];
1081
- },
1082
- addCommands() {
1083
- return { insertSection: () => ({ commands }) => {
1084
- return commands.insertContent({
1085
- type: this.name,
1086
- content: [{
1087
- type: "paragraph",
1088
- content: []
1089
- }]
1090
- });
1091
- } };
1092
- },
1093
- renderToReactEmail({ children, node, styles }) {
1094
- const inlineStyles = inlineCssToJs(node.attrs?.style);
1095
- const textAlign = node.attrs?.align || node.attrs?.alignment;
1096
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
1097
- className: node.attrs?.class || void 0,
1098
- align: textAlign,
1099
- style: {
1100
- ...styles.section,
1101
- ...inlineStyles,
1102
- ...getTextAlignment(textAlign)
1103
- },
1104
- children
1105
- });
1106
- }
1107
- });
1108
-
1109
- //#endregion
1110
- //#region src/extensions/style-attribute.tsx
1111
- const StyleAttribute = __tiptap_core.Extension.create({
1112
- name: "styleAttribute",
1113
- priority: 101,
1114
- addOptions() {
1115
- return {
1116
- types: [],
1117
- style: []
1118
- };
1119
- },
1120
- addGlobalAttributes() {
1121
- return [{
1122
- types: this.options.types,
1123
- attributes: { style: {
1124
- default: "",
1125
- parseHTML: (element) => element.getAttribute("style") || "",
1126
- renderHTML: (attributes) => {
1127
- return { style: attributes.style ?? "" };
1128
- }
1129
- } }
1130
- }];
1131
- },
1132
- addCommands() {
1133
- return {
1134
- unsetStyle: () => ({ commands }) => {
1135
- return this.options.types.every((type) => commands.resetAttributes(type, "style"));
1136
- },
1137
- setStyle: (style) => ({ commands }) => {
1138
- return this.options.types.every((type) => commands.updateAttributes(type, { style }));
1139
- }
1140
- };
1141
- },
1142
- addKeyboardShortcuts() {
1143
- return { Enter: ({ editor }) => {
1144
- const { state } = editor.view;
1145
- const { selection } = state;
1146
- const { $from } = selection;
1147
- const textBefore = $from.nodeBefore?.text || "";
1148
- if (textBefore.includes("{{") || textBefore.includes("{{{")) return false;
1149
- requestAnimationFrame(() => {
1150
- editor.commands.resetAttributes("paragraph", "style");
1151
- });
1152
- return false;
1153
- } };
1154
- }
1155
- });
1156
-
1157
- //#endregion
1158
- //#region src/extensions/sup.ts
1159
- /**
1160
- * This extension allows you to mark text as superscript.
1161
- * @see https://tiptap.dev/api/marks/superscript
1162
- */
1163
- const Sup = __tiptap_core.Mark.create({
1164
- name: "sup",
1165
- addOptions() {
1166
- return { HTMLAttributes: {} };
1167
- },
1168
- parseHTML() {
1169
- return [{ tag: "sup" }];
1170
- },
1171
- renderHTML({ HTMLAttributes }) {
1172
- return [
1173
- "sup",
1174
- (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
1175
- 0
1176
- ];
1177
- },
1178
- addCommands() {
1179
- return {
1180
- setSup: () => ({ commands }) => {
1181
- return commands.setMark(this.name);
1182
- },
1183
- toggleSup: () => ({ commands }) => {
1184
- return commands.toggleMark(this.name);
1185
- },
1186
- unsetSup: () => ({ commands }) => {
1187
- return commands.unsetMark(this.name);
1188
- }
1189
- };
1190
- }
1191
- });
1192
-
1193
- //#endregion
1194
- //#region src/extensions/table.tsx
1195
- const Table = EmailNode.create({
1196
- name: "table",
1197
- group: "block",
1198
- content: "tableRow+",
1199
- isolating: true,
1200
- tableRole: "table",
1201
- addAttributes() {
1202
- return { ...createStandardAttributes([
1203
- ...TABLE_ATTRIBUTES,
1204
- ...LAYOUT_ATTRIBUTES,
1205
- ...COMMON_HTML_ATTRIBUTES
1206
- ]) };
1207
- },
1208
- parseHTML() {
1209
- return [{
1210
- tag: "table",
1211
- getAttrs: (node) => {
1212
- if (typeof node === "string") return false;
1213
- const element = node;
1214
- const attrs = {};
1215
- Array.from(element.attributes).forEach((attr) => {
1216
- attrs[attr.name] = attr.value;
1217
- });
1218
- return attrs;
1219
- }
1220
- }];
1221
- },
1222
- renderHTML({ HTMLAttributes }) {
1223
- return [
1224
- "table",
1225
- (0, __tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
1226
- [
1227
- "tbody",
1228
- {},
1229
- 0
1230
- ]
1231
- ];
1232
- },
1233
- renderToReactEmail({ children, node, styles }) {
1234
- const inlineStyles = inlineCssToJs(node.attrs?.style);
1235
- const alignment = node.attrs?.align || node.attrs?.alignment;
1236
- const width = node.attrs?.width;
1237
- const centeringStyles = alignment === "center" ? {
1238
- marginLeft: "auto",
1239
- marginRight: "auto"
1240
- } : {};
1241
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Section, {
1242
- className: node.attrs?.class || void 0,
1243
- align: alignment,
1244
- style: resolveConflictingStyles(styles.reset, {
1245
- ...inlineStyles,
1246
- ...centeringStyles
1247
- }),
1248
- ...width !== void 0 ? { width } : {},
1249
- children
1250
- });
1251
- }
1252
- });
1253
- const TableRow = EmailNode.create({
1254
- name: "tableRow",
1255
- group: "tableRow",
1256
- content: "(tableCell | tableHeader)+",
1257
- addAttributes() {
1258
- return { ...createStandardAttributes([
1259
- ...TABLE_CELL_ATTRIBUTES,
1260
- ...LAYOUT_ATTRIBUTES,
1261
- ...COMMON_HTML_ATTRIBUTES
1262
- ]) };
1263
- },
1264
- parseHTML() {
1265
- return [{
1266
- tag: "tr",
1267
- getAttrs: (node) => {
1268
- if (typeof node === "string") return false;
1269
- const element = node;
1270
- const attrs = {};
1271
- Array.from(element.attributes).forEach((attr) => {
1272
- attrs[attr.name] = attr.value;
1273
- });
1274
- return attrs;
1275
- }
1276
- }];
1277
- },
1278
- renderHTML({ HTMLAttributes }) {
1279
- return [
1280
- "tr",
1281
- HTMLAttributes,
1282
- 0
1283
- ];
1284
- },
1285
- renderToReactEmail({ children, node, styles }) {
1286
- const inlineStyles = inlineCssToJs(node.attrs?.style);
1287
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
1288
- className: node.attrs?.class || void 0,
1289
- style: {
1290
- ...styles.reset,
1291
- ...inlineStyles
1292
- },
1293
- children
1294
- });
1295
- }
1296
- });
1297
- const TableCell = EmailNode.create({
1298
- name: "tableCell",
1299
- group: "tableCell",
1300
- content: "block+",
1301
- isolating: true,
1302
- addAttributes() {
1303
- return { ...createStandardAttributes([
1304
- ...TABLE_CELL_ATTRIBUTES,
1305
- ...LAYOUT_ATTRIBUTES,
1306
- ...COMMON_HTML_ATTRIBUTES
1307
- ]) };
1308
- },
1309
- parseHTML() {
1310
- return [{
1311
- tag: "td",
1312
- getAttrs: (node) => {
1313
- if (typeof node === "string") return false;
1314
- const element = node;
1315
- const attrs = {};
1316
- Array.from(element.attributes).forEach((attr) => {
1317
- attrs[attr.name] = attr.value;
1318
- });
1319
- return attrs;
1320
- }
1321
- }];
1322
- },
1323
- renderHTML({ HTMLAttributes }) {
1324
- return [
1325
- "td",
1326
- HTMLAttributes,
1327
- 0
1328
- ];
1329
- },
1330
- renderToReactEmail({ children, node, styles }) {
1331
- const inlineStyles = inlineCssToJs(node.attrs?.style);
1332
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__react_email_components.Column, {
1333
- className: node.attrs?.class || void 0,
1334
- align: node.attrs?.align || node.attrs?.alignment,
1335
- style: {
1336
- ...styles.reset,
1337
- ...inlineStyles
1338
- },
1339
- children
1340
- });
1341
- }
1342
- });
1343
- const TableHeader = __tiptap_core.Node.create({
1344
- name: "tableHeader",
1345
- group: "tableCell",
1346
- content: "block+",
1347
- isolating: true,
1348
- addAttributes() {
1349
- return { ...createStandardAttributes([
1350
- ...TABLE_HEADER_ATTRIBUTES,
1351
- ...TABLE_CELL_ATTRIBUTES,
1352
- ...LAYOUT_ATTRIBUTES,
1353
- ...COMMON_HTML_ATTRIBUTES
1354
- ]) };
1355
- },
1356
- parseHTML() {
1357
- return [{
1358
- tag: "th",
1359
- getAttrs: (node) => {
1360
- if (typeof node === "string") return false;
1361
- const element = node;
1362
- const attrs = {};
1363
- Array.from(element.attributes).forEach((attr) => {
1364
- attrs[attr.name] = attr.value;
1365
- });
1366
- return attrs;
1367
- }
1368
- }];
1369
- },
1370
- renderHTML({ HTMLAttributes }) {
1371
- return [
1372
- "th",
1373
- HTMLAttributes,
1374
- 0
1375
- ];
1376
- }
1377
- });
1378
-
1379
- //#endregion
1380
- exports.AlignmentAttribute = AlignmentAttribute;
1381
- exports.Body = Body;
1382
- exports.Bold = Bold;
1383
- exports.Button = Button;
1384
- exports.ClassAttribute = ClassAttribute;
1385
- exports.CodeBlockPrism = CodeBlockPrism;
1386
- exports.Div = Div;
1387
- exports.EmailNode = EmailNode;
1388
- exports.MaxNesting = MaxNesting;
1389
- exports.PreservedStyle = PreservedStyle;
1390
- exports.Section = Section;
1391
- exports.StyleAttribute = StyleAttribute;
1392
- exports.Sup = Sup;
1393
- exports.Table = Table;
1394
- exports.TableCell = TableCell;
1395
- exports.TableHeader = TableHeader;
1396
- exports.TableRow = TableRow;
1397
- exports.createPlaceholderExtension = createPlaceholderExtension;
1398
- exports.placeholder = placeholder;
1399
- exports.processStylesForUnlink = processStylesForUnlink;