mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
fix(x): stop clipping the last line of short email bodies
A message body whose first block has a top margin (a bare <p>, most Outlook mail) rendered with its final line cut off. The margin collapses through <body>, so body.scrollHeight under-reports by the margin height while the content's real bottom sits lower — and that short height is what we set on the iframe. Measured in Electron: a two-line reply reported 63px against a 73px content bottom, losing 10px. display: flow-root on <body> contains the margin (the existing rule only reset the *last* child's bottom margin). Pre-existing; not caused by quote collapsing. Also restore <body> attributes when re-serializing a prepared body. prepareEmailHtml joined head.innerHTML + body.innerHTML, dropping the <body> tag — and with it the bgcolor/style that 431 of 584 local messages rely on for their background (the host parser merges a nested <body>'s attributes onto its own). Serialize documentElement.innerHTML instead so the tag and its attributes survive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
992c1883a7
commit
0e3918f32a
3 changed files with 18 additions and 4 deletions
|
|
@ -312,6 +312,10 @@ function buildEmailDocument(
|
|||
overflow-y: hidden;
|
||||
word-wrap: break-word;
|
||||
padding-bottom: 4px;
|
||||
/* Contain the first child's top margin. Without this it collapses through
|
||||
<body>, shifting the box down while body.scrollHeight stays short — so
|
||||
the height we hand the iframe cuts the last line off. */
|
||||
display: flow-root;
|
||||
}
|
||||
body > *:last-child { margin-bottom: 0; }
|
||||
img { max-width: 100%; height: auto; }
|
||||
|
|
|
|||
|
|
@ -79,6 +79,14 @@ describe('prepareEmailHtml — quote detection', () => {
|
|||
expect(html).toContain('.a{color:red}')
|
||||
})
|
||||
|
||||
it('preserves <body> attributes, which carry the email background', () => {
|
||||
// Embedded in the host <body>, the parser merges these onto it. Dropping
|
||||
// the tag would strip the background off every styled newsletter.
|
||||
const { html } = prepareEmailHtml('<html><body bgcolor="#f4f4f4" style="margin:0"><p>Hi</p></body></html>')
|
||||
expect(html).toContain('bgcolor="#f4f4f4"')
|
||||
expect(html).toContain('margin:0')
|
||||
})
|
||||
|
||||
it('ignores quoted content when deciding whether the email is styled', () => {
|
||||
// The table lives only in the quote, so the body should still adapt to theme.
|
||||
const { styled } = prepareEmailHtml(
|
||||
|
|
|
|||
|
|
@ -143,10 +143,12 @@ export function prepareEmailHtml(rawHtml: string): PreparedEmail {
|
|||
const doc = new DOMParser().parseFromString(rawHtml, 'text/html')
|
||||
const hasQuote = markQuotedNodes(doc) && unmarkIfNothingRemains(doc)
|
||||
const styled = isStyledDocument(doc)
|
||||
// Emails ship <style> blocks that the parser hoists into <head>. Only the
|
||||
// body gets re-embedded downstream, so carry the head along with it — a
|
||||
// <style> is just as valid inside <body>.
|
||||
const html = (doc.head?.innerHTML || '') + (doc.body?.innerHTML || '')
|
||||
// Serialize <head> and <body> as *elements*, not just their contents. Most
|
||||
// emails carry a <body bgcolor=… style=…>, and when this HTML is embedded
|
||||
// inside the host <body>, the parser merges those attributes onto it — which
|
||||
// is how backgrounds have always applied. Concatenating innerHTML would drop
|
||||
// the tag and with it the attributes.
|
||||
const html = doc.documentElement?.innerHTML || doc.body?.innerHTML || ''
|
||||
return { html, hasQuote, styled }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue