mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(gmail): stop wrapping draft/send HTML body in per-paragraph <p> tags (#5479)
* fix(gmail): stop wrapping draft/send HTML body in per-paragraph <p> tags * fix(gmail): use <br> instead of CSS white-space for line breaks white-space: pre-wrap has inconsistent email-client support (including Gmail for non-Google accounts per caniemail.com); <br> is the client-agnostic standard for plain-text-to-HTML line breaks.
This commit is contained in:
@@ -91,10 +91,10 @@ describe('escapeHtml', () => {
|
||||
})
|
||||
|
||||
describe('plainTextToHtml', () => {
|
||||
it('renders blank lines as paragraph breaks and single newlines as <br>', () => {
|
||||
it('converts newlines to <br> without paragraph margins', () => {
|
||||
const html = plainTextToHtml('Hi Janice,\n\nHope you are well.\nSecond line.')
|
||||
expect(html).toContain('<p>Hi Janice,</p>')
|
||||
expect(html).toContain('<p>Hope you are well.<br>Second line.</p>')
|
||||
expect(html).not.toContain('<p>')
|
||||
expect(html).toContain('Hi Janice,<br><br>Hope you are well.<br>Second line.')
|
||||
})
|
||||
|
||||
it('escapes HTML in the source text', () => {
|
||||
@@ -150,7 +150,7 @@ describe('buildSimpleEmailMessage', () => {
|
||||
expect(plainIdx).toBeGreaterThan(-1)
|
||||
expect(htmlIdx).toBeGreaterThan(plainIdx)
|
||||
expect(decodePart(decoded, 'text/plain')).toBe('Hi Janice,\n\nQuick question.')
|
||||
expect(decodePart(decoded, 'text/html')).toContain('<p>Hi Janice,</p>')
|
||||
expect(decodePart(decoded, 'text/html')).toContain('Hi Janice,<br><br>Quick question.')
|
||||
})
|
||||
|
||||
it('encodes bodies as base64 so UTF-8 (emoji, accents) round-trips cleanly', () => {
|
||||
@@ -242,7 +242,7 @@ describe('buildMimeMessage', () => {
|
||||
expect(message).toMatch(/Content-Type: multipart\/alternative; boundary="([^"]+)"/)
|
||||
expect(message).toContain('Content-Disposition: attachment; filename="note.txt"')
|
||||
expect(decodePart(message, 'text/plain')).toBe('Hello')
|
||||
expect(decodePart(message, 'text/html')).toContain('<p>Hello</p>')
|
||||
expect(decodePart(message, 'text/html')).toContain('Hello')
|
||||
})
|
||||
|
||||
it('emits multipart/alternative without multipart/mixed when no attachments', () => {
|
||||
|
||||
@@ -339,17 +339,18 @@ export function escapeHtml(value: string): string {
|
||||
|
||||
/**
|
||||
* Convert a plain-text body to an HTML body that flows naturally in Gmail.
|
||||
* Blank lines become paragraph breaks; single newlines become `<br>`.
|
||||
* Newlines become `<br>` rather than per-paragraph `<p>` tags or a CSS
|
||||
* `white-space` rule — `<p>` margins are what Gmail's "Remove formatting"
|
||||
* button strips, and `white-space` has inconsistent support across email
|
||||
* clients (including Gmail for non-Google accounts). Plain `<br>` line
|
||||
* breaks are the standard, client-agnostic way to preserve plain-text
|
||||
* formatting in an HTML alternative part.
|
||||
* This avoids the narrow hard-wrapped rendering Gmail uses for `text/plain`.
|
||||
*/
|
||||
export function plainTextToHtml(body: string): string {
|
||||
const normalized = body.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
|
||||
const paragraphs = normalized.split(/\n{2,}/)
|
||||
const htmlParagraphs = paragraphs.map((paragraph) => {
|
||||
const escaped = escapeHtml(paragraph).replace(/\n/g, '<br>')
|
||||
return `<p>${escaped}</p>`
|
||||
})
|
||||
return `<!DOCTYPE html><html><body>${htmlParagraphs.join('')}</body></html>`
|
||||
const escaped = escapeHtml(normalized).replace(/\n/g, '<br>')
|
||||
return `<!DOCTYPE html><html><body>${escaped}</body></html>`
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user