function MessagePane({ msg, msgTags, attachments, token, onToggleUnread, onStar, onArchive, onUnarchive, onDelete, onReply, archived, busy, account, onBack, }) { const { t, lang } = useI18n(); const [reply, setReply] = React.useState(""); React.useEffect(() => { setReply(""); }, [msg?.id]); if (!msg) { return (
{t("select_message")}
); } const rawHtml = decodeBodyHtml(msg.body_html); const html = rawHtml || "
" + (msg.body_text || "") + "
"; // Senders style HTML mail for a white page. On the dark theme that means // dark text on a dark background, so real HTML mail gets its own light // surface; plain text keeps following the UI theme. const bodyClass = rawHtml ? " prose-mail--html" : ""; const btn = "text-xs px-2.5 py-1 rounded border border-paper-line text-ink-muted hover:border-accent hover:text-accent"; const chips = (msgTags || []).filter((t) => t !== "archive"); return (
{onBack && ( )}

{msg.subject}

{t("from")} {msg.from_addr} {t("to")} {msg.to_addr} {formatWhen(msg.created_at, lang)}
{chips.length > 0 && (
{chips.map((t) => ( #{t} ))}
)} {(attachments || []).length > 0 && (
{t("attachments")} {attachments.map((a) => { const size = a.bytes ? " (" + fmtBytes(a.bytes) + ")" : ""; // Outbound copies keep the name and size but not the bytes, so // there is deliberately nothing to link to. if (a.stored === false || !a.download_url) { return ( {(a.filename || t("attachment")) + size} · {t("not_stored")} ); } return ( {(a.filename || t("attachment")) + size} ); })}
)}
{archived ? ( ) : ( )}
{msg.direction !== "out" && (
{ e.preventDefault(); if (!reply.trim() || busy) return; onReply(reply.trim()).then(() => setReply("")); }} >
{t("from")} {account?.address || msg.received_for || msg.to_addr || "—"}