/**
* Turns a message's sanitized HTML into something a frame can actually show.
*
* Two things were missing and both looked like "images are broken": a message
* written by any normal mail client references its own pictures as
* src="cid:...", which resolves against nothing in a browser; and the frame was
* handed a bare fragment with no document around it, so mail written for a
* white page rendered as dark text on a dark surface with no width limit.
*/
/** The parts the API returns alongside the body, keyed by Content-ID. */
export type InlineImages = Record;
/**
* Replaces src="cid:x" with the data: URI the server delivered for that part.
*
* A reference with no matching part loses its source entirely rather than
* keeping a cid: the browser cannot fetch: an unresolvable src draws a
* broken-image icon, while no src at all falls back to the alt text.
*/
export function resolveInlineImages(html: string, images: InlineImages): string {
if (!html) return "";
return html.replace(
/(
]*?)\ssrc\s*=\s*("|')cid:([^"']*)\2/gi,
(_match, head: string, _quote: string, reference: string) => {
// Clients disagree about whether the reference keeps the angle brackets
// the Content-ID header carries, so both spellings resolve.
const key = decodeURIComponent(reference.trim()).replace(/^<|>$/g, "");
const resolved = images[key];
return resolved ? `${head} src="${resolved}"` : head;
},
);
}
/**
* Wraps a message body in the document the reader frame renders.
*
* The frame is sandboxed with an opaque origin, so it cannot inherit anything
* from the app: every style, the light surface mail assumes, and the height
* report the host page sizes the frame from all have to travel inside it.
*/
export function buildReaderDocument(bodyHtml: string): string {
return `
${bodyHtml}
`;
}