ba3813e2422d9fe4e252a90242393cada65644dc
The previous detection tripped on ANY inline `style=` attribute and on `<span>` elements specifically, which forced templates into HTML-only editing mode whenever the user had used Visual mode features like text color. TipTap's TextStyle + Color + Link extensions (configured in EmailEditor.tsx) natively round-trip exactly that markup -- TipTap emits `<span style="color: rgb(...)">…</span>` itself when you change a text color, then the detection rejected it as "custom HTML" on the very next load. Empirically about 21% of a 156-template corpus tripped this purely on TipTap-export artifacts (`background-color: initial`, color spans). This rewrite permits what TipTap can represent and rejects only what it can't: * Drop the broad inline-style check entirely (TextStyle/Color/Link preserve inline styles on spans and links). * Remove `<span>` from the custom-elements list (TextStyle handles it). * Expand the custom-elements list to explicitly cover everything TipTap has no extension for: `<div>`, `<section>`, `<article>`, `<header>`, `<footer>`, `<nav>`, `<aside>`, `<main>`, full table family (`<table>`, `<tr>`, `<td>`, `<th>`, `<tbody>`, `<thead>`, `<tfoot>`, `<colgroup>`, `<col>` -- no Table extension is loaded), form/embed/ media/interactive (`<form>`, `<input>`, `<button>`, `<select>`, `<textarea>`, `<iframe>`, `<video>`, `<audio>`, `<svg>`, `<object>`, `<embed>`, `<details>`, `<summary>`, `<dialog>`). Single-table is now enough to opt out (previously needed nested tables -- harmless tightening, single `<table>` already isn't TipTap content). * Tighten the custom-attributes regex with a leading `[\s"']` boundary so query strings like `<a href="…?id=…">` no longer false-match as an HTML `id=` attribute. * `<style>` tags, `@media` queries, and the class allowlist (`prose`, `variable-`, `email-image`, `ProseMirror`, `resizable-image`, `selected`, `resize-handle`) are unchanged. Mirrors the same logic in apps/api/src/services/EmailService.ts so the server-side wrap decision in `EmailService.compile()` stays in lockstep with the client-side editor-mode decision. Side-effect on `wrapEmailWithStyles` / `EmailService.compile`: templates that previously kept their own (unwrapped) shell because they contained a colored `<span>` or an inline-styled `<a>` will now flow through the prose wrapper. This is the correct behavior -- those templates ARE visual-editor output and SHOULD get the same wrapper the preview modal applies. Tests: new vitest suite at apps/web/src/lib/__tests__/emailStyles.test.ts covers 24 cases including the TipTap-export artifacts above, the href-URL-with-id false-match, and the rejected-element set.
Plunk
The Open-Source Email Platform
Introduction
Transactional emails, marketing campaigns, and workflow automation — in one platform. Self-hostable, $0.001 per email, no contact limits.
An open-source, self-hosted alternative to tools SendGrid, Resend or Mailgun.
Features
- Transactional Emails: Send emails straight from your API with template support and variable substitution
- SMTP: Use Plunk as an SMTP relay to send emails from any existing tool or framework
- Campaigns: Send newsletters and product updates to large audiences
- Workflows: Create advanced automations with triggers, delays, and conditional logic
- Segments: Organize contacts with dynamic filtering and target the right audience
- Contact Management: Manage contacts with custom fields and full activity history
- Analytics: Track opens, clicks, bounces, and engagement metrics in real-time
- Custom Domains: Verify and send from your own domains with DKIM/SPF support
- Inbound Emails: Receive and process incoming emails with custom routing rules
Sponsors
Plunk is made possible by the support of our sponsors. If you self-host Plunk, consider supporting via GitHub Sponsors.
Self-hosting Plunk
The easiest way to self-host Plunk is by using the plunk Docker image.
You can pull the latest image from Github.
A complete guide on how to deploy Plunk can be found in the documentation.
Community
- Documentation: docs.useplunk.com
- Discord: useplunk.com/discord
Contributing
You are welcome to contribute to Plunk. You can find a guide on how to contribute in CONTRIBUTING.md.
License
AGPL-3.0 License - see LICENSE for details.
Languages
TypeScript
94.1%
MDX
4.7%
Shell
0.4%
Dockerfile
0.4%
CSS
0.2%
Other
0.2%