feat(webmail): conversations - list, stacked reader, inline reply, settings switch

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Sonnet 5.5 committed 2026-10-22 16:46:11 -03:00
1 parent 86a69d1a96
commit 2080489319
14 files changed
+1225 -39

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
import { sanitizeEmailHtml, blockRemoteImages, unblockRemoteImages } from "./sanitizer";
import { resolveInlineImages, buildReaderDocument, type InlineImages } from "./emailBody";
/** What it takes to draw a message's body. */
export interface RenderableBody {
html: string;
inline_images?: InlineImages;
}
/**
* The document the reader frame renders, or null for a message with no HTML.
*
* Order matters: sanitise first so nothing downstream handles untrusted
* markup, then resolve the message's own inline parts, then block or restore
* remote sources - which is the only part the reader chooses.
*/
export function messageDocument(message: RenderableBody, showImages: boolean): string | null {
if (!message.html) return null;
const sanitized = sanitizeEmailHtml(message.html);
if (!sanitized) return null;
const withInline = resolveInlineImages(sanitized, message.inline_images ?? {});
const body = showImages ? unblockRemoteImages(withInline) : blockRemoteImages(withInline);
return buildReaderDocument(body);
}
/**
* Whether anything was actually held back, so the privacy bar is not shown over
* a message that has no remote content at all.
*/
export function hasBlockedRemoteContent(message: RenderableBody): boolean {
if (!message.html) return false;
const sanitized = resolveInlineImages(sanitizeEmailHtml(message.html), message.inline_images ?? {});
return blockRemoteImages(sanitized) !== sanitized;
}