managarten/apps
Till JS 3ac32d9f3e feat(broadcast): M3 email HTML render + desktop/mobile/text preview
Step 3 of the compose wizard (Preflight) now shows a real preview +
pre-send checks. User sees exactly what the recipient gets, minus the
substituted tracking URLs.

Render (render/email-html.ts)
- renderEmailHtml(): wraps Tiptap HTML in an email-client-compatible
  shell — inline style="" throughout, single-cell table layout,
  preheader block (hidden from body, visible in inbox preview), sender
  banner, footer with unsubscribe + legal address
- HTML-escapes subject / sender name / address (Tiptap body stays
  verbatim since its schema already forbids <script> / on* handlers)
- Placeholders for {{unsubscribe}} / {{web_view}} URLs — mana-mail's
  orchestrator substitutes the HMAC-signed per-recipient URL at send
  time; preview uses #unsubscribe-preview so nothing 404s

Render (render/plain-text.ts)
- renderPlainText(): mandatory multipart/alternative counterpart that
  keeps SpamAssassin happy. Soft-wraps at 72 chars (honours the old
  78-char RFC limit with a bit of quote-prefix headroom). Same footer
  signals as HTML so the "why am I getting this" info travels with
  text-only clients too.

Preview (preview/)
- EmailPreview.svelte: iframe with srcdoc (no blob-URL revocation
  overhead) + macOS-style device chrome, width 640/375 for desktop/
  mobile. sandbox="allow-same-origin" only — no top navigation, no
  script execution.
- PreviewTabs.svelte: Desktop / Mobile / Nur-Text switcher, derives
  html + plaintext reactively so edits in step 2 reflect immediately
  when the user hops back to step 3.

Preflight checks (ComposeView step 3)
- Subject set? Audience > 0? Sender valid? Legal address filled?
- Legal-address warning is actionable — links straight to
  /broadcasts/settings. Unblocks the user instead of just scolding.

Tests (20 new, 57 total for broadcast)
- email-html: subject in title, XSS-escape, preheader hidden, body
  verbatim, empty-body fallback, sender name, unsubscribe default vs
  override, legal address line breaks, optional footer, web-view link
- plain-text: sender, body, 72-char wrapping, paragraph preservation,
  unsubscribe placeholder vs override, legal at end, optional footer

Still client-side only; server-side juice inlining lands in M4 when we
add richer CSS (currently everything inline already, so juice just
passes through).

Plan: docs/plans/broadcast-module.md §M3.
Next: M4 bulk-send via mana-mail (synchronous loop, HMAC tokens).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:48:14 +02:00
..
api feat(auth): server-side tier gating via requireTier middleware 2026-04-19 17:38:06 +02:00
calc/packages/shared chore: delete 25 web-archived directories, remove stale stubs, clean workspace config 2026-04-03 13:03:49 +02:00
calendar refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
cards chore(mobile): remove 6 of 7 mobile apps — keep only memoro 2026-04-20 15:31:47 +02:00
chat chore(mobile): remove 6 of 7 mobile apps — keep only memoro 2026-04-20 15:31:47 +02:00
citycorners chore: complete ManaCore → Mana rename (docs, go modules, plists, images) 2026-04-07 12:26:10 +02:00
contacts refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
context chore(mobile): remove 6 of 7 mobile apps — keep only memoro 2026-04-20 15:31:47 +02:00
docs feat(ai): Mission Grant rollout gating — flag, alerts, runbook, user docs 2026-04-15 14:02:47 +02:00
food refactor: rename nutriphi module to food (Essen) 2026-04-14 15:30:07 +02:00
guides refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
inventory refactor(mana): rename inventar → inventory across the codebase 2026-04-09 15:50:24 +02:00
mana feat(broadcast): M3 email HTML render + desktop/mobile/text preview 2026-04-20 20:48:14 +02:00
manavoxel fix(type-check): clear the last five failures — monorepo type-check is now 76/76 green 2026-04-20 15:53:07 +02:00
memoro chore: remove abandoned per-product workspace artifacts 2026-04-09 11:56:51 +02:00
moodlit refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
mukke feat: rename ManaCore to Mana across entire codebase 2026-04-05 20:00:13 +02:00
news refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
photos refactor: rename nutriphi module to food (Essen) 2026-04-14 15:30:07 +02:00
picture chore(mobile): remove 6 of 7 mobile apps — keep only memoro 2026-04-20 15:31:47 +02:00
plants refactor: rename planta → plants, clean up codebase 2026-04-12 18:59:44 +02:00
presi fix(presi): wire up db:push for presi schema via @mana/api 2026-04-12 14:32:44 +02:00
questions refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
quotes/packages/content fix(mana-llm): google-genai v1.73 keyword-only Part.from_text() 2026-04-16 12:47:23 +02:00
skilltree chore: delete 25 web-archived directories, remove stale stubs, clean workspace config 2026-04-03 13:03:49 +02:00
storage refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
times chore: complete ManaCore → Mana rename (docs, go modules, plists, images) 2026-04-07 12:26:10 +02:00
todo refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
traces chore(mobile): remove 6 of 7 mobile apps — keep only memoro 2026-04-20 15:31:47 +02:00
uload fix(type-check): clear the last five failures — monorepo type-check is now 76/76 green 2026-04-20 15:53:07 +02:00