diff --git a/lib/mailer.js b/lib/mailer.js index 2838781..04f703b 100644 --- a/lib/mailer.js +++ b/lib/mailer.js @@ -62,12 +62,22 @@ async function verify() { } // Minimal HTML rendering of a plain-text body: escape, then turn newlines into -//
. Sending multipart/alternative (text + html) reads as normal personal -// mail and avoids the "text-only, no html" heuristic some filters apply. +// explicit
tags. Sending multipart/alternative (text + html) reads as +// normal personal mail and avoids the "text-only, no html" heuristic some +// filters apply. +// +// The line breaks MUST be real
tags, not merely CSS white-space:pre-wrap +// on literal newlines: pre-wrap renders correctly on first receipt, but when +// the recipient's client (e.g. Outlook) quotes the message back to us it +// re-parses the HTML, drops the pre-wrap style and collapses the bare newlines +// into spaces — turning our carefully formatted reply into a run-on wall of +// text in the quoted thread. Explicit
survives that round-trip. function textToHtml(text) { const esc = String(text || '') - .replace(/&/g, '&').replace(//g, '>'); - return `
${esc}
`; + .replace(/&/g, '&').replace(//g, '>') + .replace(/\r\n?/g, '\n') + .replace(/\n/g, '
\n'); + return `
${esc}
`; } // Send one message via authenticated submission. Returns nodemailer info