Skip to content

rip email: a preview CLI for rip/ui/email templates - #301

Merged
shreeve merged 8 commits into
mainfrom
email-preview
Sep 12, 2026
Merged

shreeve merged 8 commits into
mainfrom
email-preview

Conversation

@philiplindberg

@philiplindberg philiplindberg commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

A small CLI for the rip ui/email package. rip email dev, run from an emails directory or pointed at one (medlabs/api/emails, say), serves a live preview of every template as a Rip Site under the edge at https://email.local/, with desktop and mobile widths, a Gmail-style dark mode, and the text and source views. A template is one component per file with a subject static and previewProps.

Three changes touch rendered email itself. Body carries its background on the body element and zeroes that element's margin always, and its padding whenever the style sets one. Container and Section move any padding in their style from the table onto the cell, where Outlook and Klaviyo honor it; everything else stays on the table. A template that styles padding on Container or Section renders that padding on the td. The README's "Where a style lands" section says so, with the split shown.

How it works: the launcher writes a transient project whose app is the package's preview page and whose entry is the package's API, and hands it to the Sites manager, so the page gets the hub and hot reload for free. The launcher owns the one watcher on the template directory and publishes a stamp on the site's hub when a save settles; the page subscribes and refetches. Ready means the edge holds the site's registration. --host takes another name in a family that resolves here (.local, .via.rip, .localhost). rip email export writes html, and with --text the text twin, per template. bin/rip dispatches email through the sub-command runner so signals reach the launcher, and rip/sites exports edge for the control socket.

rip email dev runs the preview as a Rip Site under the edge: a transient project whose app is the package's preview page and whose entry is the package's API, managed by the Sites manager so the page gets the hub and hot reload for free. The launcher owns the one watcher on the template directory and publishes a stamp on the site's hub when a save settles; the page subscribes and refetches the catalog and the open message. Ready means the edge holds the site's registration. --host takes another name in a family that resolves here (.local, .via.rip, .localhost). rip email export writes html, and with --text the text twin, per template.

A template file exports one component and is addressed by its path without the extension; underscore-led files are shared. Component.previewProps chooses the props, Component.subject and Component.from answer the header, and the Preview block's text is the inbox line.

The preview page follows react-email's chrome: HTML, text, and source views, desktop and mobile viewports, and a dark mode that inverts colors the way Gmail does. The inbox row above the sheet shows the sender's name, subject, and preview line.

Email components: Section and Container split padding onto the cell, Body paints its background on the body element, and toEmail answers the preview text beside html and text. bin/rip dispatches email through the sub-command runner so signals reach the launcher; rip/sites exports edge for the control socket.
The sender is the mailer's, not a template's, so the from static is gone: a template answers its subject, and the inbox row shows the subject over the preview line.
A client's default body margin is not zero, and only a template that happened to set one had it zeroed. The body element now carries margin:0 always, and padding:0 whenever the template's style sets a padding, which the cell inside carries.
Ready waited for a registration named email on the host, which another run's registration matches exactly, so a second launcher greeted the first one's site and then watched its own manager refuse the claim. It now looks for a registration whose dist root lies under its own transient project, which no other run can carry.
The chrome's tokens are Tailwind neutral steps in a stylesheet shaped like medlabs's: the palette on :root, mapped into Tailwind's text, background, and border namespaces in @theme, with the body's defaults in the base layer and Inter loaded from Google Fonts. The sidebar and header follow shadcn's sidebar, with bordered segmented controls and no colored accent. The sheet is as tall as the email, centered on a neutral canvas, 800 wide for desktop and 390 for mobile, and follows its document's height through a resize observer. The Source view indents the html for reading; what is sent stays the serializer's one line.
The Sites manager announces its host on stdout once registered, and the launcher inherited that stream, so the URL printed twice and the announcement's newline pushed Ready off the spinner's line. The launcher now pipes the manager's stdout and forwards everything but that one line.
The Sites manager now speaks as rip-sites, so the line the launcher drops from its stdout is spelled that way.
@shreeve
shreeve merged commit fdea0f0 into main Sep 12, 2026
2 checks passed
@shreeve
shreeve deleted the email-preview branch September 12, 2026 02:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants