diff --git a/docs/content/docs/features/export/email.mdx b/docs/content/docs/features/export/email.mdx index b85f466ba5..7422c7ac80 100644 --- a/docs/content/docs/features/export/email.mdx +++ b/docs/content/docs/features/export/email.mdx @@ -55,16 +55,33 @@ See the [full example](/examples/interoperability/converting-blocks-to-react-ema - **header**: Add content to the top of the email (must be a React-Email compatible component) - **footer**: Add content to the bottom of the email (must be a React-Email compatible component) - **head**: Inject elements into the [Head element](https://react.email/docs/components/head) +- **container**: Customize the container element (A component which will wrap the email content including the header and footer) Example usage: -```tsx -import { Text } from "@react-email/components"; +```tsx twoslash +import React from "react"; +import { + ReactEmailExporter, + reactEmailDefaultSchemaMappings, +} from "@blocknote/xl-email-exporter"; +import { BlockNoteEditor } from "@blocknote/core"; +import { Text, Container } from "@react-email/components"; + +const editor = BlockNoteEditor.create(); + +// ---cut--- +const exporter = new ReactEmailExporter( + editor.schema, + reactEmailDefaultSchemaMappings, +); + const html = await exporter.toReactEmailDocument(editor.document, { preview: "This is a preview of the email content", header: Header, footer: Footer, head: My email, + container: ({ children }) => {children}, }); ``` @@ -106,3 +123,41 @@ const defaultOptions = { colors: COLORS_DEFAULT, // defaults from @blocknote/core }; ``` + +### Custom styles + +Want to tweak the default styles of the email? You can use `reactEmailDefaultSchemaMappingsWithStyles` to create a custom mapping with your own styles. + +```tsx +import { + ReactEmailExporter, + reactEmailDefaultSchemaMappingsWithStyles, +} from "@blocknote/xl-email-exporter"; +import { Text } from "@react-email/components"; + +const { blockMapping, inlineContentMapping, styleMapping } = + reactEmailDefaultSchemaMappingsWithStyles({ + textStyles: { + paragraph: { + style: { + fontSize: 16, + lineHeight: 1.5, + margin: 3, + minHeight: 24, + }, + }, + }, + }); + +new ReactEmailExporter(schema, { + // You can still use the default block mapping, but you can also overwrite it + blockMapping: { + ...blockMapping, + audio: (block, exporter) => { + return Audio block; + }, + }, + inlineContentMapping, + styleMapping, +}); +``` diff --git a/docs/package.json b/docs/package.json index 358bd32d45..3389fb11a9 100644 --- a/docs/package.json +++ b/docs/package.json @@ -83,7 +83,7 @@ "fumadocs-typescript": "^4.0.6", "fumadocs-ui": "^15.5.4", "import-in-the-middle": "^1.14.2", - "next": "^15.4.1", + "next": "^15.4.3", "nodemailer": "^6.10.1", "partykit": "^0.0.115", "pg": "^8.15.5", diff --git a/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx b/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx index c74855be0e..6b70999d78 100644 --- a/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx +++ b/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx @@ -1,5 +1,7 @@ import { BlockNoteSchema, + COLORS_DARK_MODE_DEFAULT, + COLORS_DEFAULT, combineByGroup, filterSuggestionItems, withPageBreak, @@ -10,7 +12,9 @@ import "@blocknote/mantine/style.css"; import { SuggestionMenuController, getDefaultReactSlashMenuItems, + useBlockNoteContext, useCreateBlockNote, + usePrefersColorScheme, } from "@blocknote/react"; import { ReactEmailExporter, @@ -314,6 +318,11 @@ export default function App() { ], }); + const existingContext = useBlockNoteContext(); + const systemColorScheme = usePrefersColorScheme(); + const colorScheme = + existingContext?.colorSchemePreference || systemColorScheme; + const onChange = async () => { if (!editor || !editor.document) { return; @@ -321,6 +330,10 @@ export default function App() { const exporter = new ReactEmailExporter( editor.schema, reactEmailDefaultSchemaMappings, + { + colors: + colorScheme === "dark" ? COLORS_DARK_MODE_DEFAULT : COLORS_DEFAULT, + }, ); const emailHtml = await exporter.toReactEmailDocument(editor.document); diff --git a/examples/05-interoperability/08-converting-blocks-to-react-email/src/styles.css b/examples/05-interoperability/08-converting-blocks-to-react-email/src/styles.css index ea15fbecd3..1ccbb9d746 100644 --- a/examples/05-interoperability/08-converting-blocks-to-react-email/src/styles.css +++ b/examples/05-interoperability/08-converting-blocks-to-react-email/src/styles.css @@ -21,8 +21,6 @@ .email { min-height: 500px; - display: flex; - align-items: stretch; } /* hack to get react-email to show on website */ diff --git a/packages/xl-email-exporter/src/react-email/__snapshots__/reactEmailExporter.test.tsx.snap b/packages/xl-email-exporter/src/react-email/__snapshots__/reactEmailExporter.test.tsx.snap index 3bd0a6b1bc..5334c2cd62 100644 --- a/packages/xl-email-exporter/src/react-email/__snapshots__/reactEmailExporter.test.tsx.snap +++ b/packages/xl-email-exporter/src/react-email/__snapshots__/reactEmailExporter.test.tsx.snap @@ -1,35 +1,35 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`react email exporter > should export a document (HTML snapshot) > __snapshots__/reactEmailExporter 1`] = `"

Welcome to this demo 🙌!

Hello World nested

Hello World double nested

This paragraph has a background color

Paragraph

Heading

Heading right

justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


    Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    • Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    • Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    1. Numbered List Item 1

    2. Numbered List Item 2

      1. Numbered List Item Nested 1

      2. Numbered List Item Nested 2

      3. Numbered List Item Nested funky right

      4. Numbered List Item Nested funky center

    Numbered List Item

Check List Item

Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg
Open video file

From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm

Open audio file

From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3

audio.mp3

Audio file caption

Inline Content:

Styled Text Link

Table Cell 1Table Cell 2Table Cell 3
Table Cell 4Table Cell Bold 5Table Cell 6
Table Cell 7Table Cell 8Table Cell 9

const helloWorld = (message) => {

console.log("Hello World", message);

};

"`; +exports[`react email exporter > should export a document (HTML snapshot) > __snapshots__/reactEmailExporter 1`] = `"

Welcome to this demo 🙌!

Hello World nested

Hello World double nested

This paragraph has a background color

Paragraph

Heading

Heading right

justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


  1. Numbered List Item

Check List Item

Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Open file
From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg
Open video file

From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm

Open audio file

From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3

audio.mp3

Audio file caption

Inline Content:

Styled Text Link

Table Cell 1Table Cell 2Table Cell 3
Table Cell 4Table Cell Bold 5Table Cell 6
Table Cell 7Table Cell 8Table Cell 9

const helloWorld = (message) => {

console.log("Hello World", message);

};

"`; -exports[`react email exporter > should export a document with multiple preview lines > __snapshots__/reactEmailExporterWithMultiplePreview 1`] = `"
First preview lineSecond preview line
 ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏

Welcome to this demo 🙌!

Hello World nested

Hello World double nested

This paragraph has a background color

Paragraph

Heading

Heading right

justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


    Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    • Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    • Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    1. Numbered List Item 1

    2. Numbered List Item 2

      1. Numbered List Item Nested 1

      2. Numbered List Item Nested 2

      3. Numbered List Item Nested funky right

      4. Numbered List Item Nested funky center

    Numbered List Item

Check List Item

Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg
Open video file

From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm

Open audio file

From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3

audio.mp3

Audio file caption

Inline Content:

Styled Text Link

Table Cell 1Table Cell 2Table Cell 3
Table Cell 4Table Cell Bold 5Table Cell 6
Table Cell 7Table Cell 8Table Cell 9

const helloWorld = (message) => {

console.log("Hello World", message);

};

"`; +exports[`react email exporter > should export a document with multiple preview lines > __snapshots__/reactEmailExporterWithMultiplePreview 1`] = `"
First preview lineSecond preview line
 ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏

Welcome to this demo 🙌!

Hello World nested

Hello World double nested

This paragraph has a background color

Paragraph

Heading

Heading right

justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


  1. Numbered List Item

Check List Item

Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Open file
From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg
Open video file

From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm

Open audio file

From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3

audio.mp3

Audio file caption

Inline Content:

Styled Text Link

Table Cell 1Table Cell 2Table Cell 3
Table Cell 4Table Cell Bold 5Table Cell 6
Table Cell 7Table Cell 8Table Cell 9

const helloWorld = (message) => {

console.log("Hello World", message);

};

"`; -exports[`react email exporter > should export a document with preview > __snapshots__/reactEmailExporterWithPreview 1`] = `"
This is a preview of the email content
 ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏

Welcome to this demo 🙌!

Hello World nested

Hello World double nested

This paragraph has a background color

Paragraph

Heading

Heading right

justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


    Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    • Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    • Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

    1. Numbered List Item 1

    2. Numbered List Item 2

      1. Numbered List Item Nested 1

      2. Numbered List Item Nested 2

      3. Numbered List Item Nested funky right

      4. Numbered List Item Nested funky center

    Numbered List Item

Check List Item

Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg
Open video file

From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm

Open audio file

From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3

audio.mp3

Audio file caption

Inline Content:

Styled Text Link

Table Cell 1Table Cell 2Table Cell 3
Table Cell 4Table Cell Bold 5Table Cell 6
Table Cell 7Table Cell 8Table Cell 9

const helloWorld = (message) => {

console.log("Hello World", message);

};

"`; +exports[`react email exporter > should export a document with preview > __snapshots__/reactEmailExporterWithPreview 1`] = `"
This is a preview of the email content
 ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏

Welcome to this demo 🙌!

Hello World nested

Hello World double nested

This paragraph has a background color

Paragraph

Heading

Heading right

justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.


  1. Numbered List Item

Check List Item

Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Wide CellTable CellTable Cell
Open file
From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg
Open video file

From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm

Open audio file

From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3

audio.mp3

Audio file caption

Inline Content:

Styled Text Link

Table Cell 1Table Cell 2Table Cell 3
Table Cell 4Table Cell Bold 5Table Cell 6
Table Cell 7Table Cell 8Table Cell 9

const helloWorld = (message) => {

console.log("Hello World", message);

};

"`; -exports[`react email exporter > should handle document with background colors > __snapshots__/reactEmailExporterBackgroundColor 1`] = `"

Text with background color

"`; +exports[`react email exporter > should handle document with background colors > __snapshots__/reactEmailExporterBackgroundColor 1`] = `"

Text with background color

"`; -exports[`react email exporter > should handle document with check list items > __snapshots__/reactEmailExporterCheckList 1`] = `"

Checked item

Unchecked item

"`; +exports[`react email exporter > should handle document with check list items > __snapshots__/reactEmailExporterCheckList 1`] = `"

Checked item

Unchecked item

"`; -exports[`react email exporter > should handle document with code blocks > __snapshots__/reactEmailExporterCodeBlock 1`] = `"

const hello = 'world';

console.log(hello);

"`; +exports[`react email exporter > should handle document with code blocks > __snapshots__/reactEmailExporterCodeBlock 1`] = `"

const hello = 'world';

console.log(hello);

"`; -exports[`react email exporter > should handle document with complex nested structure > __snapshots__/reactEmailExporterComplexNested 1`] = `"

Complex Document

This is a paragraph with bold and italic text, plus a link.

    List item with nested content

    Nested paragraph

    1. Nested numbered item

"`; +exports[`react email exporter > should handle document with complex nested structure > __snapshots__/reactEmailExporterComplexNested 1`] = `"

Complex Document

This is a paragraph with bold and italic text, plus a link.

"`; -exports[`react email exporter > should handle document with headings of different levels > __snapshots__/reactEmailExporterHeadings 1`] = `"

Heading 1

Heading 2

Heading 3

"`; +exports[`react email exporter > should handle document with headings of different levels > __snapshots__/reactEmailExporterHeadings 1`] = `"

Heading 1

Heading 2

Heading 3

"`; -exports[`react email exporter > should handle document with links > __snapshots__/reactEmailExporterWithLinks 1`] = `"

Click here

"`; +exports[`react email exporter > should handle document with links > __snapshots__/reactEmailExporterWithLinks 1`] = `"

Click here

"`; -exports[`react email exporter > should handle document with mixed content types > __snapshots__/reactEmailExporterMixedContent 1`] = `"

Main Heading

Regular paragraph with bold text

    Numbered list item

"`; +exports[`react email exporter > should handle document with mixed content types > __snapshots__/reactEmailExporterMixedContent 1`] = `"

Main Heading

Regular paragraph with bold text

  1. Numbered list item

"`; -exports[`react email exporter > should handle document with mixed list types > __snapshots__/reactEmailExporterMixedLists 1`] = `"

    Bullet item 1

    Bullet item 2

    Numbered item 1

    Numbered item 2

"`; +exports[`react email exporter > should handle document with mixed list types > __snapshots__/reactEmailExporterMixedLists 1`] = `"
  1. Numbered item 1

  2. Numbered item 2

"`; -exports[`react email exporter > should handle document with nested lists > __snapshots__/reactEmailExporterNestedLists 1`] = `"

    Parent item

    • Child item

"`; +exports[`react email exporter > should handle document with nested lists > __snapshots__/reactEmailExporterNestedLists 1`] = `""`; -exports[`react email exporter > should handle document with only text blocks > __snapshots__/reactEmailExporterSimpleText 1`] = `"

Simple text content

"`; +exports[`react email exporter > should handle document with only text blocks > __snapshots__/reactEmailExporterSimpleText 1`] = `"

Simple text content

"`; -exports[`react email exporter > should handle document with styled text > __snapshots__/reactEmailExporterStyledText 1`] = `"

Bold and italic text

"`; +exports[`react email exporter > should handle document with styled text > __snapshots__/reactEmailExporterStyledText 1`] = `"

Bold and italic text

"`; -exports[`react email exporter > should handle document with text alignment > __snapshots__/reactEmailExporterTextAlignment 1`] = `"

Center aligned text

Right aligned text

"`; +exports[`react email exporter > should handle document with text alignment > __snapshots__/reactEmailExporterTextAlignment 1`] = `"

Center aligned text

Right aligned text

"`; -exports[`react email exporter > should handle document with text colors > __snapshots__/reactEmailExporterTextColor 1`] = `"

Colored text

"`; +exports[`react email exporter > should handle document with text colors > __snapshots__/reactEmailExporterTextColor 1`] = `"

Colored text

"`; -exports[`react email exporter > should handle empty document > __snapshots__/reactEmailExporterEmpty 1`] = `"
"`; +exports[`react email exporter > should handle empty document > __snapshots__/reactEmailExporterEmpty 1`] = `""`; diff --git a/packages/xl-email-exporter/src/react-email/defaultSchema/blocks.tsx b/packages/xl-email-exporter/src/react-email/defaultSchema/blocks.tsx index 91f2f8ea71..1dddcb52f5 100644 --- a/packages/xl-email-exporter/src/react-email/defaultSchema/blocks.tsx +++ b/packages/xl-email-exporter/src/react-email/defaultSchema/blocks.tsx @@ -1,10 +1,10 @@ import { + BlockMapping, DefaultBlockSchema, mapTableCell, pageBreakSchema, StyledText, } from "@blocknote/core"; -import { BlockMapping } from "@blocknote/core/src/exporter/mapping.js"; import { CodeBlock, dracula, @@ -15,27 +15,169 @@ import { Text, } from "@react-email/components"; -export const reactEmailBlockMappingForDefaultSchema: BlockMapping< +// Define TextProps type based on React Email Text component +type TextProps = React.ComponentPropsWithoutRef; + +// Define the styles interface for configurable Text components +export interface ReactEmailTextStyles { + paragraph?: Partial; + bulletListItem?: Partial; + toggleListItem?: Partial; + numberedListItem?: Partial; + checkListItem?: Partial; + quote?: Partial; + tableError?: Partial; + tableCell?: Partial; + caption?: Partial; + heading1?: Partial; + heading2?: Partial; + heading3?: Partial; + heading4?: Partial; + heading5?: Partial; + heading6?: Partial; + codeBlock?: Partial>; +} + +const defaultTextStyle: TextProps["style"] = { + fontSize: 16, + lineHeight: 1.5, + margin: 3, + minHeight: 24, +}; + +// Default styles for Text components +export const defaultReactEmailTextStyles = { + paragraph: { + style: defaultTextStyle, + }, + bulletListItem: { + style: defaultTextStyle, + }, + toggleListItem: { + style: defaultTextStyle, + }, + numberedListItem: { + style: defaultTextStyle, + }, + checkListItem: { + style: defaultTextStyle, + }, + quote: { + style: defaultTextStyle, + }, + tableError: { + style: defaultTextStyle, + }, + tableCell: { + style: defaultTextStyle, + }, + caption: { + style: defaultTextStyle, + }, + heading1: { + style: { + fontSize: 48, + margin: 3, + }, + }, + heading2: { + style: { + fontSize: 36, + margin: 3, + }, + }, + heading3: { + style: { + fontSize: 24, + margin: 3, + }, + }, + heading4: { + style: { + fontSize: 20, + margin: 3, + }, + }, + heading5: { + style: { + fontSize: 18, + margin: 3, + }, + }, + heading6: { + style: { + fontSize: 16, + margin: 3, + }, + }, + codeBlock: { + style: defaultTextStyle, + }, +} satisfies ReactEmailTextStyles; + +export const createReactEmailBlockMappingForDefaultSchema = ( + textStyles: ReactEmailTextStyles = defaultReactEmailTextStyles, +): BlockMapping< DefaultBlockSchema & typeof pageBreakSchema.blockSchema, any, any, React.ReactElement, React.ReactElement | React.ReactElement -> = { +> => ({ paragraph: (block, t) => { - return {t.transformInlineContent(block.content)}; + return ( + + {t.transformInlineContent(block.content)} + + ); }, bulletListItem: (block, t) => { // Return only the
  • for grouping in the exporter - return {t.transformInlineContent(block.content)}; + return ( + + {t.transformInlineContent(block.content)} + + ); }, toggleListItem: (block, t) => { // Return only the
  • for grouping in the exporter - return {t.transformInlineContent(block.content)}; + return ( + + {t.transformInlineContent(block.content)} + + ); }, numberedListItem: (block, t, _nestingLevel) => { // Return only the
  • for grouping in the exporter - return {t.transformInlineContent(block.content)}; + return ( + + {t.transformInlineContent(block.content)} + + ); }, checkListItem: (block, t) => { // Render a checkbox using inline SVG for better appearance in email @@ -85,7 +227,13 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< ); return ( - + {checkboxSvg} {t.transformInlineContent(block.content)} @@ -93,7 +241,14 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< }, heading: (block, t) => { return ( - + {t.transformInlineContent(block.content)} ); @@ -108,6 +263,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< fontFamily="'CommitMono', monospace" language={block.props.language as PrismLanguage} theme={dracula} + {...textStyles.codeBlock} + style={{ + ...defaultReactEmailTextStyles.codeBlock.style, + ...textStyles.codeBlock?.style, + }} /> ); }, @@ -136,7 +296,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< defaultText="Open audio file" icon={icon} /> - + ); }, @@ -165,7 +329,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< defaultText="Open video file" icon={icon} /> - + ); }, @@ -194,7 +362,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< defaultText="Open file" icon={icon} /> - + ); }, @@ -211,7 +383,7 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< // Render table using standard HTML table elements for email compatibility const table = block.content; if (!table || typeof table !== "object" || !Array.isArray(table.rows)) { - return Table data not available; + return Table data not available; } const headerRowsCount = (table.headerRows as number) ?? 0; const headerColsCount = (table.headerCols as number) ?? 0; @@ -247,17 +419,24 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< style={{ border: "1px solid #ddd", padding: "8px 12px", - background: isHeader - ? "#f5f5f5" - : normalizedCell.props.backgroundColor !== "default" - ? normalizedCell.props.backgroundColor - : "#fff", - fontWeight: isHeader ? "bold" : "normal", + background: + normalizedCell.props.backgroundColor !== "default" + ? t.options.colors[ + normalizedCell.props + .backgroundColor as keyof typeof t.options.colors + ].background + : "inherit", + fontWeight: isHeader ? "bold" : undefined, textAlign: normalizedCell.props.textAlignment || "left", color: normalizedCell.props.textColor !== "default" - ? normalizedCell.props.textColor + ? t.options.colors[ + normalizedCell.props + .textColor as keyof typeof t.options.colors + ].text : "inherit", + ...defaultReactEmailTextStyles.tableCell.style, + ...textStyles.tableCell?.style, }} {...((normalizedCell.props.colspan || 1) > 1 && { colSpan: normalizedCell.props.colspan || 1, @@ -280,14 +459,15 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< // Render block quote with a left border and subtle background for email compatibility return ( {t.transformInlineContent(block.content)} @@ -306,7 +486,11 @@ export const reactEmailBlockMappingForDefaultSchema: BlockMapping< /> ); }, -}; +}); + +// Export the original mapping for backward compatibility +export const reactEmailBlockMappingForDefaultSchema = + createReactEmailBlockMappingForDefaultSchema(); // Helper for file-like blocks (audio, video, file) function FileLink({ @@ -338,12 +522,30 @@ function FileLink({ ); } -function Caption({ caption, width }: { caption?: string; width?: number }) { +function Caption({ + caption, + width, + textStyles, +}: { + caption?: string; + width?: number; + textStyles: ReactEmailTextStyles; +}) { if (!caption) { return null; } return ( - + {caption} ); diff --git a/packages/xl-email-exporter/src/react-email/defaultSchema/index.ts b/packages/xl-email-exporter/src/react-email/defaultSchema/index.ts index 20b049705d..4e24d24e95 100644 --- a/packages/xl-email-exporter/src/react-email/defaultSchema/index.ts +++ b/packages/xl-email-exporter/src/react-email/defaultSchema/index.ts @@ -1,9 +1,77 @@ -import { reactEmailBlockMappingForDefaultSchema } from "./blocks.js"; -import { reactEmailInlineContentMappingForDefaultSchema } from "./inlinecontent.js"; -import { reactEmailStyleMappingForDefaultSchema } from "./styles.js"; +import { + reactEmailBlockMappingForDefaultSchema, + createReactEmailBlockMappingForDefaultSchema, + type ReactEmailTextStyles, + defaultReactEmailTextStyles, +} from "./blocks.js"; +import { + reactEmailInlineContentMappingForDefaultSchema, + createReactEmailInlineContentMappingForDefaultSchema, + type ReactEmailLinkStyles, + defaultReactEmailLinkStyles, +} from "./inlinecontent.js"; +import { + reactEmailStyleMappingForDefaultSchema, + createReactEmailStyleMappingForDefaultSchema, + type ReactEmailStyleTransformStyles, + defaultReactEmailStyleTransformStyles, +} from "./styles.js"; + +// Re-export for backward compatibility +export { reactEmailBlockMappingForDefaultSchema } from "./blocks.js"; +export { reactEmailInlineContentMappingForDefaultSchema } from "./inlinecontent.js"; +export { reactEmailStyleMappingForDefaultSchema } from "./styles.js"; + +// Export the new configurable functions +export { + createReactEmailBlockMappingForDefaultSchema, + type ReactEmailTextStyles, + defaultReactEmailTextStyles, +} from "./blocks.js"; +export { + createReactEmailInlineContentMappingForDefaultSchema, + type ReactEmailLinkStyles, + defaultReactEmailLinkStyles, +} from "./inlinecontent.js"; +export { + createReactEmailStyleMappingForDefaultSchema, + type ReactEmailStyleTransformStyles, + defaultReactEmailStyleTransformStyles, +} from "./styles.js"; + +// Export the combined styles interface +export interface ReactEmailDefaultSchemaStyles { + textStyles?: ReactEmailTextStyles; + linkStyles?: ReactEmailLinkStyles; + styleTransformStyles?: ReactEmailStyleTransformStyles; +} + +// Export the default combined styles +export const defaultReactEmailDefaultSchemaStyles: ReactEmailDefaultSchemaStyles = + { + textStyles: defaultReactEmailTextStyles, + linkStyles: defaultReactEmailLinkStyles, + styleTransformStyles: defaultReactEmailStyleTransformStyles, + }; export const reactEmailDefaultSchemaMappings = { blockMapping: reactEmailBlockMappingForDefaultSchema, inlineContentMapping: reactEmailInlineContentMappingForDefaultSchema, styleMapping: reactEmailStyleMappingForDefaultSchema, }; + +export const reactEmailDefaultSchemaMappingsWithStyles = ( + styles: ReactEmailDefaultSchemaStyles = defaultReactEmailDefaultSchemaStyles, +): typeof reactEmailDefaultSchemaMappings => { + return { + blockMapping: createReactEmailBlockMappingForDefaultSchema( + styles.textStyles, + ), + inlineContentMapping: createReactEmailInlineContentMappingForDefaultSchema( + styles.linkStyles, + ), + styleMapping: createReactEmailStyleMappingForDefaultSchema( + styles.styleTransformStyles, + ), + }; +}; diff --git a/packages/xl-email-exporter/src/react-email/defaultSchema/inlinecontent.tsx b/packages/xl-email-exporter/src/react-email/defaultSchema/inlinecontent.tsx index bbb15ae9dd..96adc8dfba 100644 --- a/packages/xl-email-exporter/src/react-email/defaultSchema/inlinecontent.tsx +++ b/packages/xl-email-exporter/src/react-email/defaultSchema/inlinecontent.tsx @@ -1,19 +1,31 @@ import { DefaultInlineContentSchema, DefaultStyleSchema, + InlineContentMapping, } from "@blocknote/core"; -import { InlineContentMapping } from "@blocknote/core/src/exporter/mapping.js"; import { Link } from "@react-email/components"; -export const reactEmailInlineContentMappingForDefaultSchema: InlineContentMapping< +// Define the styles interface for configurable Link components +export interface ReactEmailLinkStyles { + link?: Partial>; +} + +// Default styles for Link components +export const defaultReactEmailLinkStyles: ReactEmailLinkStyles = { + link: {}, +}; + +export const createReactEmailInlineContentMappingForDefaultSchema = ( + linkStyles: ReactEmailLinkStyles = defaultReactEmailLinkStyles, +): InlineContentMapping< DefaultInlineContentSchema, DefaultStyleSchema, React.ReactElement | React.ReactElement, React.ReactElement -> = { +> => ({ link: (ic, t) => { return ( - + {...ic.content.map((content) => { return t.transformStyledText(content); })} @@ -23,4 +35,8 @@ export const reactEmailInlineContentMappingForDefaultSchema: InlineContentMappin text: (ic, t) => { return t.transformStyledText(ic); }, -}; +}); + +// Export the original mapping for backward compatibility +export const reactEmailInlineContentMappingForDefaultSchema = + createReactEmailInlineContentMappingForDefaultSchema(); diff --git a/packages/xl-email-exporter/src/react-email/defaultSchema/styles.tsx b/packages/xl-email-exporter/src/react-email/defaultSchema/styles.tsx index 9b86345139..4b025ee6fa 100644 --- a/packages/xl-email-exporter/src/react-email/defaultSchema/styles.tsx +++ b/packages/xl-email-exporter/src/react-email/defaultSchema/styles.tsx @@ -1,10 +1,17 @@ import { DefaultStyleSchema, StyleMapping } from "@blocknote/core"; import { CSSProperties } from "react"; -export const reactEmailStyleMappingForDefaultSchema: StyleMapping< - DefaultStyleSchema, - CSSProperties -> = { +// Define the styles interface for configurable style transformations +// This can be extended in the future to allow customizing style transformations +export type ReactEmailStyleTransformStyles = Record; + +// Default styles for style transformations +export const defaultReactEmailStyleTransformStyles: ReactEmailStyleTransformStyles = + {}; + +export const createReactEmailStyleMappingForDefaultSchema = ( + _styleTransformStyles: ReactEmailStyleTransformStyles = defaultReactEmailStyleTransformStyles, +): StyleMapping => ({ bold: (val) => { if (!val) { return {}; @@ -37,17 +44,24 @@ export const reactEmailStyleMappingForDefaultSchema: StyleMapping< textDecoration: "line-through", }; }, - backgroundColor: (val) => { + backgroundColor: (val, exporter) => { + if (!val) { + return {}; + } return { - backgroundColor: val, + backgroundColor: + exporter.options.colors[val as keyof typeof exporter.options.colors] + .background, }; }, - textColor: (val) => { + textColor: (val, exporter) => { if (!val) { return {}; } return { - color: val, + color: + exporter.options.colors[val as keyof typeof exporter.options.colors] + .text, }; }, code: (val) => { @@ -55,7 +69,11 @@ export const reactEmailStyleMappingForDefaultSchema: StyleMapping< return {}; } return { - fontFamily: "Courier", + fontFamily: "GeistMono", }; }, -}; +}); + +// Export the original mapping for backward compatibility +export const reactEmailStyleMappingForDefaultSchema = + createReactEmailStyleMappingForDefaultSchema(); diff --git a/packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx b/packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx index 493f5bf20f..3721f569b0 100644 --- a/packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx +++ b/packages/xl-email-exporter/src/react-email/reactEmailExporter.tsx @@ -12,7 +12,6 @@ import { } from "@blocknote/core"; import { Body, - Container, Head, Html, Link, @@ -63,7 +62,14 @@ export class ReactEmailExporter< public transformStyledText(styledText: StyledText) { const stylesArray = this.mapStyles(styledText.styles); const styles = Object.assign({}, ...stylesArray); - return {styledText.text}; + return ( + "), + }} + /> + ); } private async renderGroupedListBlocks( @@ -95,10 +101,10 @@ export class ReactEmailExporter< ); } listItems.push( - +
  • {liContent} {nestedList.length > 0 && nestedList} - , +
  • , ); } let element: React.ReactElement; @@ -226,7 +232,11 @@ export class ReactEmailExporter< let i = 0; while (i < blocks.length) { const b = blocks[i]; - if (b.type === "bulletListItem" || b.type === "numberedListItem") { + if ( + b.type === "bulletListItem" || + b.type === "numberedListItem" || + b.type === "toggleListItem" + ) { const { element, nextIndex } = await this.renderGroupedListBlocks( blocks, i, @@ -278,9 +288,18 @@ export class ReactEmailExporter< * @see https://react.email/components */ footer?: React.ReactElement; + /** + * Customize the container element + */ + container?: React.FC<{ children: React.ReactNode }>; }, ) { const transformedBlocks = await this.transformBlocks(blocks); + const DefaultContainer = + options?.container || + (({ children }: { children: React.ReactNode }) => ( + {children} + )); return renderEmail( {options?.head} @@ -295,11 +314,11 @@ export class ReactEmailExporter< > {options?.preview && {options.preview}} - + {options?.header} {transformedBlocks} {options?.footer} - + , diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca73f99bc7..ff75b1448d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,7 +115,7 @@ importers: version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) '@fumadocs/mdx-remote': specifier: 1.3.0 - version: 1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@headlessui/react': specifier: ^2.2.4 version: 2.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -151,7 +151,7 @@ importers: version: 1.0.4(@polar-sh/sdk@0.34.3(zod@3.25.76))(better-auth@1.2.12) '@polar-sh/nextjs': specifier: ^0.4.1 - version: 0.4.2(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.25.76) + version: 0.4.2(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.25.76) '@polar-sh/sdk': specifier: ^0.34.2 version: 0.34.3(zod@3.25.76) @@ -163,7 +163,7 @@ importers: version: 4.3.0(react@19.1.0) '@sentry/nextjs': specifier: 9.14.0 - version: 9.14.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0) + version: 9.14.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0) '@shikijs/core': specifier: ^3.2.1 version: 3.7.0 @@ -217,7 +217,7 @@ importers: version: 3.6.8(@uppy/core@3.13.1) '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 1.5.0(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@vercel/og': specifier: ^0.6.8 version: 0.6.8 @@ -250,28 +250,28 @@ importers: version: 11.18.2(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) fumadocs-core: specifier: ^15.5.4 - version: 15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) fumadocs-docgen: specifier: ^2.0.1 version: 2.1.0 fumadocs-mdx: specifier: ^11.6.9 - version: 11.6.10(@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.19.3)(yaml@2.7.0)) + version: 11.6.10(@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.19.3)(yaml@2.7.0)) fumadocs-twoslash: specifier: ^3.1.4 - version: 3.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(fumadocs-ui@15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) + version: 3.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(fumadocs-ui@15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) fumadocs-typescript: specifier: ^4.0.6 version: 4.0.6(@types/react@19.1.8)(typescript@5.8.3) fumadocs-ui: specifier: ^15.5.4 - version: 15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11) + version: 15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11) import-in-the-middle: specifier: ^1.14.2 version: 1.14.2 next: - specifier: ^15.4.1 - version: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^15.4.3 + version: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) nodemailer: specifier: ^6.10.1 version: 6.10.1 @@ -6816,53 +6816,53 @@ packages: '@napi-rs/wasm-runtime@0.2.4': resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} - '@next/env@15.4.1': - resolution: {integrity: sha512-DXQwFGAE2VH+f2TJsKepRXpODPU+scf5fDbKOME8MMyeyswe4XwgRdiiIYmBfkXU+2ssliLYznajTrOQdnLR5A==} + '@next/env@15.4.3': + resolution: {integrity: sha512-lKJ9KJAvaWzqurIsz6NWdQOLj96mdhuDMusLSYHw9HBe2On7BjUwU1WeRvq19x7NrEK3iOgMeSBV5qEhVH1cMw==} - '@next/swc-darwin-arm64@15.4.1': - resolution: {integrity: sha512-L+81yMsiHq82VRXS2RVq6OgDwjvA4kDksGU8hfiDHEXP+ncKIUhUsadAVB+MRIp2FErs/5hpXR0u2eluWPAhig==} + '@next/swc-darwin-arm64@15.4.3': + resolution: {integrity: sha512-YAhZWKeEYY7LHQJiQ8fe3Y6ymfcDcTn7rDC8PDu/pdeIl1Z2LHD4uyPNuQUGCEQT//MSNv6oZCeQzZfTCKZv+A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.4.1': - resolution: {integrity: sha512-jfz1RXu6SzL14lFl05/MNkcN35lTLMJWPbqt7Xaj35+ZWAX342aePIJrN6xBdGeKl6jPXJm0Yqo3Xvh3Gpo3Uw==} + '@next/swc-darwin-x64@15.4.3': + resolution: {integrity: sha512-ZPHRdd51xaxCMpT4viQ6h8TgYM1zPW1JIeksPY9wKlyvBVUQqrWqw8kEh1sa7/x0Ied+U7pYHkAkutrUwxbMcg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.4.1': - resolution: {integrity: sha512-k0tOFn3dsnkaGfs6iQz8Ms6f1CyQe4GacXF979sL8PNQxjYS1swx9VsOyUQYaPoGV8nAZ7OX8cYaeiXGq9ahPQ==} + '@next/swc-linux-arm64-gnu@15.4.3': + resolution: {integrity: sha512-QUdqftCXC5vw5cowucqi9FeOPQ0vdMxoOHLY0J5jPdercwSJFjdi9CkEO4Xkq1eG4t1TB/BG81n6rmTsWoILnw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.4.1': - resolution: {integrity: sha512-4ogGQ/3qDzbbK3IwV88ltihHFbQVq6Qr+uEapzXHXBH1KsVBZOB50sn6BWHPcFjwSoMX2Tj9eH/fZvQnSIgc3g==} + '@next/swc-linux-arm64-musl@15.4.3': + resolution: {integrity: sha512-HTL31NsmoafX+r5g91Yj3+q34nrn1xKmCWVuNA+fUWO4X0pr+n83uGzLyEOn0kUqbMZ40KmWx+4wsbMoUChkiQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.4.1': - resolution: {integrity: sha512-Jj0Rfw3wIgp+eahMz/tOGwlcYYEFjlBPKU7NqoOkTX0LY45i5W0WcDpgiDWSLrN8KFQq/LW7fZq46gxGCiOYlQ==} + '@next/swc-linux-x64-gnu@15.4.3': + resolution: {integrity: sha512-HRQLWoeFkKXd2YCEEy9GhfwOijRm37x4w5r0MMVHxBKSA6ms3JoPUXvGhfHT6srnGRcEUWNrQ2vzkHir5ZWTSw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.4.1': - resolution: {integrity: sha512-9WlEZfnw1vFqkWsTMzZDgNL7AUI1aiBHi0S2m8jvycPyCq/fbZjtE/nDkhJRYbSjXbtRHYLDBlmP95kpjEmJbw==} + '@next/swc-linux-x64-musl@15.4.3': + resolution: {integrity: sha512-NyXUx6G7AayaRGUsVPenuwhyAoyxjQuQPaK50AXoaAHPwRuif4WmSrXUs8/Y0HJIZh8E/YXRm9H7uuGfiacpuQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.4.1': - resolution: {integrity: sha512-WodRbZ9g6CQLRZsG3gtrA9w7Qfa9BwDzhFVdlI6sV0OCPq9JrOrJSp9/ioLsezbV8w9RCJ8v55uzJuJ5RgWLZg==} + '@next/swc-win32-arm64-msvc@15.4.3': + resolution: {integrity: sha512-2CUTmpzN/7cL1a7GjdLkDFlfH3nwMwW8a6JiaAUsL9MtKmNNO3fnXqnY0Zk30fii3hVEl4dr7ztrpYt0t2CcGQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.4.1': - resolution: {integrity: sha512-y+wTBxelk2xiNofmDOVU7O5WxTHcvOoL3srOM0kxTzKDjQ57kPU0tpnPJ/BWrRnsOwXEv0+3QSbGR7hY4n9LkQ==} + '@next/swc-win32-x64-msvc@15.4.3': + resolution: {integrity: sha512-i54YgUhvrUQxQD84SjAbkfWhYkOdm/DNRAVekCHLWxVg3aUbyC6NFQn9TwgCkX5QAS2pXCJo3kFboSFvrsd7dA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -12728,8 +12728,8 @@ packages: next-validate-link@1.5.2: resolution: {integrity: sha512-BMGyo0qJqscSJw0gSCUy3gDrF7LEID6qkVhHtLSIdPPtOYONq0+fidWvLteUzEUqHeKz1GBI9QN3bpfT6Me42A==} - next@15.4.1: - resolution: {integrity: sha512-eNKB1q8C7o9zXF8+jgJs2CzSLIU3T6bQtX6DcTnCq1sIR1CJ0GlSyRs1BubQi3/JgCnr9Vr+rS5mOMI38FFyQw==} + next@15.4.3: + resolution: {integrity: sha512-uW7Qe6poVasNIE1X382nI29oxSdFJzjQzTgJFLD43MxyPfGKKxCMySllhBpvqr48f58Om+tLMivzRwBpXEytvA==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -17100,10 +17100,10 @@ snapshots: dependencies: tslib: 2.8.1 - '@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': dependencies: '@mdx-js/mdx': 3.1.0(acorn@8.14.1) - fumadocs-core: 15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + fumadocs-core: 15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) gray-matter: 4.0.3 react: 19.1.0 zod: 3.25.76 @@ -17670,30 +17670,30 @@ snapshots: '@emnapi/runtime': 1.4.3 '@tybys/wasm-util': 0.9.0 - '@next/env@15.4.1': {} + '@next/env@15.4.3': {} - '@next/swc-darwin-arm64@15.4.1': + '@next/swc-darwin-arm64@15.4.3': optional: true - '@next/swc-darwin-x64@15.4.1': + '@next/swc-darwin-x64@15.4.3': optional: true - '@next/swc-linux-arm64-gnu@15.4.1': + '@next/swc-linux-arm64-gnu@15.4.3': optional: true - '@next/swc-linux-arm64-musl@15.4.1': + '@next/swc-linux-arm64-musl@15.4.3': optional: true - '@next/swc-linux-x64-gnu@15.4.1': + '@next/swc-linux-x64-gnu@15.4.3': optional: true - '@next/swc-linux-x64-musl@15.4.1': + '@next/swc-linux-x64-musl@15.4.3': optional: true - '@next/swc-win32-arm64-msvc@15.4.1': + '@next/swc-win32-arm64-msvc@15.4.3': optional: true - '@next/swc-win32-x64-msvc@15.4.1': + '@next/swc-win32-x64-msvc@15.4.3': optional: true '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': @@ -18211,11 +18211,11 @@ snapshots: better-auth: 1.2.12 zod: 3.25.76 - '@polar-sh/nextjs@0.4.2(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.25.76)': + '@polar-sh/nextjs@0.4.2(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.25.76)': dependencies: '@polar-sh/adapter-utils': 0.2.1(zod@3.25.76) '@polar-sh/sdk': 0.34.3(zod@3.25.76) - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' - zod @@ -19519,7 +19519,7 @@ snapshots: '@sentry/core@9.14.0': {} - '@sentry/nextjs@9.14.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0)': + '@sentry/nextjs@9.14.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.98.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.30.0 @@ -19532,7 +19532,7 @@ snapshots: '@sentry/vercel-edge': 9.14.0 '@sentry/webpack-plugin': 3.3.1(encoding@0.1.13)(webpack@5.98.0) chalk: 3.0.0 - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) resolve: 1.22.8 rollup: 4.35.0 stacktrace-parser: 0.1.11 @@ -20779,9 +20779,9 @@ snapshots: '@uppy/core': 3.13.1 '@uppy/utils': 5.9.0 - '@vercel/analytics@1.5.0(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@vercel/analytics@1.5.0(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': optionalDependencies: - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 '@vercel/og@0.6.8': @@ -22755,7 +22755,7 @@ snapshots: fsevents@2.3.3: optional: true - fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@formatjs/intl-localematcher': 0.6.1 '@orama/orama': 3.1.10 @@ -22776,7 +22776,7 @@ snapshots: unist-util-visit: 5.0.0 optionalDependencies: '@types/react': 19.1.8 - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -22791,14 +22791,14 @@ snapshots: unist-util-visit: 5.0.0 zod: 3.25.76 - fumadocs-mdx@11.6.10(@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.19.3)(yaml@2.7.0)): + fumadocs-mdx@11.6.10(@fumadocs/mdx-remote@1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0))(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.19.3)(yaml@2.7.0)): dependencies: '@mdx-js/mdx': 3.1.0(acorn@8.14.1) '@standard-schema/spec': 1.0.0 chokidar: 4.0.3 esbuild: 0.25.6 estree-util-value-to-estree: 3.4.0 - fumadocs-core: 15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + fumadocs-core: 15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) js-yaml: 4.1.0 lru-cache: 11.1.0 picocolors: 1.1.1 @@ -22807,18 +22807,18 @@ snapshots: unist-util-visit: 5.0.0 zod: 3.25.76 optionalDependencies: - '@fumadocs/mdx-remote': 1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@fumadocs/mdx-remote': 1.3.0(acorn@8.14.1)(fumadocs-core@15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite: 6.3.5(@types/node@22.15.2)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - acorn - supports-color - fumadocs-twoslash@3.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(fumadocs-ui@15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3): + fumadocs-twoslash@3.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(fumadocs-ui@15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3): dependencies: '@radix-ui/react-popover': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@shikijs/twoslash': 3.7.0(typescript@5.8.3) - fumadocs-ui: 15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11) + fumadocs-ui: 15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11) mdast-util-from-markdown: 2.0.2 mdast-util-gfm: 3.1.0 mdast-util-to-hast: 13.2.0 @@ -22851,7 +22851,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-ui@15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11): + fumadocs-ui@15.6.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.1.11): dependencies: '@radix-ui/react-accordion': 1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-collapsible': 1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -22864,7 +22864,7 @@ snapshots: '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) '@radix-ui/react-tabs': 1.1.12(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) class-variance-authority: 0.7.1 - fumadocs-core: 15.6.3(@types/react@19.1.8)(next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + fumadocs-core: 15.6.3(@types/react@19.1.8)(next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) lodash.merge: 4.6.2 next-themes: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) postcss-selector-parser: 7.1.0 @@ -22875,7 +22875,7 @@ snapshots: tailwind-merge: 3.3.1 optionalDependencies: '@types/react': 19.1.8 - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tailwindcss: 4.1.11 transitivePeerDependencies: - '@oramacloud/client' @@ -23661,7 +23661,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.15.2 + '@types/node': 20.17.50 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -24830,9 +24830,9 @@ snapshots: transitivePeerDependencies: - supports-color - next@15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@next/env': 15.4.1 + '@next/env': 15.4.3 '@swc/helpers': 0.5.15 caniuse-lite: 1.0.30001727 postcss: 8.4.31 @@ -24840,14 +24840,14 @@ snapshots: react-dom: 19.1.0(react@19.1.0) styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.1.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.4.1 - '@next/swc-darwin-x64': 15.4.1 - '@next/swc-linux-arm64-gnu': 15.4.1 - '@next/swc-linux-arm64-musl': 15.4.1 - '@next/swc-linux-x64-gnu': 15.4.1 - '@next/swc-linux-x64-musl': 15.4.1 - '@next/swc-win32-arm64-msvc': 15.4.1 - '@next/swc-win32-x64-msvc': 15.4.1 + '@next/swc-darwin-arm64': 15.4.3 + '@next/swc-darwin-x64': 15.4.3 + '@next/swc-linux-arm64-gnu': 15.4.3 + '@next/swc-linux-arm64-musl': 15.4.3 + '@next/swc-linux-x64-gnu': 15.4.3 + '@next/swc-linux-x64-musl': 15.4.3 + '@next/swc-win32-arm64-msvc': 15.4.3 + '@next/swc-win32-x64-msvc': 15.4.3 '@opentelemetry/api': 1.9.0 '@playwright/test': 1.51.1 babel-plugin-react-compiler: 19.1.0-rc.2 @@ -25659,7 +25659,7 @@ snapshots: glob: 11.0.3 log-symbols: 7.0.1 mime-types: 3.0.1 - next: 15.4.1(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.3(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) normalize-path: 3.0.0 ora: 8.2.0 socket.io: 4.8.1