/** Render plain-text-only messages with clickable links and readable line breaks. */ const URL_RE = /https?:\/\/[^\s<>"')\]]+/gi function escapeHtml(text: string): string { return text .replace(/&/g, "&") .replace(//g, ">") } function linkifyEscapedText(text: string): string { let out = "" let last = 0 for (const match of text.matchAll(URL_RE)) { const index = match.index ?? 0 out += text.slice(last, index) const url = match[0] out += `${url}` last = index + url.length } out += text.slice(last) return out } /** Insert breaks before common transactional-mail markers when the body is one long line. */ function softenPlainTextWall(text: string): string { if (text.includes("\n")) return text return text .replace(/,\s*(Cher(?:e)?\s)/gi, ",\n\n$1") .replace(/,\s*(Le \d{2}\/\d{2}\/\d{4})/g, ",\n\n$1") .replace(/\.\s+(Nous restons)/g, ".\n\n$1") .replace(/\.\s+(L['’]équipe)/g, ".\n\n$1") .replace(/\.\s+(Pour obtenir)/g, ".\n\n$1") .replace(/\.\s+(Vous pouvez)/g, ".\n\n$1") } export function plainTextToDisplayHtml(text: string): string { const normalized = softenPlainTextWall(text.trim()) const escaped = escapeHtml(normalized) const linked = linkifyEscapedText(escaped) return `
${linked}
` }