feat: add setting to render plain-text emails in normal or monospace font #830

This commit is contained in:
Linus Rath
2026-08-16 21:54:52 +02:00
parent ffff165a4b
commit d8c9aaa66a
30 changed files with 180 additions and 4 deletions
+2 -1
View File
@@ -662,6 +662,7 @@ export function EmailViewer({
const tWelcome = useTranslations('welcome');
const externalContentPolicy = useSettingsStore((state) => state.externalContentPolicy);
const messageSpacing = useSettingsStore((state) => state.messageSpacing);
const plainTextFont = useSettingsStore((state) => state.plainTextFont);
const mailAttachmentAction = useSettingsStore((state) => state.mailAttachmentAction);
const attachmentPosition = useSettingsStore((state) => state.attachmentPosition);
const addTrustedSender = useSettingsStore((state) => state.addTrustedSender);
@@ -5024,7 +5025,7 @@ export function EmailViewer({
className="email-content-text text-foreground"
dangerouslySetInnerHTML={{ __html: sanitizePlainTextRenderedHtml(effectiveEmailContent.html) }}
style={{
fontFamily: 'ui-monospace, "SF Mono", Consolas, monospace',
...(plainTextFont === 'mono' && { fontFamily: 'ui-monospace, "SF Mono", Consolas, monospace' }),
fontSize: '14px',
lineHeight: '1.6',
wordBreak: 'break-word',
@@ -240,6 +240,7 @@ function EmailCard({
const mailAttachmentAction = useSettingsStore((state) => state.mailAttachmentAction);
const hideInlineImageAttachments = useSettingsStore((state) => state.hideInlineImageAttachments);
const emailAlwaysLightMode = useSettingsStore((state) => state.emailAlwaysLightMode);
const plainTextFont = useSettingsStore((state) => state.plainTextFont);
const sender = email.from?.[0];
const isUnread = !email.keywords?.$seen;
const isStarred = email.keywords?.$flagged;
@@ -616,7 +617,7 @@ function EmailCard({
"[&_table]:border-collapse [&_td]:p-2 [&_th]:p-2",
"[&_img]:max-w-full [&_img]:h-auto"
)}
style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, "SF Mono", Consolas, monospace', fontSize: '13px' }}
style={{ whiteSpace: 'pre-wrap', ...(plainTextFont === 'mono' && { fontFamily: 'ui-monospace, "SF Mono", Consolas, monospace' }), fontSize: '13px' }}
dangerouslySetInnerHTML={{ __html: sanitizePlainTextRenderedHtml(emailContent.html) }}
/>
)}
+14
View File
@@ -36,6 +36,7 @@ export function ReadingSettings() {
hideInlineImageAttachments,
attachmentImagePreviewsEnabled,
messageSpacing,
plainTextFont,
updateSetting,
} = useSettingsStore();
@@ -130,6 +131,19 @@ export function ReadingSettings() {
</SettingItem>
)}
{!isSettingHidden('plainTextFont') && (
<SettingItem label={t('plain_text_font.label')} description={t('plain_text_font.description')} locked={isSettingLocked('plainTextFont')}>
<Select
value={plainTextFont}
onChange={(value) => updateSetting('plainTextFont', value as typeof plainTextFont)}
options={[
{ value: 'mono', label: t('plain_text_font.mono') },
{ value: 'sans', label: t('plain_text_font.sans') },
]}
/>
</SettingItem>
)}
{!isSettingHidden('deleteAction') && (
<SettingItem label={t('delete_action.label')} description={t('delete_action.description')} locked={isSettingLocked('deleteAction')}>
<div className="flex flex-col gap-2">
+2 -1
View File
@@ -207,6 +207,7 @@ const tabSearchPaths: Record<Tab, string[]> = {
'settings.email_behavior.hide_inline_image_attachments',
'settings.email_behavior.hover_actions',
'settings.email_behavior.permanently_delete_junk',
'settings.email_behavior.plain_text_font',
'settings.email_behavior.show_preview',
],
composing: [
@@ -250,7 +251,7 @@ const tabKeywords: Record<Tab, string> = {
notifications: 'sound alert push badge',
appearance: 'theme dark light font size accent color animation density',
layout: 'toolbar sidebar account switcher unified mailbox icons rail',
reading: 'mark read preview thread conversation archive delete attachment open',
reading: 'mark read preview thread conversation archive delete attachment open monospace mono font plain text',
composing: 'editor signature plain text reply forward draft compose',
downloads: 'download filename template eml attachment save export',
identities: 'from address signature email',
+6
View File
@@ -1195,6 +1195,12 @@
"always": "إضافة تباعد دائمًا",
"edge": "من الحافة إلى الحافة"
},
"plain_text_font": {
"label": "خط النص العادي",
"description": "الخط المستخدم لرسائل النص العادي",
"mono": "أحادي المسافة",
"sans": "عادي (مثل التطبيق)"
},
"archive_mode": {
"label": "الأرشفة في",
"description": "كيفية تنظيم الرسائل عند الأرشفة",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "Afegeix sempre espaiat",
"edge": "De vora a vora"
},
"plain_text_font": {
"label": "Tipus de lletra del text pla",
"description": "Tipus de lletra per als correus de text pla",
"mono": "Monoespaiada",
"sans": "Normal (igual que l'aplicació)"
},
"archive_mode": {
"label": "Arxiva a",
"description": "Com organitzar els correus en arxivar-los",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Vždy přidat odsazení",
"edge": "Od okraje k okraji"
},
"plain_text_font": {
"label": "Písmo prostého textu",
"description": "Písmo použité pro e-maily v prostém textu",
"mono": "Neproporcionální",
"sans": "Normální (stejné jako aplikace)"
},
"archive_mode": {
"label": "Archivovat do",
"description": "Jak organizovat e-maily při archivaci",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "Tilføj altid afstand",
"edge": "Kant til kant"
},
"plain_text_font": {
"label": "Skrifttype til ren tekst",
"description": "Skrifttype til e-mails i ren tekst",
"mono": "Monospace",
"sans": "Normal (samme som appen)"
},
"archive_mode": {
"label": "Arkivér i",
"description": "Hvordan e-mails organiseres ved arkivering",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Immer Abstand hinzufügen",
"edge": "Randlos"
},
"plain_text_font": {
"label": "Schriftart für Nur-Text",
"description": "Schriftart für Nur-Text-E-Mails",
"mono": "Monospace",
"sans": "Normal (wie die App)"
},
"archive_mode": {
"label": "Nachrichten archivieren in",
"description": "Wie E-Mails beim Archivieren organisiert werden",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "Always add spacing",
"edge": "Edge to edge"
},
"plain_text_font": {
"label": "Plain Text Font",
"description": "Font used for plain text emails",
"mono": "Monospace",
"sans": "Normal (same as the app)"
},
"archive_mode": {
"label": "Archive in",
"description": "How to organize emails when archiving",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Añadir espaciado siempre",
"edge": "De borde a borde"
},
"plain_text_font": {
"label": "Fuente de texto sin formato",
"description": "Fuente utilizada para los correos de texto sin formato",
"mono": "Monoespaciada",
"sans": "Normal (igual que la aplicación)"
},
"archive_mode": {
"label": "Archivar en",
"description": "Cómo organizar los correos al archivar",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "همیشه فاصله اضافه شود",
"edge": "لبه به لبه"
},
"plain_text_font": {
"label": "قلم متن ساده",
"description": "قلم مورد استفاده برای ایمیل‌های متن ساده",
"mono": "تک‌عرض",
"sans": "معمولی (مانند برنامه)"
},
"archive_mode": {
"label": "بایگانی در",
"description": "سازمان‌دهی بایگانی",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Toujours ajouter un espacement",
"edge": "Bord à bord"
},
"plain_text_font": {
"label": "Police du texte brut",
"description": "Police utilisée pour les e-mails en texte brut",
"mono": "À chasse fixe",
"sans": "Normale (comme l'application)"
},
"archive_mode": {
"label": "Archiver dans",
"description": "Comment organiser les emails lors de l'archivage",
+6
View File
@@ -1146,6 +1146,12 @@
"always": "תמיד להוסיף מרווח",
"edge": "מקצה לקצה"
},
"plain_text_font": {
"label": "גופן טקסט פשוט",
"description": "הגופן המשמש להודעות בטקסט פשוט",
"mono": "ברוחב קבוע",
"sans": "רגיל (כמו היישום)"
},
"archive_mode": {
"label": "ארכיון ב",
"description": "כיצד לארגן מיילים בעת אחסון בארכיון",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "Mindig legyen térköz",
"edge": "Széltől szélig"
},
"plain_text_font": {
"label": "Egyszerű szöveg betűtípusa",
"description": "Az egyszerű szöveges e-mailekhez használt betűtípus",
"mono": "Rögzített szélességű",
"sans": "Normál (mint az alkalmazás)"
},
"archive_mode": {
"label": "Archiválás helye",
"description": "Hogyan rendezze az e-maileket archiváláskor",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Aggiungi sempre spaziatura",
"edge": "Da bordo a bordo"
},
"plain_text_font": {
"label": "Carattere del testo semplice",
"description": "Carattere usato per le email in testo semplice",
"mono": "Monospazio",
"sans": "Normale (come l'app)"
},
"archive_mode": {
"label": "Archivia in",
"description": "Come organizzare le email durante l'archiviazione",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "常に余白を追加",
"edge": "余白なし"
},
"plain_text_font": {
"label": "プレーンテキストのフォント",
"description": "プレーンテキストメールに使用するフォント",
"mono": "等幅",
"sans": "標準(アプリと同じ)"
},
"archive_mode": {
"label": "アーカイブ先",
"description": "アーカイブ時のメール整理方法",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "항상 여백 추가",
"edge": "가장자리까지 채우기"
},
"plain_text_font": {
"label": "일반 텍스트 글꼴",
"description": "일반 텍스트 이메일에 사용되는 글꼴",
"mono": "고정폭",
"sans": "기본(앱과 동일)"
},
"archive_mode": {
"label": "보관 방식",
"description": "메일을 보관할 때 어떻게 분류할지 설정해요",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Vienmēr pievienot atstarpi",
"edge": "No malas līdz malai"
},
"plain_text_font": {
"label": "Vienkārša teksta fonts",
"description": "Fonts vienkārša teksta e-pastiem",
"mono": "Vienplatuma",
"sans": "Parasts (tāpat kā lietotnē)"
},
"archive_mode": {
"label": "Arhivēt mapē",
"description": "Kā organizēt vēstules arhivējot",
+10 -1
View File
@@ -855,7 +855,10 @@
"zh": "简体中文",
"fa": "فارسی",
"farsi": "فارسی",
"switch_to_farsi": "Фарси хэл рүү шилжих"
"switch_to_farsi": "Фарси хэл рүү шилжих",
"mn": "Монгол",
"mongolian": "Монгол",
"switch_to_mongolian": "Монгол хэл рүү шилжих"
},
"settings": {
"title": "Тохиргоо",
@@ -1192,6 +1195,12 @@
"always": "Үргэлж хоорондын зайг нэмэх",
"edge": "Ирмэгээс ирмэг хүртэл"
},
"plain_text_font": {
"label": "Энгийн текстийн фонт",
"description": "Энгийн текстэн имэйлд ашиглах фонт",
"mono": "Тогтмол өргөнтэй",
"sans": "Энгийн (апптай ижил)"
},
"archive_mode": {
"label": "Архив",
"description": "Архивлахдаа имэйлийг хэрхэн зохион байгуулах вэ",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Altijd ruimte toevoegen",
"edge": "Rand tot rand"
},
"plain_text_font": {
"label": "Lettertype voor platte tekst",
"description": "Lettertype voor e-mails in platte tekst",
"mono": "Monospace",
"sans": "Normaal (zoals de app)"
},
"archive_mode": {
"label": "Archiveren in",
"description": "Hoe e-mails bij archivering worden georganiseerd",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Zawsze dodawaj odstępy",
"edge": "Od krawędzi do krawędzi"
},
"plain_text_font": {
"label": "Czcionka zwykłego tekstu",
"description": "Czcionka używana dla wiadomości w zwykłym tekście",
"mono": "O stałej szerokości",
"sans": "Normalna (jak aplikacja)"
},
"archive_mode": {
"label": "Archiwizuj w",
"description": "Jak organizować wiadomości e-mail podczas archiwizacji",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Sempre adicionar espaçamento",
"edge": "De borda a borda"
},
"plain_text_font": {
"label": "Fonte de texto simples",
"description": "Fonte usada para e-mails de texto simples",
"mono": "Monoespaçada",
"sans": "Normal (igual ao aplicativo)"
},
"archive_mode": {
"label": "Arquivar em",
"description": "Como organizar os e-mails ao arquivar",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "Adaugă întotdeauna spațiere",
"edge": "Din margine în margine"
},
"plain_text_font": {
"label": "Font pentru text simplu",
"description": "Fontul folosit pentru e-mailurile în text simplu",
"mono": "Monospațiat",
"sans": "Normal (la fel ca aplicația)"
},
"archive_mode": {
"label": "Arhivați în",
"description": "Cum să organizați e-mailurile atunci când le arhivați",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Всегда добавлять отступы",
"edge": "От края до края"
},
"plain_text_font": {
"label": "Шрифт обычного текста",
"description": "Шрифт для писем в обычном тексте",
"mono": "Моноширинный",
"sans": "Обычный (как в приложении)"
},
"archive_mode": {
"label": "Архивировать в",
"description": "Как организовывать письма при архивировании",
+6
View File
@@ -1195,6 +1195,12 @@
"always": "Vždy pridať odsadenie",
"edge": "Od okraja po okraj"
},
"plain_text_font": {
"label": "Písmo obyčajného textu",
"description": "Písmo použité pre e-maily v obyčajnom texte",
"mono": "Neproporcionálne",
"sans": "Normálne (rovnaké ako aplikácia)"
},
"archive_mode": {
"label": "Archivovať do",
"description": "Ako organizovať e-maily pri archivácii",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Her zaman boşluk ekle",
"edge": "Kenardan kenara"
},
"plain_text_font": {
"label": "Düz metin yazı tipi",
"description": "Düz metin e-postalar için kullanılan yazı tipi",
"mono": "Eş aralıklı",
"sans": "Normal (uygulamayla aynı)"
},
"archive_mode": {
"label": "Arşivle",
"description": "Arşivlerken e-postaların nasıl düzenleneceği",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "Завжди додавати відступи",
"edge": "Від краю до краю"
},
"plain_text_font": {
"label": "Шрифт звичайного тексту",
"description": "Шрифт для листів у звичайному тексті",
"mono": "Моноширинний",
"sans": "Звичайний (як у застосунку)"
},
"archive_mode": {
"label": "Архівувати в",
"description": "Як упорядкувати листи під час архівування",
+6
View File
@@ -1192,6 +1192,12 @@
"always": "始终添加边距",
"edge": "无边距"
},
"plain_text_font": {
"label": "纯文本字体",
"description": "用于纯文本邮件的字体",
"mono": "等宽",
"sans": "普通(与应用相同)"
},
"archive_mode": {
"label": "归档方式",
"description": "归档时如何组织邮件",
+6
View File
@@ -128,6 +128,9 @@ export type MailLayout = 'split' | 'focus' | 'horizontal';
* - 'edge' : never add one (render edge-to-edge)
*/
export type MessageSpacing = 'auto' | 'always' | 'edge';
/** Font used to render text/plain email bodies. */
export type PlainTextFont = 'mono' | 'sans';
export type CalendarHoverPreview = 'off' | 'instant' | 'delay-500ms' | 'delay-1s' | 'delay-2s';
export type SendDelaySeconds = 0 | 10 | 30 | 60;
export type ProtocolOpenMode = 'active-session' | 'new-tab';
@@ -313,6 +316,7 @@ interface SettingsState {
emailsPerPage: number;
externalContentPolicy: ExternalContentPolicy;
messageSpacing: MessageSpacing; // Gutter around the message body in the reader
plainTextFont: PlainTextFont; // Font for text/plain email bodies in the reader
mailAttachmentAction: MailAttachmentAction;
attachmentPosition: AttachmentPosition;
emailAlwaysLightMode: boolean; // Always render email content in light mode
@@ -541,6 +545,7 @@ const DEFAULT_SETTINGS = {
emailsPerPage: 50,
externalContentPolicy: 'ask' as ExternalContentPolicy,
messageSpacing: 'auto' as MessageSpacing,
plainTextFont: 'mono' as PlainTextFont,
mailAttachmentAction: 'preview' as MailAttachmentAction,
attachmentPosition: 'beside-sender' as AttachmentPosition,
emailAlwaysLightMode: false,
@@ -759,6 +764,7 @@ export const useSettingsStore = create<SettingsState>()(
emailsPerPage: state.emailsPerPage,
externalContentPolicy: state.externalContentPolicy,
messageSpacing: state.messageSpacing,
plainTextFont: state.plainTextFont,
mailAttachmentAction: state.mailAttachmentAction,
attachmentPosition: state.attachmentPosition,
archiveMode: state.archiveMode,