Files
Félix Malfait 29e68a7f87 Refactor outbound email content compilation (#23782)
## Integration status

This is now the final landing PR for the reviewed editor/email
architecture stack.

| Order | Pull request | Scope | Status |
| --- | --- | --- | --- |
| 0 | #23657 | Advanced text editor capability presets | Merged into
`main` |
| 1 | **This PR** | Outbound email content compilation | Ready to land
into `main` |
| 2 | #23783 | Clean editor surface seam | Reviewed and merged into this
branch |
| 3 | #23790 | Shared editor block catalog | Reviewed and merged through
#23783 |
| 4 | #23791 | Canonical TipTap document persistence | Reviewed and
merged through #23790 |

The current branch tree contains the complete stack. Merging this PR
lands all four follow-up layers.

## Architecture

The stack establishes four reusable boundaries:

1. **Outbound compilation** — campaign, workflow, and one-to-one/tool
email share one compiler, sanitizer policy, renderer, and plain-text
derivation path.
2. **Editor surface profiles** — the generic editor owns rendering
mechanics while each consuming surface declares chrome, extensions, and
explicit compatibility readers.
3. **Shared block primitives** — sections, columns, HTML, images,
buttons, and related commands live in the neutral advanced-editor
catalog; email behavior is supplied by email schemas/rendering, not by
relocating reusable blocks into an email editor.
4. **Canonical persistence** — Twenty-owned authoring persists complete,
versioned TipTap JSON documents. HTML, Markdown, plain text, and
BlockNote are projections or explicitly owned legacy boundaries.

## Compatibility boundaries

Compatibility remains only where shipped data requires it:

- workflow Send Email: versionless TipTap JSON, HTML, and plain text
- inline email: HTML
- AI instructions: Markdown
- record rich text: BlockNote arrays and older Markdown/plain text

Campaign is unshipped, so its editor, stored rows, sendability
validation, and send-time compilation require the current canonical
schema version. AI chat drafts are canonical-only local state; old or
malformed drafts are rejected at hydration, and plain-text preprompts
are converted at their entry point.

## Outbound compiler details

The shared compiler owns:

- strict structured email-document parsing
- React-email rendering
- one cached DOMPurify/JSDOM policy for structured and legacy HTML
- plain-text derivation from sanitized HTML
- single-pass structured-document binding resolution across text,
variable tags, links, images, buttons, and raw HTML

Resolved workflow values remain inert, legacy workflow and one-to-one
HTML remain supported, and Campaign HTML/plain text come from the same
compiled result.

## Verification

- all automated standard/security reviews passed on the three merged
upper PRs with no unresolved threads
- shared TipTap/email codec tests: 20 passing
- editor, AI draft, and workflow compatibility tests: 14 passing
- campaign validation and compilation tests: 31 passing
- full shared suite during development: 223 suites / 1,738 tests passing
- twenty-front, twenty-shared, and twenty-server typechecks
- changed-file type-aware lint and formatting checks
2026-08-05 14:31:50 +02:00
..
2025-04-03 14:26:19 +02:00

Twenty Emails

This package contains the email templates used by Twenty.

Features

  • Email templates built with React Email
  • Internationalization (i18n) support via @lingui/react
  • Local preview server for testing email templates

Getting Started

Starting the Local Preview Server

To start the local preview server for email development:

npx nx start twenty-emails

This will run the development server on port 4001. You can then view your email templates at http://localhost:4001.

Building Emails

To build the email templates:

npx nx build twenty-emails

Email Structure

Each email template is located in the src/emails directory. The templates use various components from the src/components directory to maintain consistent styling and functionality.