From b1db100c3a4ca0616688347506eda8c719ff3e1e Mon Sep 17 00:00:00 2001 From: Linus Rath Date: Wed, 18 Mar 2026 14:53:08 +0100 Subject: [PATCH] feat: enhance email body handling by prioritizing textBody for minimal HTML and expanding JMAPClient properties --- components/email/email-viewer.tsx | 11 ++++++++++- components/email/thread-conversation-view.tsx | 11 ++++++++++- lib/jmap/client.ts | 11 ++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 0193011f..d7c0b47b 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -1925,7 +1925,16 @@ export function EmailViewer({ if (email.htmlBody?.[0]?.partId && email.bodyValues[email.htmlBody[0].partId]) { htmlContent = email.bodyValues[email.htmlBody[0].partId].value; - useHtmlVersion = !!htmlContent; + // Prefer textBody when HTML is auto-generated minimal wrapper (no rich formatting). + // Server-generated HTML from text/plain emails often lacks
tags, collapsing newlines. + const hasTextBody = email.textBody?.[0]?.partId && email.bodyValues[email.textBody[0].partId]; + if (hasTextBody && htmlContent) { + const stripped = htmlContent.replace(/<\/?(html|head|body|meta|!doctype|!DOCTYPE|br\s*\/?)[^>]*>/gi, '').trim(); + const hasRichContent = /<(table|tr|td|th|img|style|link|div\s+[^>]*class|span\s+[^>]*class|font|center|blockquote|ul|ol|li|h[1-6])\b/i.test(stripped); + useHtmlVersion = hasRichContent; + } else { + useHtmlVersion = !!htmlContent; + } } // If we should use HTML version and it exists diff --git a/components/email/thread-conversation-view.tsx b/components/email/thread-conversation-view.tsx index 572bd793..13aab9ef 100644 --- a/components/email/thread-conversation-view.tsx +++ b/components/email/thread-conversation-view.tsx @@ -316,7 +316,16 @@ function EmailCard({ if (email.htmlBody?.[0]?.partId && email.bodyValues[email.htmlBody[0].partId]) { htmlContent = email.bodyValues[email.htmlBody[0].partId].value; - useHtmlVersion = !!htmlContent; + // Prefer textBody when HTML is auto-generated minimal wrapper (no rich formatting). + // Server-generated HTML from text/plain emails often lacks
tags, collapsing newlines. + const hasTextBody = email.textBody?.[0]?.partId && email.bodyValues[email.textBody[0].partId]; + if (hasTextBody && htmlContent) { + const stripped = htmlContent.replace(/<\/?(html|head|body|meta|!doctype|!DOCTYPE|br\s*\/?)[^>]*>/gi, '').trim(); + const hasRichContent = /<(table|tr|td|th|img|style|link|div\s+[^>]*class|span\s+[^>]*class|font|center|blockquote|ul|ol|li|h[1-6])\b/i.test(stripped); + useHtmlVersion = hasRichContent; + } else { + useHtmlVersion = !!htmlContent; + } } if (useHtmlVersion && htmlContent) { diff --git a/lib/jmap/client.ts b/lib/jmap/client.ts index 02da6f39..afc91070 100644 --- a/lib/jmap/client.ts +++ b/lib/jmap/client.ts @@ -1088,7 +1088,16 @@ export class JMAPClient { ["Email/get", { accountId: targetAccountId, ids: thread.emailIds, - properties: [...EMAIL_LIST_PROPERTIES], + properties: [ + ...EMAIL_LIST_PROPERTIES, + "textBody", "htmlBody", "bodyValues", + "attachments", "blobId", "sentAt", "bcc", "replyTo", + "messageId", "inReplyTo", "references", "headers", "bodyStructure", + ], + fetchTextBodyValues: true, + fetchHTMLBodyValues: true, + fetchAllBodyValues: true, + maxBodyValueBytes: 256000, }, "0"], ]);