feat: add setting to hide inline images from attachment list

This commit is contained in:
Linus Rath
2026-04-18 01:07:34 +02:00
parent d4f7ae522e
commit 1689315c3a
18 changed files with 96 additions and 12 deletions
+15 -9
View File
@@ -896,6 +896,7 @@ export function EmailViewer({
const showToolbarLabels = useSettingsStore((state) => state.showToolbarLabels);
const mailLayout = useSettingsStore((state) => state.mailLayout);
const calendarInvitationParsingEnabled = useSettingsStore((state) => state.calendarInvitationParsingEnabled);
const hideInlineImageAttachments = useSettingsStore((state) => state.hideInlineImageAttachments);
const timeFormat = useSettingsStore((state) => state.timeFormat);
const isFocusedMailLayout = mailLayout === 'focus';
@@ -2074,14 +2075,16 @@ export function EmailViewer({
const effectiveAttachments = useMemo<EffectiveAttachment[]>(() => {
if (smimeDecryptedAttachments.length > 0) {
return smimeDecryptedAttachments.map((attachment, index) => ({
id: `smime-${index}-${attachment.filename || attachment.mimeType}`,
name: attachment.filename,
type: attachment.mimeType || 'application/octet-stream',
size: getPostalMimeAttachmentSize(attachment),
cid: attachment.contentId,
decryptedAttachment: attachment,
}));
return smimeDecryptedAttachments
.filter(att => !(hideInlineImageAttachments && att.contentId && (att.mimeType || '').startsWith('image/')))
.map((attachment, index) => ({
id: `smime-${index}-${attachment.filename || attachment.mimeType}`,
name: attachment.filename,
type: attachment.mimeType || 'application/octet-stream',
size: getPostalMimeAttachmentSize(attachment),
cid: attachment.contentId,
decryptedAttachment: attachment,
}));
}
const hasCalInvitation = calendarInvitationParsingEnabled && !!email && !!findCalendarAttachment(email);
@@ -2093,6 +2096,9 @@ export function EmailViewer({
// Hide calendar MIME parts (text/calendar, application/ics) when the invitation
// banner is shown - prevents raw ICS files appearing as spurious attachments.
.filter(att => !hasCalInvitation || !isCalendarMimeType(att.type))
// Hide inline cid-referenced images when the user has opted to keep them
// out of the attachment list (default on): these are embedded in the body.
.filter(att => !(hideInlineImageAttachments && att.cid && att.disposition === 'inline' && (att.type || '').startsWith('image/')))
.map((attachment, index) => ({
id: attachment.blobId || `${attachment.name || 'attachment'}-${index}`,
name: attachment.name || null,
@@ -2123,7 +2129,7 @@ export function EmailViewer({
}));
return [...jmapAttachments, ...tnefExtracted, ...embeddedExtracted];
}, [email?.attachments, smimeDecryptedAttachments, tnefHtml, tnefText, tnefAttachments, embeddedEmailUnwrapped, embeddedEmailAttachments, calendarInvitationParsingEnabled]);
}, [email?.attachments, smimeDecryptedAttachments, tnefHtml, tnefText, tnefAttachments, embeddedEmailUnwrapped, embeddedEmailAttachments, calendarInvitationParsingEnabled, hideInlineImageAttachments]);
// Generate email source for viewing
const generateEmailSource = (email: Email): string => {
@@ -237,6 +237,7 @@ function EmailCard({
const resolvedTheme = useThemeStore((state) => state.resolvedTheme);
const density = useSettingsStore((state) => state.density);
const mailAttachmentAction = useSettingsStore((state) => state.mailAttachmentAction);
const hideInlineImageAttachments = useSettingsStore((state) => state.hideInlineImageAttachments);
const emailAlwaysLightMode = useSettingsStore((state) => state.emailAlwaysLightMode);
const sender = email.from?.[0];
const isUnread = !email.keywords?.$seen;
@@ -544,10 +545,14 @@ function EmailCard({
</div>
{/* Attachments */}
{email.attachments && email.attachments.length > 0 && (
{(() => {
const visibleAttachments = (email.attachments ?? []).filter(
att => !(hideInlineImageAttachments && att.cid && att.disposition === 'inline' && (att.type || '').startsWith('image/'))
);
return visibleAttachments.length > 0 && (
<div className="px-4 pb-4">
<div className="flex flex-wrap gap-2">
{email.attachments.map((attachment, idx) => {
{visibleAttachments.map((attachment, idx) => {
const Icon = getFileIcon(attachment.name, attachment.type);
const isPreviewable = isFilePreviewable(attachment.name, attachment.type);
const opensPreview = isPreviewable && mailAttachmentAction === 'preview';
@@ -576,7 +581,8 @@ function EmailCard({
})}
</div>
</div>
)}
);
})()}
{/* Action Buttons */}
<div className="px-4 pb-4 flex gap-2">