payload-plugin-newsletter 0.16.10 → 0.17.1
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/CHANGELOG.md +36 -0
- package/dist/collections.cjs +73 -31
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +73 -31
- package/dist/collections.js.map +1 -1
- package/dist/components.cjs +295 -275
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +305 -285
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +118 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +118 -33
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +7 -0
- package/dist/types.d.ts +7 -0
- package/dist/utils.cjs +64 -28
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +64 -28
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/components.cjs
CHANGED
|
@@ -968,62 +968,73 @@ async function convertToEmailSafeHtml(editorState, options) {
|
|
|
968
968
|
if (!editorState) {
|
|
969
969
|
return "";
|
|
970
970
|
}
|
|
971
|
-
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl);
|
|
971
|
+
const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter);
|
|
972
972
|
const sanitizedHtml = import_isomorphic_dompurify.default.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
|
|
973
973
|
if (options?.wrapInTemplate) {
|
|
974
974
|
return wrapInEmailTemplate(sanitizedHtml, options.preheader);
|
|
975
975
|
}
|
|
976
976
|
return sanitizedHtml;
|
|
977
977
|
}
|
|
978
|
-
async function lexicalToEmailHtml(editorState, mediaUrl) {
|
|
978
|
+
async function lexicalToEmailHtml(editorState, mediaUrl, customBlockConverter) {
|
|
979
979
|
const { root } = editorState;
|
|
980
980
|
if (!root || !root.children) {
|
|
981
981
|
return "";
|
|
982
982
|
}
|
|
983
|
-
const
|
|
984
|
-
|
|
983
|
+
const htmlParts = await Promise.all(
|
|
984
|
+
root.children.map((node) => convertNode(node, mediaUrl, customBlockConverter))
|
|
985
|
+
);
|
|
986
|
+
return htmlParts.join("");
|
|
985
987
|
}
|
|
986
|
-
function convertNode(node, mediaUrl) {
|
|
988
|
+
async function convertNode(node, mediaUrl, customBlockConverter) {
|
|
987
989
|
switch (node.type) {
|
|
988
990
|
case "paragraph":
|
|
989
|
-
return convertParagraph(node, mediaUrl);
|
|
991
|
+
return convertParagraph(node, mediaUrl, customBlockConverter);
|
|
990
992
|
case "heading":
|
|
991
|
-
return convertHeading(node, mediaUrl);
|
|
993
|
+
return convertHeading(node, mediaUrl, customBlockConverter);
|
|
992
994
|
case "list":
|
|
993
|
-
return convertList(node, mediaUrl);
|
|
995
|
+
return convertList(node, mediaUrl, customBlockConverter);
|
|
994
996
|
case "listitem":
|
|
995
|
-
return convertListItem(node, mediaUrl);
|
|
997
|
+
return convertListItem(node, mediaUrl, customBlockConverter);
|
|
996
998
|
case "blockquote":
|
|
997
|
-
return convertBlockquote(node, mediaUrl);
|
|
999
|
+
return convertBlockquote(node, mediaUrl, customBlockConverter);
|
|
998
1000
|
case "text":
|
|
999
1001
|
return convertText(node);
|
|
1000
1002
|
case "link":
|
|
1001
|
-
return convertLink(node, mediaUrl);
|
|
1003
|
+
return convertLink(node, mediaUrl, customBlockConverter);
|
|
1002
1004
|
case "linebreak":
|
|
1003
1005
|
return "<br>";
|
|
1004
1006
|
case "upload":
|
|
1005
1007
|
return convertUpload(node, mediaUrl);
|
|
1006
1008
|
case "block":
|
|
1007
|
-
return convertBlock(node, mediaUrl);
|
|
1009
|
+
return await convertBlock(node, mediaUrl, customBlockConverter);
|
|
1008
1010
|
default:
|
|
1009
1011
|
if (node.children) {
|
|
1010
|
-
|
|
1012
|
+
const childParts = await Promise.all(
|
|
1013
|
+
node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1014
|
+
);
|
|
1015
|
+
return childParts.join("");
|
|
1011
1016
|
}
|
|
1012
1017
|
return "";
|
|
1013
1018
|
}
|
|
1014
1019
|
}
|
|
1015
|
-
function convertParagraph(node, mediaUrl) {
|
|
1020
|
+
async function convertParagraph(node, mediaUrl, customBlockConverter) {
|
|
1016
1021
|
const align = getAlignment(node.format);
|
|
1017
|
-
const
|
|
1022
|
+
const childParts = await Promise.all(
|
|
1023
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1024
|
+
);
|
|
1025
|
+
const children = childParts.join("");
|
|
1018
1026
|
if (!children.trim()) {
|
|
1019
1027
|
return '<p style="margin: 0 0 16px 0; min-height: 1em;"> </p>';
|
|
1020
1028
|
}
|
|
1021
1029
|
return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
|
|
1022
1030
|
}
|
|
1023
|
-
function convertHeading(node, mediaUrl) {
|
|
1031
|
+
async function convertHeading(node, mediaUrl, customBlockConverter) {
|
|
1024
1032
|
const tag = node.tag || "h1";
|
|
1025
1033
|
const align = getAlignment(node.format);
|
|
1026
|
-
const
|
|
1034
|
+
const childParts = await Promise.all(
|
|
1035
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1036
|
+
);
|
|
1037
|
+
const children = childParts.join("");
|
|
1027
1038
|
const styles = {
|
|
1028
1039
|
h1: "font-size: 32px; font-weight: 700; margin: 0 0 24px 0; line-height: 1.2;",
|
|
1029
1040
|
h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
|
|
@@ -1032,18 +1043,27 @@ function convertHeading(node, mediaUrl) {
|
|
|
1032
1043
|
const style = `${styles[tag] || styles.h3} text-align: ${align};`;
|
|
1033
1044
|
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
1034
1045
|
}
|
|
1035
|
-
function convertList(node, mediaUrl) {
|
|
1046
|
+
async function convertList(node, mediaUrl, customBlockConverter) {
|
|
1036
1047
|
const tag = node.listType === "number" ? "ol" : "ul";
|
|
1037
|
-
const
|
|
1048
|
+
const childParts = await Promise.all(
|
|
1049
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1050
|
+
);
|
|
1051
|
+
const children = childParts.join("");
|
|
1038
1052
|
const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
|
|
1039
1053
|
return `<${tag} style="${style}">${children}</${tag}>`;
|
|
1040
1054
|
}
|
|
1041
|
-
function convertListItem(node, mediaUrl) {
|
|
1042
|
-
const
|
|
1055
|
+
async function convertListItem(node, mediaUrl, customBlockConverter) {
|
|
1056
|
+
const childParts = await Promise.all(
|
|
1057
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1058
|
+
);
|
|
1059
|
+
const children = childParts.join("");
|
|
1043
1060
|
return `<li style="margin: 0 0 8px 0;">${children}</li>`;
|
|
1044
1061
|
}
|
|
1045
|
-
function convertBlockquote(node, mediaUrl) {
|
|
1046
|
-
const
|
|
1062
|
+
async function convertBlockquote(node, mediaUrl, customBlockConverter) {
|
|
1063
|
+
const childParts = await Promise.all(
|
|
1064
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1065
|
+
);
|
|
1066
|
+
const children = childParts.join("");
|
|
1047
1067
|
const style = "margin: 0 0 16px 0; padding-left: 16px; border-left: 4px solid #e5e7eb; color: #6b7280;";
|
|
1048
1068
|
return `<blockquote style="${style}">${children}</blockquote>`;
|
|
1049
1069
|
}
|
|
@@ -1063,8 +1083,11 @@ function convertText(node) {
|
|
|
1063
1083
|
}
|
|
1064
1084
|
return text;
|
|
1065
1085
|
}
|
|
1066
|
-
function convertLink(node, mediaUrl) {
|
|
1067
|
-
const
|
|
1086
|
+
async function convertLink(node, mediaUrl, customBlockConverter) {
|
|
1087
|
+
const childParts = await Promise.all(
|
|
1088
|
+
(node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1089
|
+
);
|
|
1090
|
+
const children = childParts.join("");
|
|
1068
1091
|
const url = node.fields?.url || "#";
|
|
1069
1092
|
const newTab = node.fields?.newTab ?? false;
|
|
1070
1093
|
const targetAttr = newTab ? ' target="_blank"' : "";
|
|
@@ -1095,8 +1118,18 @@ function convertUpload(node, mediaUrl) {
|
|
|
1095
1118
|
}
|
|
1096
1119
|
return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
|
|
1097
1120
|
}
|
|
1098
|
-
function convertBlock(node, mediaUrl) {
|
|
1099
|
-
const blockType = node.fields?.blockName;
|
|
1121
|
+
async function convertBlock(node, mediaUrl, customBlockConverter) {
|
|
1122
|
+
const blockType = node.fields?.blockName || node.blockName;
|
|
1123
|
+
if (customBlockConverter) {
|
|
1124
|
+
try {
|
|
1125
|
+
const customHtml = await customBlockConverter(node, mediaUrl);
|
|
1126
|
+
if (customHtml) {
|
|
1127
|
+
return customHtml;
|
|
1128
|
+
}
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
console.error(`Custom block converter error for ${blockType}:`, error);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1100
1133
|
switch (blockType) {
|
|
1101
1134
|
case "button":
|
|
1102
1135
|
return convertButtonBlock(node.fields);
|
|
@@ -1104,7 +1137,10 @@ function convertBlock(node, mediaUrl) {
|
|
|
1104
1137
|
return convertDividerBlock(node.fields);
|
|
1105
1138
|
default:
|
|
1106
1139
|
if (node.children) {
|
|
1107
|
-
|
|
1140
|
+
const childParts = await Promise.all(
|
|
1141
|
+
node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
|
|
1142
|
+
);
|
|
1143
|
+
return childParts.join("");
|
|
1108
1144
|
}
|
|
1109
1145
|
return "";
|
|
1110
1146
|
}
|
|
@@ -1738,208 +1774,11 @@ var BroadcastEditor = (props) => {
|
|
|
1738
1774
|
};
|
|
1739
1775
|
|
|
1740
1776
|
// src/components/Broadcasts/BroadcastInlinePreview.tsx
|
|
1741
|
-
var import_react9 = require("react");
|
|
1742
|
-
var import_ui3 = require("@payloadcms/ui");
|
|
1743
|
-
|
|
1744
|
-
// src/utils/contentTransformer.ts
|
|
1745
|
-
async function transformContentForPreview(lexicalState, options = {}) {
|
|
1746
|
-
const html = await convertToEmailSafeHtml(lexicalState, {
|
|
1747
|
-
mediaUrl: options.mediaUrl
|
|
1748
|
-
});
|
|
1749
|
-
const processedHtml = processCustomBlocks(html);
|
|
1750
|
-
return processedHtml;
|
|
1751
|
-
}
|
|
1752
|
-
function processCustomBlocks(html) {
|
|
1753
|
-
return html;
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
// src/email-templates/DefaultBroadcastTemplate.tsx
|
|
1757
|
-
var import_components2 = require("@react-email/components");
|
|
1758
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1759
|
-
var DefaultBroadcastTemplate = ({
|
|
1760
|
-
subject,
|
|
1761
|
-
preheader,
|
|
1762
|
-
content
|
|
1763
|
-
}) => {
|
|
1764
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_components2.Html, { children: [
|
|
1765
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Head, {}),
|
|
1766
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Preview, { children: preheader || subject }),
|
|
1767
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Body, { style: main, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_components2.Container, { style: container, children: [
|
|
1768
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Section, { style: contentSection, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { dangerouslySetInnerHTML: { __html: content } }) }),
|
|
1769
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Hr, { style: divider }),
|
|
1770
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_components2.Section, { style: footer, children: [
|
|
1771
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Text, { style: footerText, children: "You're receiving this email because you subscribed to our newsletter." }),
|
|
1772
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Text, { style: footerText, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components2.Link, { href: "{{unsubscribe_url}}", style: footerLink, children: "Unsubscribe" }) })
|
|
1773
|
-
] })
|
|
1774
|
-
] }) })
|
|
1775
|
-
] });
|
|
1776
|
-
};
|
|
1777
|
-
var main = {
|
|
1778
|
-
backgroundColor: "#ffffff",
|
|
1779
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
|
|
1780
|
-
};
|
|
1781
|
-
var container = {
|
|
1782
|
-
margin: "0 auto",
|
|
1783
|
-
padding: "40px 20px",
|
|
1784
|
-
maxWidth: "600px"
|
|
1785
|
-
};
|
|
1786
|
-
var contentSection = {
|
|
1787
|
-
fontSize: "16px",
|
|
1788
|
-
lineHeight: "1.6",
|
|
1789
|
-
color: "#374151"
|
|
1790
|
-
};
|
|
1791
|
-
var divider = {
|
|
1792
|
-
borderColor: "#e5e7eb",
|
|
1793
|
-
margin: "40px 0 20px"
|
|
1794
|
-
};
|
|
1795
|
-
var footer = {
|
|
1796
|
-
textAlign: "center"
|
|
1797
|
-
};
|
|
1798
|
-
var footerText = {
|
|
1799
|
-
fontSize: "14px",
|
|
1800
|
-
lineHeight: "1.5",
|
|
1801
|
-
color: "#6b7280",
|
|
1802
|
-
margin: "0 0 10px"
|
|
1803
|
-
};
|
|
1804
|
-
var footerLink = {
|
|
1805
|
-
color: "#6b7280",
|
|
1806
|
-
textDecoration: "underline"
|
|
1807
|
-
};
|
|
1808
|
-
|
|
1809
|
-
// src/utils/templateLoader.ts
|
|
1810
|
-
var TemplateLoader = class {
|
|
1811
|
-
constructor() {
|
|
1812
|
-
this.loadAttempted = false;
|
|
1813
|
-
this.defaultTemplate = DefaultBroadcastTemplate;
|
|
1814
|
-
}
|
|
1815
|
-
async loadTemplate() {
|
|
1816
|
-
if (!this.loadAttempted) {
|
|
1817
|
-
this.loadAttempted = true;
|
|
1818
|
-
await this.attemptCustomTemplateLoad();
|
|
1819
|
-
}
|
|
1820
|
-
return this.customTemplate || this.defaultTemplate;
|
|
1821
|
-
}
|
|
1822
|
-
async attemptCustomTemplateLoad() {
|
|
1823
|
-
try {
|
|
1824
|
-
const customTemplatePath = `${process.cwd()}/email-templates/broadcast-template`;
|
|
1825
|
-
const module2 = await import(
|
|
1826
|
-
/* @vite-ignore */
|
|
1827
|
-
/* webpackIgnore: true */
|
|
1828
|
-
customTemplatePath
|
|
1829
|
-
).catch(() => null);
|
|
1830
|
-
if (module2) {
|
|
1831
|
-
this.customTemplate = module2.default || module2.BroadcastTemplate;
|
|
1832
|
-
}
|
|
1833
|
-
} catch {
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
// Reset for testing
|
|
1837
|
-
reset() {
|
|
1838
|
-
this.customTemplate = void 0;
|
|
1839
|
-
this.loadAttempted = false;
|
|
1840
|
-
}
|
|
1841
|
-
};
|
|
1842
|
-
var templateLoader = new TemplateLoader();
|
|
1843
|
-
async function loadTemplate() {
|
|
1844
|
-
return templateLoader.loadTemplate();
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
// src/components/Broadcasts/EmailRenderer.tsx
|
|
1848
1777
|
var import_react8 = require("react");
|
|
1849
|
-
var
|
|
1850
|
-
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1851
|
-
var EmailRenderer = ({
|
|
1852
|
-
template,
|
|
1853
|
-
data,
|
|
1854
|
-
device = "desktop",
|
|
1855
|
-
onRender
|
|
1856
|
-
}) => {
|
|
1857
|
-
const [renderedHtml, setRenderedHtml] = (0, import_react8.useState)("");
|
|
1858
|
-
const [error, setError] = (0, import_react8.useState)(null);
|
|
1859
|
-
const iframeRef = (0, import_react8.useRef)(null);
|
|
1860
|
-
const renderEmail = (0, import_react8.useCallback)(async () => {
|
|
1861
|
-
try {
|
|
1862
|
-
const TemplateComponent = template;
|
|
1863
|
-
const element = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TemplateComponent, { ...data });
|
|
1864
|
-
const html = await (0, import_render.render)(element, {
|
|
1865
|
-
pretty: true
|
|
1866
|
-
});
|
|
1867
|
-
setRenderedHtml(html);
|
|
1868
|
-
onRender?.(html);
|
|
1869
|
-
setError(null);
|
|
1870
|
-
} catch (err) {
|
|
1871
|
-
setError(err);
|
|
1872
|
-
console.error("Failed to render email template:", err);
|
|
1873
|
-
}
|
|
1874
|
-
}, [template, data, onRender]);
|
|
1875
|
-
(0, import_react8.useEffect)(() => {
|
|
1876
|
-
renderEmail();
|
|
1877
|
-
}, [renderEmail]);
|
|
1878
|
-
(0, import_react8.useEffect)(() => {
|
|
1879
|
-
if (iframeRef.current && renderedHtml) {
|
|
1880
|
-
const iframe = iframeRef.current;
|
|
1881
|
-
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
1882
|
-
if (doc) {
|
|
1883
|
-
doc.open();
|
|
1884
|
-
doc.write(renderedHtml);
|
|
1885
|
-
doc.close();
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
}, [renderedHtml]);
|
|
1889
|
-
const containerStyle = {
|
|
1890
|
-
width: "100%",
|
|
1891
|
-
height: "100%",
|
|
1892
|
-
display: "flex",
|
|
1893
|
-
alignItems: "flex-start",
|
|
1894
|
-
justifyContent: "center",
|
|
1895
|
-
overflow: "auto",
|
|
1896
|
-
padding: "2rem",
|
|
1897
|
-
boxSizing: "border-box"
|
|
1898
|
-
};
|
|
1899
|
-
const iframeStyle = {
|
|
1900
|
-
width: device === "mobile" ? "375px" : "600px",
|
|
1901
|
-
height: "100%",
|
|
1902
|
-
minHeight: "600px",
|
|
1903
|
-
background: "white",
|
|
1904
|
-
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
1905
|
-
borderRadius: device === "mobile" ? "20px" : "8px",
|
|
1906
|
-
border: "none",
|
|
1907
|
-
display: "block"
|
|
1908
|
-
};
|
|
1909
|
-
const errorStyle = {
|
|
1910
|
-
background: "white",
|
|
1911
|
-
border: "1px solid #ef4444",
|
|
1912
|
-
borderRadius: "4px",
|
|
1913
|
-
padding: "2rem",
|
|
1914
|
-
maxWidth: "500px"
|
|
1915
|
-
};
|
|
1916
|
-
if (error) {
|
|
1917
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: errorStyle, children: [
|
|
1918
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { style: { color: "#ef4444", margin: "0 0 1rem" }, children: "Template Render Error" }),
|
|
1919
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { style: {
|
|
1920
|
-
background: "#f9fafb",
|
|
1921
|
-
padding: "1rem",
|
|
1922
|
-
borderRadius: "4px",
|
|
1923
|
-
overflowX: "auto",
|
|
1924
|
-
fontSize: "12px",
|
|
1925
|
-
color: "#374151",
|
|
1926
|
-
margin: 0
|
|
1927
|
-
}, children: error.message })
|
|
1928
|
-
] });
|
|
1929
|
-
}
|
|
1930
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1931
|
-
"iframe",
|
|
1932
|
-
{
|
|
1933
|
-
ref: iframeRef,
|
|
1934
|
-
style: iframeStyle,
|
|
1935
|
-
sandbox: "allow-same-origin",
|
|
1936
|
-
title: "Email Preview"
|
|
1937
|
-
}
|
|
1938
|
-
) });
|
|
1939
|
-
};
|
|
1778
|
+
var import_ui3 = require("@payloadcms/ui");
|
|
1940
1779
|
|
|
1941
1780
|
// src/components/Broadcasts/PreviewControls.tsx
|
|
1942
|
-
var
|
|
1781
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1943
1782
|
var PreviewControls = ({
|
|
1944
1783
|
onUpdate,
|
|
1945
1784
|
device,
|
|
@@ -1981,8 +1820,8 @@ var PreviewControls = ({
|
|
|
1981
1820
|
cursor: "pointer",
|
|
1982
1821
|
fontSize: "14px"
|
|
1983
1822
|
});
|
|
1984
|
-
return /* @__PURE__ */ (0,
|
|
1985
|
-
/* @__PURE__ */ (0,
|
|
1823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: controlsStyle, children: [
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1986
1825
|
"button",
|
|
1987
1826
|
{
|
|
1988
1827
|
style: updateButtonStyle,
|
|
@@ -1991,33 +1830,33 @@ var PreviewControls = ({
|
|
|
1991
1830
|
children: isLoading ? "Updating..." : "Update Preview"
|
|
1992
1831
|
}
|
|
1993
1832
|
),
|
|
1994
|
-
/* @__PURE__ */ (0,
|
|
1995
|
-
/* @__PURE__ */ (0,
|
|
1833
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: deviceSelectorStyle, children: [
|
|
1834
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1996
1835
|
"button",
|
|
1997
1836
|
{
|
|
1998
1837
|
style: deviceButtonStyle(device === "desktop"),
|
|
1999
1838
|
onClick: () => onDeviceChange("desktop"),
|
|
2000
1839
|
"aria-label": "Desktop view",
|
|
2001
1840
|
children: [
|
|
2002
|
-
/* @__PURE__ */ (0,
|
|
2003
|
-
/* @__PURE__ */ (0,
|
|
2004
|
-
/* @__PURE__ */ (0,
|
|
2005
|
-
/* @__PURE__ */ (0,
|
|
1841
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1842
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
|
|
1843
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
|
|
1844
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
|
|
2006
1845
|
] }),
|
|
2007
1846
|
"Desktop"
|
|
2008
1847
|
]
|
|
2009
1848
|
}
|
|
2010
1849
|
),
|
|
2011
|
-
/* @__PURE__ */ (0,
|
|
1850
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2012
1851
|
"button",
|
|
2013
1852
|
{
|
|
2014
1853
|
style: deviceButtonStyle(device === "mobile"),
|
|
2015
1854
|
onClick: () => onDeviceChange("mobile"),
|
|
2016
1855
|
"aria-label": "Mobile view",
|
|
2017
1856
|
children: [
|
|
2018
|
-
/* @__PURE__ */ (0,
|
|
2019
|
-
/* @__PURE__ */ (0,
|
|
2020
|
-
/* @__PURE__ */ (0,
|
|
1857
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: "5", y: "2", width: "14", height: "20", rx: "2", ry: "2" }),
|
|
1859
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "12", y1: "18", x2: "12", y2: "18" })
|
|
2021
1860
|
] }),
|
|
2022
1861
|
"Mobile"
|
|
2023
1862
|
]
|
|
@@ -2028,19 +1867,19 @@ var PreviewControls = ({
|
|
|
2028
1867
|
};
|
|
2029
1868
|
|
|
2030
1869
|
// src/components/Broadcasts/BroadcastInlinePreview.tsx
|
|
2031
|
-
var
|
|
1870
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2032
1871
|
var BroadcastInlinePreview = () => {
|
|
2033
|
-
const [device, setDevice] = (0,
|
|
2034
|
-
const [isLoading, setIsLoading] = (0,
|
|
2035
|
-
const [showPreview, setShowPreview] = (0,
|
|
2036
|
-
const [
|
|
2037
|
-
const [error, setError] = (0,
|
|
1872
|
+
const [device, setDevice] = (0, import_react8.useState)("desktop");
|
|
1873
|
+
const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
|
|
1874
|
+
const [showPreview, setShowPreview] = (0, import_react8.useState)(false);
|
|
1875
|
+
const [previewHtml, setPreviewHtml] = (0, import_react8.useState)(null);
|
|
1876
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
2038
1877
|
const fields = (0, import_ui3.useFormFields)(([fields2]) => ({
|
|
2039
1878
|
subject: fields2["subject"]?.value,
|
|
2040
1879
|
preheader: fields2["contentSection.preheader"]?.value,
|
|
2041
1880
|
content: fields2["contentSection.content"]?.value
|
|
2042
1881
|
}));
|
|
2043
|
-
const updatePreview = (0,
|
|
1882
|
+
const updatePreview = (0, import_react8.useCallback)(async () => {
|
|
2044
1883
|
if (!fields.content) {
|
|
2045
1884
|
setError(new Error("Please add some content before previewing"));
|
|
2046
1885
|
return;
|
|
@@ -2048,18 +1887,22 @@ var BroadcastInlinePreview = () => {
|
|
|
2048
1887
|
setIsLoading(true);
|
|
2049
1888
|
setError(null);
|
|
2050
1889
|
try {
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
}
|
|
1890
|
+
const response = await fetch("/api/broadcasts/preview", {
|
|
1891
|
+
method: "POST",
|
|
1892
|
+
headers: {
|
|
1893
|
+
"Content-Type": "application/json"
|
|
1894
|
+
},
|
|
1895
|
+
body: JSON.stringify({
|
|
1896
|
+
content: fields.content,
|
|
1897
|
+
preheader: fields.preheader,
|
|
1898
|
+
subject: fields.subject
|
|
1899
|
+
})
|
|
2062
1900
|
});
|
|
1901
|
+
const data = await response.json();
|
|
1902
|
+
if (!response.ok || !data.success) {
|
|
1903
|
+
throw new Error(data.error || "Failed to generate preview");
|
|
1904
|
+
}
|
|
1905
|
+
setPreviewHtml(data.preview.html);
|
|
2063
1906
|
setShowPreview(true);
|
|
2064
1907
|
} catch (err) {
|
|
2065
1908
|
setError(err);
|
|
@@ -2111,10 +1954,10 @@ var BroadcastInlinePreview = () => {
|
|
|
2111
1954
|
fontSize: "14px",
|
|
2112
1955
|
fontWeight: 500
|
|
2113
1956
|
};
|
|
2114
|
-
return /* @__PURE__ */ (0,
|
|
2115
|
-
/* @__PURE__ */ (0,
|
|
2116
|
-
/* @__PURE__ */ (0,
|
|
2117
|
-
/* @__PURE__ */ (0,
|
|
1957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: containerStyle, children: [
|
|
1958
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: headerStyle, children: [
|
|
1959
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { style: titleStyle, children: "Email Preview" }),
|
|
1960
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2118
1961
|
"button",
|
|
2119
1962
|
{
|
|
2120
1963
|
onClick: () => showPreview ? setShowPreview(false) : updatePreview(),
|
|
@@ -2124,9 +1967,9 @@ var BroadcastInlinePreview = () => {
|
|
|
2124
1967
|
}
|
|
2125
1968
|
)
|
|
2126
1969
|
] }),
|
|
2127
|
-
showPreview && /* @__PURE__ */ (0,
|
|
2128
|
-
/* @__PURE__ */ (0,
|
|
2129
|
-
/* @__PURE__ */ (0,
|
|
1970
|
+
showPreview && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: previewContainerStyle, children: error ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: errorStyle, children: [
|
|
1971
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: { color: "#ef4444", margin: "0 0 1rem" }, children: error.message }),
|
|
1972
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2130
1973
|
"button",
|
|
2131
1974
|
{
|
|
2132
1975
|
onClick: updatePreview,
|
|
@@ -2141,8 +1984,8 @@ var BroadcastInlinePreview = () => {
|
|
|
2141
1984
|
children: "Retry"
|
|
2142
1985
|
}
|
|
2143
1986
|
)
|
|
2144
|
-
] }) :
|
|
2145
|
-
/* @__PURE__ */ (0,
|
|
1987
|
+
] }) : previewHtml ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2146
1989
|
PreviewControls,
|
|
2147
1990
|
{
|
|
2148
1991
|
onUpdate: updatePreview,
|
|
@@ -2151,12 +1994,42 @@ var BroadcastInlinePreview = () => {
|
|
|
2151
1994
|
isLoading
|
|
2152
1995
|
}
|
|
2153
1996
|
),
|
|
2154
|
-
/* @__PURE__ */ (0,
|
|
2155
|
-
|
|
1997
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1998
|
+
"div",
|
|
2156
1999
|
{
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2000
|
+
style: {
|
|
2001
|
+
flex: 1,
|
|
2002
|
+
padding: device === "mobile" ? "1rem" : "2rem",
|
|
2003
|
+
display: "flex",
|
|
2004
|
+
justifyContent: "center",
|
|
2005
|
+
overflow: "auto"
|
|
2006
|
+
},
|
|
2007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2008
|
+
"div",
|
|
2009
|
+
{
|
|
2010
|
+
style: {
|
|
2011
|
+
width: device === "mobile" ? "375px" : "600px",
|
|
2012
|
+
maxWidth: "100%",
|
|
2013
|
+
background: "white",
|
|
2014
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
2015
|
+
borderRadius: "8px",
|
|
2016
|
+
overflow: "hidden"
|
|
2017
|
+
},
|
|
2018
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2019
|
+
"iframe",
|
|
2020
|
+
{
|
|
2021
|
+
srcDoc: previewHtml,
|
|
2022
|
+
style: {
|
|
2023
|
+
width: "100%",
|
|
2024
|
+
height: "100%",
|
|
2025
|
+
minHeight: "600px",
|
|
2026
|
+
border: "none"
|
|
2027
|
+
},
|
|
2028
|
+
title: "Email Preview"
|
|
2029
|
+
}
|
|
2030
|
+
)
|
|
2031
|
+
}
|
|
2032
|
+
)
|
|
2160
2033
|
}
|
|
2161
2034
|
)
|
|
2162
2035
|
] }) : null })
|
|
@@ -2164,9 +2037,9 @@ var BroadcastInlinePreview = () => {
|
|
|
2164
2037
|
};
|
|
2165
2038
|
|
|
2166
2039
|
// src/components/Broadcasts/BroadcastPreviewField.tsx
|
|
2167
|
-
var
|
|
2040
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2168
2041
|
var BroadcastPreviewField = () => {
|
|
2169
|
-
return /* @__PURE__ */ (0,
|
|
2042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: {
|
|
2170
2043
|
padding: "1rem",
|
|
2171
2044
|
background: "#f9fafb",
|
|
2172
2045
|
borderRadius: "4px",
|
|
@@ -2175,8 +2048,102 @@ var BroadcastPreviewField = () => {
|
|
|
2175
2048
|
}, children: "Email preview is available inline below the content editor." });
|
|
2176
2049
|
};
|
|
2177
2050
|
|
|
2051
|
+
// src/components/Broadcasts/EmailRenderer.tsx
|
|
2052
|
+
var import_react9 = require("react");
|
|
2053
|
+
var import_render = require("@react-email/render");
|
|
2054
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2055
|
+
var EmailRenderer = ({
|
|
2056
|
+
template,
|
|
2057
|
+
data,
|
|
2058
|
+
device = "desktop",
|
|
2059
|
+
onRender
|
|
2060
|
+
}) => {
|
|
2061
|
+
const [renderedHtml, setRenderedHtml] = (0, import_react9.useState)("");
|
|
2062
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
2063
|
+
const iframeRef = (0, import_react9.useRef)(null);
|
|
2064
|
+
const renderEmail = (0, import_react9.useCallback)(async () => {
|
|
2065
|
+
try {
|
|
2066
|
+
const TemplateComponent = template;
|
|
2067
|
+
const element = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TemplateComponent, { ...data });
|
|
2068
|
+
const html = await (0, import_render.render)(element, {
|
|
2069
|
+
pretty: true
|
|
2070
|
+
});
|
|
2071
|
+
setRenderedHtml(html);
|
|
2072
|
+
onRender?.(html);
|
|
2073
|
+
setError(null);
|
|
2074
|
+
} catch (err) {
|
|
2075
|
+
setError(err);
|
|
2076
|
+
console.error("Failed to render email template:", err);
|
|
2077
|
+
}
|
|
2078
|
+
}, [template, data, onRender]);
|
|
2079
|
+
(0, import_react9.useEffect)(() => {
|
|
2080
|
+
renderEmail();
|
|
2081
|
+
}, [renderEmail]);
|
|
2082
|
+
(0, import_react9.useEffect)(() => {
|
|
2083
|
+
if (iframeRef.current && renderedHtml) {
|
|
2084
|
+
const iframe = iframeRef.current;
|
|
2085
|
+
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
2086
|
+
if (doc) {
|
|
2087
|
+
doc.open();
|
|
2088
|
+
doc.write(renderedHtml);
|
|
2089
|
+
doc.close();
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}, [renderedHtml]);
|
|
2093
|
+
const containerStyle = {
|
|
2094
|
+
width: "100%",
|
|
2095
|
+
height: "100%",
|
|
2096
|
+
display: "flex",
|
|
2097
|
+
alignItems: "flex-start",
|
|
2098
|
+
justifyContent: "center",
|
|
2099
|
+
overflow: "auto",
|
|
2100
|
+
padding: "2rem",
|
|
2101
|
+
boxSizing: "border-box"
|
|
2102
|
+
};
|
|
2103
|
+
const iframeStyle = {
|
|
2104
|
+
width: device === "mobile" ? "375px" : "600px",
|
|
2105
|
+
height: "100%",
|
|
2106
|
+
minHeight: "600px",
|
|
2107
|
+
background: "white",
|
|
2108
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
|
2109
|
+
borderRadius: device === "mobile" ? "20px" : "8px",
|
|
2110
|
+
border: "none",
|
|
2111
|
+
display: "block"
|
|
2112
|
+
};
|
|
2113
|
+
const errorStyle = {
|
|
2114
|
+
background: "white",
|
|
2115
|
+
border: "1px solid #ef4444",
|
|
2116
|
+
borderRadius: "4px",
|
|
2117
|
+
padding: "2rem",
|
|
2118
|
+
maxWidth: "500px"
|
|
2119
|
+
};
|
|
2120
|
+
if (error) {
|
|
2121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: errorStyle, children: [
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { style: { color: "#ef4444", margin: "0 0 1rem" }, children: "Template Render Error" }),
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("pre", { style: {
|
|
2124
|
+
background: "#f9fafb",
|
|
2125
|
+
padding: "1rem",
|
|
2126
|
+
borderRadius: "4px",
|
|
2127
|
+
overflowX: "auto",
|
|
2128
|
+
fontSize: "12px",
|
|
2129
|
+
color: "#374151",
|
|
2130
|
+
margin: 0
|
|
2131
|
+
}, children: error.message })
|
|
2132
|
+
] });
|
|
2133
|
+
}
|
|
2134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2135
|
+
"iframe",
|
|
2136
|
+
{
|
|
2137
|
+
ref: iframeRef,
|
|
2138
|
+
style: iframeStyle,
|
|
2139
|
+
sandbox: "allow-same-origin",
|
|
2140
|
+
title: "Email Preview"
|
|
2141
|
+
}
|
|
2142
|
+
) });
|
|
2143
|
+
};
|
|
2144
|
+
|
|
2178
2145
|
// src/components/Broadcasts/StatusBadge.tsx
|
|
2179
|
-
var
|
|
2146
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2180
2147
|
var statusConfig = {
|
|
2181
2148
|
["draft" /* DRAFT */]: {
|
|
2182
2149
|
label: "Draft",
|
|
@@ -2224,7 +2191,7 @@ var statusConfig = {
|
|
|
2224
2191
|
var StatusBadge = ({ cellData }) => {
|
|
2225
2192
|
const status = cellData;
|
|
2226
2193
|
const config = statusConfig[status] || statusConfig["draft" /* DRAFT */];
|
|
2227
|
-
return /* @__PURE__ */ (0,
|
|
2194
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2228
2195
|
"span",
|
|
2229
2196
|
{
|
|
2230
2197
|
style: {
|
|
@@ -2246,6 +2213,59 @@ var StatusBadge = ({ cellData }) => {
|
|
|
2246
2213
|
var EmptyField = () => {
|
|
2247
2214
|
return null;
|
|
2248
2215
|
};
|
|
2216
|
+
|
|
2217
|
+
// src/email-templates/DefaultBroadcastTemplate.tsx
|
|
2218
|
+
var import_components2 = require("@react-email/components");
|
|
2219
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2220
|
+
var DefaultBroadcastTemplate = ({
|
|
2221
|
+
subject,
|
|
2222
|
+
preheader,
|
|
2223
|
+
content
|
|
2224
|
+
}) => {
|
|
2225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_components2.Html, { children: [
|
|
2226
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Head, {}),
|
|
2227
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Preview, { children: preheader || subject }),
|
|
2228
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Body, { style: main, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_components2.Container, { style: container, children: [
|
|
2229
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Section, { style: contentSection, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { dangerouslySetInnerHTML: { __html: content } }) }),
|
|
2230
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Hr, { style: divider }),
|
|
2231
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_components2.Section, { style: footer, children: [
|
|
2232
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Text, { style: footerText, children: "You're receiving this email because you subscribed to our newsletter." }),
|
|
2233
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Text, { style: footerText, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components2.Link, { href: "{{unsubscribe_url}}", style: footerLink, children: "Unsubscribe" }) })
|
|
2234
|
+
] })
|
|
2235
|
+
] }) })
|
|
2236
|
+
] });
|
|
2237
|
+
};
|
|
2238
|
+
var main = {
|
|
2239
|
+
backgroundColor: "#ffffff",
|
|
2240
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
|
|
2241
|
+
};
|
|
2242
|
+
var container = {
|
|
2243
|
+
margin: "0 auto",
|
|
2244
|
+
padding: "40px 20px",
|
|
2245
|
+
maxWidth: "600px"
|
|
2246
|
+
};
|
|
2247
|
+
var contentSection = {
|
|
2248
|
+
fontSize: "16px",
|
|
2249
|
+
lineHeight: "1.6",
|
|
2250
|
+
color: "#374151"
|
|
2251
|
+
};
|
|
2252
|
+
var divider = {
|
|
2253
|
+
borderColor: "#e5e7eb",
|
|
2254
|
+
margin: "40px 0 20px"
|
|
2255
|
+
};
|
|
2256
|
+
var footer = {
|
|
2257
|
+
textAlign: "center"
|
|
2258
|
+
};
|
|
2259
|
+
var footerText = {
|
|
2260
|
+
fontSize: "14px",
|
|
2261
|
+
lineHeight: "1.5",
|
|
2262
|
+
color: "#6b7280",
|
|
2263
|
+
margin: "0 0 10px"
|
|
2264
|
+
};
|
|
2265
|
+
var footerLink = {
|
|
2266
|
+
color: "#6b7280",
|
|
2267
|
+
textDecoration: "underline"
|
|
2268
|
+
};
|
|
2249
2269
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2250
2270
|
0 && (module.exports = {
|
|
2251
2271
|
BroadcastEditor,
|