@@ -12,6 +12,14 @@ import { CliError, handleError } from '../utils/errors';
1212import { readStdin , resolveProfileName } from '../utils/stdin' ;
1313import type { GmailAttachment } from '../types/gmail' ;
1414
15+ function escapeHtml ( text : string ) : string {
16+ return text
17+ . replace ( / & / g, '&' )
18+ . replace ( / < / g, '<' )
19+ . replace ( / > / g, '>' )
20+ . replace ( / " / g, '"' ) ;
21+ }
22+
1523async function getGmailClient ( profileName ?: string ) : Promise < { client : GmailClient ; profile : string } > {
1624 const { tokens, profile } = await getValidTokens ( 'gmail' , profileName ) ;
1725 const auth = createGoogleAuth ( tokens ) ;
@@ -288,30 +296,33 @@ Query Syntax Examples:
288296 const { client } = await getGmailClient ( options . profile ) ;
289297 const message = await client . get ( messageId , 'html' ) ;
290298
291- // Build HTML document with email metadata
292- const html = `<!DOCTYPE html>
299+ // Build HTML document - inject header before body content
300+ const emailHeader = `
301+ <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 16px 20px; margin-bottom: 16px; border-bottom: 1px solid #ddd; background: #f9f9f9;">
302+ <div style="font-size: 1.3em; font-weight: 600; margin-bottom: 12px;">${ escapeHtml ( message . subject ) } </div>
303+ <div style="margin: 4px 0; font-size: 0.9em;"><strong>From:</strong> ${ escapeHtml ( message . from ) } </div>
304+ <div style="margin: 4px 0; font-size: 0.9em;"><strong>To:</strong> ${ escapeHtml ( message . to . join ( ', ' ) ) } </div>
305+ <div style="margin: 4px 0; font-size: 0.9em;"><strong>Date:</strong> ${ escapeHtml ( message . date ) } </div>
306+ </div>` ;
307+
308+ let html : string ;
309+ const body = message . body || '' ;
310+
311+ // Check if body is already a full HTML document
312+ if ( body . trim ( ) . toLowerCase ( ) . startsWith ( '<!doctype' ) || body . trim ( ) . toLowerCase ( ) . startsWith ( '<html' ) ) {
313+ // Inject header after <body> tag
314+ html = body . replace ( / < b o d y [ ^ > ] * > / i, ( match ) => `${ match } ${ emailHeader } ` ) ;
315+ } else {
316+ // Wrap fragment in minimal HTML
317+ html = `<!DOCTYPE html>
293318<html>
294- <head>
295- <meta charset="utf-8">
296- <style>
297- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; line-height: 1.5; }
298- .header { border-bottom: 1px solid #ddd; padding-bottom: 16px; margin-bottom: 24px; }
299- .header-field { margin: 4px 0; }
300- .header-label { font-weight: 600; color: #555; }
301- .subject { font-size: 1.4em; font-weight: 600; margin-bottom: 16px; }
302- .body { white-space: pre-wrap; }
303- </style>
304- </head>
319+ <head><meta charset="utf-8"></head>
305320<body>
306- <div class="header">
307- <div class="subject">${ escapeHtml ( message . subject ) } </div>
308- <div class="header-field"><span class="header-label">From:</span> ${ escapeHtml ( message . from ) } </div>
309- <div class="header-field"><span class="header-label">To:</span> ${ escapeHtml ( message . to ) } </div>
310- <div class="header-field"><span class="header-label">Date:</span> ${ escapeHtml ( message . date ) } </div>
311- </div>
312- <div class="body">${ message . body } </div>
321+ ${ emailHeader }
322+ <div style="padding: 0 20px;">${ body } </div>
313323</body>
314324</html>` ;
325+ }
315326
316327 // Launch browser and generate PDF
317328 console . error ( 'Launching browser...' ) ;
0 commit comments