diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 50198d82..4b54c122 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -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(() => { 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 => { diff --git a/components/email/thread-conversation-view.tsx b/components/email/thread-conversation-view.tsx index 9519a600..48fe735c 100644 --- a/components/email/thread-conversation-view.tsx +++ b/components/email/thread-conversation-view.tsx @@ -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({ {/* 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 && (
- {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({ })}
- )} + ); + })()} {/* Action Buttons */}
diff --git a/components/settings/email-settings.tsx b/components/settings/email-settings.tsx index 24555577..d581f133 100644 --- a/components/settings/email-settings.tsx +++ b/components/settings/email-settings.tsx @@ -135,6 +135,7 @@ export function EmailSettings() { trustedSendersAddressBook, attachmentReminderEnabled, attachmentReminderKeywords, + hideInlineImageAttachments, updateSetting, } = useSettingsStore(); const { trustedSenderEmails } = useContactStore(); @@ -401,6 +402,14 @@ export function EmailSettings() {
)} + {/* Hide inline images from attachment list */} + + updateSetting('hideInlineImageAttachments', checked)} + /> + + {/* Quick Hover Actions */} {isFeatureEnabled('hoverActionsConfigEnabled') && (
diff --git a/locales/de/common.json b/locales/de/common.json index 997cde01..b43e8421 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Schlüsselwort hinzufügen...", "add": "Hinzufügen", "remove": "Entfernen" + }, + "hide_inline_image_attachments": { + "label": "Eingebettete Bilder in Anhangsliste ausblenden", + "description": "Im Nachrichtentext eingebettete Bilder werden nicht als separate Anhänge aufgeführt" } }, "composer": { diff --git a/locales/en/common.json b/locales/en/common.json index 38a5cb00..88b6d576 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -966,6 +966,10 @@ "add_placeholder": "Add keyword...", "add": "Add", "remove": "Remove" + }, + "hide_inline_image_attachments": { + "label": "Hide inline images from attachments", + "description": "Images embedded in the message body are not listed as separate attachments" } }, "composer": { diff --git a/locales/es/common.json b/locales/es/common.json index 1f037d25..881d476a 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Añadir palabra clave...", "add": "Add", "remove": "Eliminar" + }, + "hide_inline_image_attachments": { + "label": "Ocultar imágenes incrustadas de los adjuntos", + "description": "Las imágenes incrustadas en el cuerpo del mensaje no se listan como adjuntos separados" } }, "composer": { diff --git a/locales/fr/common.json b/locales/fr/common.json index 2ac69980..95799f70 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Ajouter un mot-clé...", "add": "Add", "remove": "Supprimer" + }, + "hide_inline_image_attachments": { + "label": "Masquer les images intégrées des pièces jointes", + "description": "Les images intégrées au corps du message ne sont pas listées comme pièces jointes séparées" } }, "composer": { diff --git a/locales/it/common.json b/locales/it/common.json index 8186a3ec..43c0589a 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Aggiungi parola chiave...", "add": "Add", "remove": "Rimuovi" + }, + "hide_inline_image_attachments": { + "label": "Nascondi le immagini inline dagli allegati", + "description": "Le immagini incorporate nel corpo del messaggio non vengono elencate come allegati separati" } }, "composer": { diff --git a/locales/ja/common.json b/locales/ja/common.json index d7c2b121..d76be001 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -961,6 +961,10 @@ "add_placeholder": "キーワードを追加...", "add": "Add", "remove": "削除" + }, + "hide_inline_image_attachments": { + "label": "添付ファイル一覧からインライン画像を隠す", + "description": "本文に埋め込まれた画像を個別の添付ファイルとして表示しません" } }, "composer": { diff --git a/locales/ko/common.json b/locales/ko/common.json index fdf935a1..478b8fcb 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -961,6 +961,10 @@ "add_placeholder": "키워드 추가...", "add": "Add", "remove": "삭제" + }, + "hide_inline_image_attachments": { + "label": "첨부 파일 목록에서 인라인 이미지 숨기기", + "description": "메시지 본문에 포함된 이미지는 별도의 첨부 파일로 표시되지 않습니다" } }, "composer": { diff --git a/locales/lv/common.json b/locales/lv/common.json index ed3a3fb3..459f69de 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Pievienot atslēgvārdu...", "add": "Add", "remove": "Noņemt" + }, + "hide_inline_image_attachments": { + "label": "Slēpt iegultos attēlus no pielikumu saraksta", + "description": "Ziņojuma pamattekstā iegultie attēli netiek rādīti kā atsevišķi pielikumi" } }, "composer": { diff --git a/locales/nl/common.json b/locales/nl/common.json index 7a65ce02..2807c315 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Trefwoord toevoegen...", "add": "Add", "remove": "Verwijderen" + }, + "hide_inline_image_attachments": { + "label": "Inline-afbeeldingen verbergen uit bijlagenlijst", + "description": "Afbeeldingen die in de berichttekst zijn ingesloten worden niet als aparte bijlagen weergegeven" } }, "composer": { diff --git a/locales/pl/common.json b/locales/pl/common.json index ca830dc6..34b397df 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Dodaj słowo kluczowe...", "add": "Add", "remove": "Usuń" + }, + "hide_inline_image_attachments": { + "label": "Ukryj obrazy osadzone z listy załączników", + "description": "Obrazy osadzone w treści wiadomości nie są wyświetlane jako osobne załączniki" } }, "composer": { diff --git a/locales/pt/common.json b/locales/pt/common.json index 63cd2c9a..76f21fe0 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Adicionar palavra-chave...", "add": "Add", "remove": "Remover" + }, + "hide_inline_image_attachments": { + "label": "Ocultar imagens incorporadas dos anexos", + "description": "As imagens incorporadas no corpo da mensagem não são listadas como anexos separados" } }, "composer": { diff --git a/locales/ru/common.json b/locales/ru/common.json index 6e6c6e0f..00967735 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Добавить ключевое слово...", "add": "Add", "remove": "Удалить" + }, + "hide_inline_image_attachments": { + "label": "Скрывать встроенные изображения из вложений", + "description": "Изображения, встроенные в тело сообщения, не отображаются как отдельные вложения" } }, "composer": { diff --git a/locales/uk/common.json b/locales/uk/common.json index 9717de14..9c477250 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -961,6 +961,10 @@ "add_placeholder": "Додати ключове слово...", "add": "додати", "remove": "видалити" + }, + "hide_inline_image_attachments": { + "label": "Приховувати вбудовані зображення з вкладень", + "description": "Зображення, вбудовані в тіло повідомлення, не відображаються як окремі вкладення" } }, "composer": { diff --git a/locales/zh/common.json b/locales/zh/common.json index d32140ce..d793310b 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -961,6 +961,10 @@ "add_placeholder": "添加关键词...", "add": "Add", "remove": "删除" + }, + "hide_inline_image_attachments": { + "label": "在附件列表中隐藏内嵌图片", + "description": "嵌入到邮件正文中的图片不会作为单独的附件显示" } }, "composer": { diff --git a/stores/settings-store.ts b/stores/settings-store.ts index 13d4e481..88a62ae2 100644 --- a/stores/settings-store.ts +++ b/stores/settings-store.ts @@ -198,6 +198,10 @@ interface SettingsState { attachmentReminderEnabled: boolean; attachmentReminderKeywords: string[]; + // Hide inline images (images referenced by cid in the HTML body) from the + // attachment list shown above the message body. + hideInlineImageAttachments: boolean; + // Sidebar Apps sidebarApps: SidebarApp[]; keepAppsLoaded: boolean; @@ -365,6 +369,8 @@ const DEFAULT_SETTINGS = { 'pielikumā', ] as string[], + hideInlineImageAttachments: true, + // Sidebar Apps sidebarApps: [] as SidebarApp[], keepAppsLoaded: false, @@ -467,6 +473,7 @@ export const useSettingsStore = create()( emailKeywords: state.emailKeywords, attachmentReminderEnabled: state.attachmentReminderEnabled, attachmentReminderKeywords: state.attachmentReminderKeywords, + hideInlineImageAttachments: state.hideInlineImageAttachments, sidebarApps: state.sidebarApps, keepAppsLoaded: state.keepAppsLoaded, debugMode: state.debugMode,