diff --git a/README.md b/README.md index 0e941e6..e5d26a9 100644 --- a/README.md +++ b/README.md @@ -216,8 +216,17 @@ For the full API reference, component props, design patterns, and common mistake | [`@unlayer-internal/shared-elements`](./packages/shared) | Framework-agnostic shared logic | Internal | | [`@unlayer/elements-demo`](./packages/demo) | Demo application | — | +## Examples + +Runnable projects you can clone and experiment with: + +| Example | Description | +|---------|-------------| +| [`examples/nextjs-app-router`](./examples/nextjs-app-router) | Next.js 15 App Router — renders an email with `renderToHtml()` in a Server Component, previews it at `/email-preview`, and serves the raw HTML from a Route Handler | + ## Development + ```bash # Prerequisites: Node.js (see .nvmrc), pnpm 9+ diff --git a/examples/nextjs-app-router/.gitignore b/examples/nextjs-app-router/.gitignore new file mode 100644 index 0000000..c3a54e7 --- /dev/null +++ b/examples/nextjs-app-router/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.next/ +package-lock.json +tsconfig.tsbuildinfo diff --git a/examples/nextjs-app-router/README.md b/examples/nextjs-app-router/README.md new file mode 100644 index 0000000..229f321 --- /dev/null +++ b/examples/nextjs-app-router/README.md @@ -0,0 +1,30 @@ +# Next.js App Router Example + +Renders an Unlayer Elements email to HTML inside a Next.js 15 App Router **Server Component**, then previews it in the browser. + +## Run it + +From this directory: + +```bash +pnpm install +pnpm dev +``` + +Then open: + +- — the email rendered in an interactive preview dashboard (Desktop/Mobile view toggles, Raw HTML / Plain Text view) +- — the raw HTML string, served as `text/html` from a Route Handler + +## Architecture + +| File | Purpose | +|------|---------| +| `src/emails/welcome-email.tsx` | A realistic transactional email as a plain component tree, parameterized by props | +| `src/app/email-preview/page.tsx` | A Server Component calling `renderToHtml()` and `renderToPlainText()`, passing the strings to the client-side layout | +| `src/app/email-preview/PreviewDashboard.tsx` | Client-side dashboard layout managing viewport resizing, copy to clipboard, and tab switching | +| `src/app/email-preview/raw/route.ts` | A Route Handler returning the rendered HTML — the shape production code usually takes | + +### Note on Server Components + +The template components and `renderToHtml()` function run completely on the server. The component tree is never shipped to the browser; only the resulting HTML/plain-text strings are. diff --git a/examples/nextjs-app-router/next-env.d.ts b/examples/nextjs-app-router/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/examples/nextjs-app-router/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/examples/nextjs-app-router/next.config.ts b/examples/nextjs-app-router/next.config.ts new file mode 100644 index 0000000..abb8d2c --- /dev/null +++ b/examples/nextjs-app-router/next.config.ts @@ -0,0 +1,12 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import type { NextConfig } from "next"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const nextConfig: NextConfig = { + serverExternalPackages: ["@unlayer/react-elements"], + outputFileTracingRoot: __dirname, +}; + +export default nextConfig; diff --git a/examples/nextjs-app-router/package.json b/examples/nextjs-app-router/package.json new file mode 100644 index 0000000..3797716 --- /dev/null +++ b/examples/nextjs-app-router/package.json @@ -0,0 +1,30 @@ +{ + "name": "@unlayer/example-nextjs-app-router", + "version": "0.0.0", + "private": true, + "description": "Next.js App Router example rendering an Elements email with renderToHtml()", + "packageManager": "pnpm@9.7.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@unlayer/react-elements": "^0.1.20", + "next": "15.5.22", + "react": "19.1.0", + "react-dom": "19.1.0" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "^5" + }, + "pnpm": { + "overrides": { + "postcss": ">=8.5.18", + "sharp": ">=0.35.0" + } + } +} diff --git a/examples/nextjs-app-router/pnpm-lock.yaml b/examples/nextjs-app-router/pnpm-lock.yaml new file mode 100644 index 0000000..903f230 --- /dev/null +++ b/examples/nextjs-app-router/pnpm-lock.yaml @@ -0,0 +1,933 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + postcss: '>=8.5.18' + sharp: '>=0.35.0' + +importers: + + .: + dependencies: + '@unlayer/react-elements': + specifier: ^0.1.20 + version: 0.1.20(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3) + next: + specifier: 15.5.22 + version: 15.5.22(@types/node@20.19.43)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: + specifier: 19.1.0 + version: 19.1.0 + react-dom: + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) + devDependencies: + '@types/node': + specifier: ^20 + version: 20.19.43 + '@types/react': + specifier: ^19 + version: 19.2.18 + '@types/react-dom': + specifier: ^19 + version: 19.2.4(@types/react@19.2.18) + typescript: + specifier: ^5 + version: 5.9.3 + +packages: + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@lexical/clipboard@0.49.0': + resolution: {integrity: sha512-AVKj21xH1qU7JAFA/v0hCoafa+Yti1I7cHG+JQIgc/EqCtP8ePeJyIfTPNN/tXskoCdq++HewQoiSXRwwlocVg==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/dragon@0.49.0': + resolution: {integrity: sha512-62/4DP5qyX/l4Yf5qRyyQrs9BV725eRU3OmLUW6g7T5xrcHXxAo7tia/NvqjqvXdpvQzyHjWgsy7dMitGITUsw==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/extension@0.49.0': + resolution: {integrity: sha512-Wv0VsuqxorbxHCK4ms1PwAu6cXIGNLtflq66auF+zwxOtBprkSFV8VzzzdKLOYD2admP0VK6EqVCeGXFK1GggA==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/headless@0.49.0': + resolution: {integrity: sha512-3fQiKMAZ+K5/YlnGli9MId4LM8TPuOdLXIy1eUszfA/8hTZyPMcAxLjG99940UClgcoW9XxP03T37PJpFvpqIA==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/html@0.49.0': + resolution: {integrity: sha512-NQqAydKzRjQl7Jx+bTTyC2iuz5uhuDaQX0SSPrdfgaFDCPjqQEejcBkCt1QXnu1CkbVHutZKVGVzljx40Y6y+Q==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/internal@0.49.0': + resolution: {integrity: sha512-s+XjPC7Qb39A/Xx9ahcz1s69CPix4ultaqyW+MDG0AXYW2quDr7m0USqReKIAiuoLIWtGN5HW8BwV2Y6t4qr6Q==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/link@0.49.0': + resolution: {integrity: sha512-CsZVu1OfnPOn6IKWVO5DbmKwIuicGtgWSzrZMdMB8w6yPoMgFyPWeVulhPE7LNz2FhaQCWv+lVVYVAiJrUzJsg==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/list@0.49.0': + resolution: {integrity: sha512-zs6wYkxakDRcJO0KmwrPPHgeLLIxzjD+P2CRu+scJCHRuA5e2iSw2iFjH5n/LlWBrlnPMSjeQse4QqsRy9nnqA==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/rich-text@0.49.0': + resolution: {integrity: sha512-LM+d8ULmQvi2Av6UaW4NAIa/Bj9FOo9oh6+dglTzuoFPFQ3sogy5c38i2K9k0ul7aE+bFsXkSmHbu4LkrHIkBQ==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/selection@0.49.0': + resolution: {integrity: sha512-08Vd1+VoC6YnztWOFWVsqF/Hxw5EP8qeL1c7t3+rVCV8revLzXxdQw+vbPX3tgM4fmjOBDUXk6Ws1+rTtsqjcQ==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@lexical/utils@0.49.0': + resolution: {integrity: sha512-Jaa6DERBqxiFOFa49VPRV1WOb7mzRbMZ5U+v+RFagjzTmBhfxDmEkbQQ9nYTU3n3DPfdT8Hkyffextmw04etXg==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + '@next/env@15.5.22': + resolution: {integrity: sha512-O5BlKb3KtsHkvO0gjjV66PuJnAgCtIEIzwkt50HRAHsQkU1t77eksIXSZV84/WMtZJjWrnDUPKHVRi0D62nSAA==} + + '@next/swc-darwin-arm64@15.5.22': + resolution: {integrity: sha512-/VISwtffSg8+fVvBbXdglsvruCsdbBC4dG25iU6xascKVqfQKsj/OtjGnOEkIS7pX5GB9e9/r5QprpicsGL3gw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.22': + resolution: {integrity: sha512-NiA9ve8hbiuhG/Q17a2mZDRVxMTtg3rTOgjLnDaLlE+AEPAQlkkuKrfePEbeOrgYmX0U2KGX4EVEn09hXU5GlQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.22': + resolution: {integrity: sha512-vAPa9vltW+UW/KWtjXeSUFgV3wb1x9d/BeyC6WFI6eBpL0D2f70oGwtOp6193mNW3qusrpgBzMQferPf+Zh8Dw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.5.22': + resolution: {integrity: sha512-iknK80pWlNDnkdSr13bd8mMuG3Z2oTxODwsZHvuMY7caMk77+rBLdHVWsy8v2EVa3ZojJ/+wJX5fnq8va6Gv8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.5.22': + resolution: {integrity: sha512-penuEdkwU2OOAiS+n4LE8T/VIoCfAI01QcLZTJ2xc3+l4Q22L/DzURocmI2LU1b+8BMQoLAP1Sze3uYAZT05Bg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.5.22': + resolution: {integrity: sha512-ZM0BKJm3FZ+guG6WT6PcyOLtp6paZ5tngcJC/uUKvLW4Y0TQnnVi1+UGdo8Q6Yxp5gaS82pmC1rD/oFlhkWB3g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.5.22': + resolution: {integrity: sha512-rY/YaumrZaS0//94BnHLF5VSRp0GFUO4GvXNuoCBb0cGSci96yO+p1JaNL2aq9YZAYv9cuZRziV02x5IQH/wjg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.22': + resolution: {integrity: sha512-s5IA4cyrbR2XK/5NWcu5dp8CfPBiKME+UhvNperia7uQybEgg5+LIhGMiY37WQE4rcI4owsDcU4IVUjLoTuDkA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@preact/signals-core@1.14.4': + resolution: {integrity: sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@types/node@20.19.43': + resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@unlayer/exporters@1.455.0': + resolution: {integrity: sha512-IJUQmrGlBig6OyRURkTeXm3sSD/leYD86kwzhJB4EgodUTty67yxjkS+Nyu6C+SRU4EwdfdiFswxNxtw4ttuMw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@unlayer/types': '>=1.455.0' + + '@unlayer/react-elements@0.1.20': + resolution: {integrity: sha512-jhI5/IozTUcEg7U/W7i3UL/Gk2xzEs/wqN2OaJ2IGnjKGT7mC5f3MSVRWZgaJiYvqqbrkEn7K6KwwAZ3Zf45Yw==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + '@unlayer/types@1.455.0': + resolution: {integrity: sha512-WHRNCpErzixSENMVqy8oyXA9l091iu3WJpl/sGlQfV64r86b/WNjBgc7vG2uT3/XOWS0AynGT0Kn+aJjsdAR6g==} + + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + happy-dom@20.11.1: + resolution: {integrity: sha512-XSt8tMzbW9ymE7687xztkO1ckR7qJNQ3LywY9vlYGhGi3zXrGBHuUo2Cl1ztZaICW+1eAGdkLbj6iwVqDT33kg==} + engines: {node: '>=20.0.0'} + + lexical@0.49.0: + resolution: {integrity: sha512-9V1ZIzGpJEd8rIN+nN7veL4fW4fFWbS66Un4JNqSZB4D5t9euzN9+3+jEXy83FjNjNy0MiiUI3+DQaGCLYko0w==} + peerDependencies: + typescript: '>=5.2' + peerDependenciesMeta: + typescript: + optional: true + + nanoid@3.3.17: + resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next@15.5.22: + resolution: {integrity: sha512-mrtal1sRxO4YrlDS98sDuIvGZivKbFix8w7oAL9ZynfOgc3cADQOQgvwtMooc18Qr8bKzvQAcHwHZ0mbJ7zcfQ==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} + engines: {node: ^10 || ^12 || >=14} + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: ^19.1.0 + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + ws@8.21.2: + resolution: {integrity: sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + +snapshots: + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-win32-arm64@0.35.3': + optional: true + + '@img/sharp-win32-ia32@0.35.3': + optional: true + + '@img/sharp-win32-x64@0.35.3': + optional: true + + '@lexical/clipboard@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/extension': 0.49.0(typescript@5.9.3) + '@lexical/html': 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/list': 0.49.0(typescript@5.9.3) + '@lexical/selection': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + '@types/trusted-types': 2.0.7 + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/dragon@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/extension': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/extension@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + '@preact/signals-core': 1.14.4 + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/headless@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/internal': 0.49.0(typescript@5.9.3) + happy-dom: 20.11.1 + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@lexical/html@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/extension': 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/selection': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/internal@0.49.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@lexical/link@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/extension': 0.49.0(typescript@5.9.3) + '@lexical/html': 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/list@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/extension': 0.49.0(typescript@5.9.3) + '@lexical/html': 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/rich-text@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/clipboard': 0.49.0(typescript@5.9.3) + '@lexical/dragon': 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.49.0(typescript@5.9.3) + '@lexical/html': 0.49.0(typescript@5.9.3) + '@lexical/selection': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/selection@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/internal': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@lexical/utils@0.49.0(typescript@5.9.3)': + dependencies: + '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/selection': 0.49.0(typescript@5.9.3) + lexical: 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@next/env@15.5.22': {} + + '@next/swc-darwin-arm64@15.5.22': + optional: true + + '@next/swc-darwin-x64@15.5.22': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.22': + optional: true + + '@next/swc-linux-arm64-musl@15.5.22': + optional: true + + '@next/swc-linux-x64-gnu@15.5.22': + optional: true + + '@next/swc-linux-x64-musl@15.5.22': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.22': + optional: true + + '@next/swc-win32-x64-msvc@15.5.22': + optional: true + + '@preact/signals-core@1.14.4': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@types/node@20.19.43': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.4(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': {} + + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 20.19.43 + + '@unlayer/exporters@1.455.0(@unlayer/types@1.455.0)(typescript@5.9.3)': + dependencies: + '@lexical/headless': 0.49.0(typescript@5.9.3) + '@lexical/html': 0.49.0(typescript@5.9.3) + '@lexical/link': 0.49.0(typescript@5.9.3) + '@lexical/list': 0.49.0(typescript@5.9.3) + '@lexical/rich-text': 0.49.0(typescript@5.9.3) + '@lexical/selection': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.49.0(typescript@5.9.3) + '@unlayer/types': 1.455.0 + lexical: 0.49.0(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@unlayer/react-elements@0.1.20(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)': + dependencies: + '@unlayer/exporters': 1.455.0(@unlayer/types@1.455.0)(typescript@5.9.3) + '@unlayer/types': 1.455.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@unlayer/types@1.455.0': {} + + buffer-image-size@0.6.4: + dependencies: + '@types/node': 20.19.43 + + caniuse-lite@1.0.30001806: {} + + client-only@0.0.1: {} + + csstype@3.2.3: {} + + detect-libc@2.1.2: + optional: true + + entities@7.0.1: {} + + happy-dom@20.11.1: + dependencies: + '@types/node': 20.19.43 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.1 + buffer-image-size: 0.6.4 + entities: 7.0.1 + whatwg-mimetype: 3.0.0 + ws: 8.21.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + lexical@0.49.0(typescript@5.9.3): + dependencies: + '@lexical/internal': 0.49.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + nanoid@3.3.17: {} + + next@15.5.22(@types/node@20.19.43)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@next/env': 15.5.22 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001806 + postcss: 8.5.25 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(react@19.1.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.22 + '@next/swc-darwin-x64': 15.5.22 + '@next/swc-linux-arm64-gnu': 15.5.22 + '@next/swc-linux-arm64-musl': 15.5.22 + '@next/swc-linux-x64-gnu': 15.5.22 + '@next/swc-linux-x64-musl': 15.5.22 + '@next/swc-win32-arm64-msvc': 15.5.22 + '@next/swc-win32-x64-msvc': 15.5.22 + sharp: 0.35.3(@types/node@20.19.43) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + + picocolors@1.1.1: {} + + postcss@8.5.25: + dependencies: + nanoid: 3.3.17 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react@19.1.0: {} + + scheduler@0.26.0: {} + + semver@7.8.5: + optional: true + + sharp@0.35.3(@types/node@20.19.43): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 20.19.43 + optional: true + + source-map-js@1.2.1: {} + + styled-jsx@5.1.6(react@19.1.0): + dependencies: + client-only: 0.0.1 + react: 19.1.0 + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + whatwg-mimetype@3.0.0: {} + + ws@8.21.2: {} diff --git a/examples/nextjs-app-router/pnpm-workspace.yaml b/examples/nextjs-app-router/pnpm-workspace.yaml new file mode 100644 index 0000000..3334c0e --- /dev/null +++ b/examples/nextjs-app-router/pnpm-workspace.yaml @@ -0,0 +1 @@ +packages: [] diff --git a/examples/nextjs-app-router/src/app/email-preview/PreviewDashboard.tsx b/examples/nextjs-app-router/src/app/email-preview/PreviewDashboard.tsx new file mode 100644 index 0000000..1352693 --- /dev/null +++ b/examples/nextjs-app-router/src/app/email-preview/PreviewDashboard.tsx @@ -0,0 +1,309 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; + +interface PreviewDashboardProps { + initialHtml: string; + initialText: string; + recipientName: string; +} + +type Tab = "preview" | "raw" | "text"; +type Viewport = "desktop" | "mobile"; + +export default function PreviewDashboard({ + initialHtml, + initialText, + recipientName, +}: PreviewDashboardProps) { + const [activeTab, setActiveTab] = useState("preview"); + const [viewport, setViewport] = useState("desktop"); + const [copying, setCopying] = useState(false); + + const handleCopy = async () => { + const textToCopy = activeTab === "raw" ? initialHtml : initialText; + try { + await navigator.clipboard.writeText(textToCopy); + setCopying(true); + setTimeout(() => setCopying(false), 2000); + } catch (err) { + console.error("Failed to copy text", err); + } + }; + + return ( +
+ {/* Top Navigation & Info */} +
+

+ + ← back to home + +

+
+
+

Welcome Email

+

+ Rendered on the server with renderToHtml() ( + {(initialHtml.length / 1024).toFixed(1)} KB) +

+
+
+ + Open Raw HTML ↗ + +
+
+
+ + {/* Control Bar: Tabs & Viewport */} +
+ {/* Format Selector Tabs */} +
+ + + +
+ + {/* Action Controls (Viewport/Copy) */} +
+ {activeTab === "preview" && ( +
+ + +
+ )} + + {activeTab !== "preview" && ( + + )} +
+
+ + {/* Main Content Area */} +
+ {activeTab === "preview" && ( +
+