feat: add 'Always Light Mode' setting for email display

This commit is contained in:
Linus Rath
2026-03-17 00:56:34 +01:00
parent a834528bc1
commit 6e78f2a09e
12 changed files with 50 additions and 4 deletions
+2 -1
View File
@@ -1036,8 +1036,9 @@ export function EmailViewer({
return /prefers-color-scheme\s*:\s*dark/i.test(emailContent.html); return /prefers-color-scheme\s*:\s*dark/i.test(emailContent.html);
}, [emailContent.html, emailContent.isHtml]); }, [emailContent.html, emailContent.isHtml]);
const emailAlwaysLightMode = useSettingsStore((state) => state.emailAlwaysLightMode);
const [emailViewDarkOverride, setEmailViewDarkOverride] = useState<boolean | null>(null); const [emailViewDarkOverride, setEmailViewDarkOverride] = useState<boolean | null>(null);
const isDark = emailViewDarkOverride !== null ? emailViewDarkOverride : resolvedTheme === 'dark'; const isDark = emailAlwaysLightMode ? false : (emailViewDarkOverride !== null ? emailViewDarkOverride : resolvedTheme === 'dark');
const emailIframeSrcDoc = useMemo(() => { const emailIframeSrcDoc = useMemo(() => {
if (!emailContent.isHtml) return ''; if (!emailContent.isHtml) return '';
@@ -225,6 +225,7 @@ function EmailCard({
const resolvedTheme = useThemeStore((state) => state.resolvedTheme); const resolvedTheme = useThemeStore((state) => state.resolvedTheme);
const density = useSettingsStore((state) => state.density); const density = useSettingsStore((state) => state.density);
const mailAttachmentAction = useSettingsStore((state) => state.mailAttachmentAction); const mailAttachmentAction = useSettingsStore((state) => state.mailAttachmentAction);
const emailAlwaysLightMode = useSettingsStore((state) => state.emailAlwaysLightMode);
const sender = email.from?.[0]; const sender = email.from?.[0];
const isUnread = !email.keywords?.$seen; const isUnread = !email.keywords?.$seen;
const isStarred = email.keywords?.$flagged; const isStarred = email.keywords?.$flagged;
@@ -363,7 +364,7 @@ function EmailCard({
node.setAttribute('rel', 'noopener noreferrer'); node.setAttribute('rel', 'noopener noreferrer');
} }
if (resolvedTheme === 'dark') { if (resolvedTheme === 'dark' && !emailAlwaysLightMode) {
if (htmlNode.style) { if (htmlNode.style) {
const originalStyles = htmlNode.style.cssText; const originalStyles = htmlNode.style.cssText;
const transformedStyles = transformInlineStyles(originalStyles, 'dark'); const transformedStyles = transformInlineStyles(originalStyles, 'dark');
@@ -415,7 +416,7 @@ function EmailCard({
} }
return { html: "", isHtml: false }; return { html: "", isHtml: false };
}, [email, allowExternal, resolvedTheme, cidBlobUrls]); }, [email, allowExternal, resolvedTheme, emailAlwaysLightMode, cidBlobUrls]);
return ( return (
<div className={cn( <div className={cn(
@@ -513,7 +514,8 @@ function EmailCard({
<div style={{ padding: 'var(--density-card-p)' }}> <div style={{ padding: 'var(--density-card-p)' }}>
<div <div
className={cn( className={cn(
"prose prose-sm max-w-none dark:prose-invert", "prose prose-sm max-w-none",
!emailAlwaysLightMode && "dark:prose-invert",
"prose-p:my-2 prose-headings:my-3", "prose-p:my-2 prose-headings:my-3",
"prose-a:text-primary prose-a:no-underline hover:prose-a:underline", "prose-a:text-primary prose-a:no-underline hover:prose-a:underline",
"[&_table]:border-collapse [&_td]:p-2 [&_th]:p-2", "[&_table]:border-collapse [&_td]:p-2 [&_th]:p-2",
+9
View File
@@ -19,6 +19,7 @@ export function EmailSettings() {
emailsPerPage, emailsPerPage,
externalContentPolicy, externalContentPolicy,
mailAttachmentAction, mailAttachmentAction,
emailAlwaysLightMode,
trustedSenders, trustedSenders,
updateSetting, updateSetting,
} = useSettingsStore(); } = useSettingsStore();
@@ -104,6 +105,14 @@ export function EmailSettings() {
/> />
</SettingItem> </SettingItem>
{/* Always Light Mode for Emails */}
<SettingItem label={t('always_light_mode.label')} description={t('always_light_mode.description')}>
<ToggleSwitch
checked={emailAlwaysLightMode}
onChange={(checked) => updateSetting('emailAlwaysLightMode', checked)}
/>
</SettingItem>
{/* External Content */} {/* External Content */}
<SettingItem label={t('external_content.label')} description={t('external_content.description')}> <SettingItem label={t('external_content.label')} description={t('external_content.description')}>
<Select <Select
+4
View File
@@ -691,6 +691,10 @@
"label": "E-Mails pro Seite", "label": "E-Mails pro Seite",
"description": "Anzahl der E-Mails, die auf einmal geladen werden" "description": "Anzahl der E-Mails, die auf einmal geladen werden"
}, },
"always_light_mode": {
"label": "E-Mails immer im Hellmodus anzeigen",
"description": "E-Mail-Inhalte im Hellmodus darstellen, auch wenn die App im Dunkelmodus ist, um Probleme bei der Dunkelmodus-Konvertierung zu vermeiden"
},
"external_content": { "external_content": {
"label": "Externe Inhalte", "label": "Externe Inhalte",
"description": "Wie mit Bildern und externen Inhalten umgegangen werden soll", "description": "Wie mit Bildern und externen Inhalten umgegangen werden soll",
+4
View File
@@ -691,6 +691,10 @@
"label": "Emails Per Page", "label": "Emails Per Page",
"description": "Number of emails to load at once" "description": "Number of emails to load at once"
}, },
"always_light_mode": {
"label": "Always Show Emails in Light Mode",
"description": "Render email content in light mode even when the app is in dark mode, avoiding dark mode conversion issues"
},
"external_content": { "external_content": {
"label": "External Content", "label": "External Content",
"description": "How to handle images and external content", "description": "How to handle images and external content",
+4
View File
@@ -691,6 +691,10 @@
"label": "Correos por Página", "label": "Correos por Página",
"description": "Número de correos a cargar a la vez" "description": "Número de correos a cargar a la vez"
}, },
"always_light_mode": {
"label": "Mostrar correos siempre en modo claro",
"description": "Mostrar el contenido del correo en modo claro incluso cuando la aplicación está en modo oscuro, evitando problemas de conversión"
},
"external_content": { "external_content": {
"label": "Contenido Externo", "label": "Contenido Externo",
"description": "Cómo manejar imágenes y contenido externo", "description": "Cómo manejar imágenes y contenido externo",
+4
View File
@@ -691,6 +691,10 @@
"label": "Emails par page", "label": "Emails par page",
"description": "Nombre d'emails à charger à la fois" "description": "Nombre d'emails à charger à la fois"
}, },
"always_light_mode": {
"label": "Toujours afficher les e-mails en mode clair",
"description": "Afficher le contenu des e-mails en mode clair même lorsque l'application est en mode sombre, évitant les problèmes de conversion"
},
"external_content": { "external_content": {
"label": "Contenu externe", "label": "Contenu externe",
"description": "Comment gérer les images et le contenu externe", "description": "Comment gérer les images et le contenu externe",
+4
View File
@@ -691,6 +691,10 @@
"label": "Messaggi per pagina", "label": "Messaggi per pagina",
"description": "Numero di messaggi da caricare alla volta" "description": "Numero di messaggi da caricare alla volta"
}, },
"always_light_mode": {
"label": "Mostra sempre le email in modalità chiara",
"description": "Visualizza il contenuto delle email in modalità chiara anche quando l'app è in modalità scura, evitando problemi di conversione"
},
"external_content": { "external_content": {
"label": "Contenuti esterni", "label": "Contenuti esterni",
"description": "Come gestire immagini e contenuti esterni", "description": "Come gestire immagini e contenuti esterni",
+4
View File
@@ -691,6 +691,10 @@
"label": "ページごとのメール数", "label": "ページごとのメール数",
"description": "一度に読み込むメールの数" "description": "一度に読み込むメールの数"
}, },
"always_light_mode": {
"label": "メールを常にライトモードで表示",
"description": "アプリがダークモードの場合でもメールをライトモードで表示し、ダークモード変換の問題を回避します"
},
"external_content": { "external_content": {
"label": "外部コンテンツ", "label": "外部コンテンツ",
"description": "画像と外部コンテンツの処理方法", "description": "画像と外部コンテンツの処理方法",
+4
View File
@@ -691,6 +691,10 @@
"label": "E-mails per pagina", "label": "E-mails per pagina",
"description": "Aantal e-mails dat in één keer wordt geladen" "description": "Aantal e-mails dat in één keer wordt geladen"
}, },
"always_light_mode": {
"label": "E-mails altijd in lichte modus weergeven",
"description": "E-mailinhoud in lichte modus weergeven, zelfs wanneer de app in donkere modus staat, om problemen met donkere modus-conversie te voorkomen"
},
"external_content": { "external_content": {
"label": "Externe inhoud", "label": "Externe inhoud",
"description": "Hoe afbeeldingen en externe inhoud moeten worden verwerkt", "description": "Hoe afbeeldingen en externe inhoud moeten worden verwerkt",
+4
View File
@@ -691,6 +691,10 @@
"label": "E-mails Por Página", "label": "E-mails Por Página",
"description": "Número de e-mails a carregar de uma vez" "description": "Número de e-mails a carregar de uma vez"
}, },
"always_light_mode": {
"label": "Mostrar e-mails sempre em modo claro",
"description": "Exibir o conteúdo dos e-mails em modo claro mesmo quando o aplicativo está em modo escuro, evitando problemas de conversão"
},
"external_content": { "external_content": {
"label": "Conteúdo Externo", "label": "Conteúdo Externo",
"description": "Como lidar com imagens e conteúdo externo", "description": "Como lidar com imagens e conteúdo externo",
+2
View File
@@ -83,6 +83,7 @@ interface SettingsState {
emailsPerPage: number; emailsPerPage: number;
externalContentPolicy: ExternalContentPolicy; externalContentPolicy: ExternalContentPolicy;
mailAttachmentAction: MailAttachmentAction; mailAttachmentAction: MailAttachmentAction;
emailAlwaysLightMode: boolean; // Always render email content in light mode
// Composer // Composer
autoSaveDraftInterval: number; // milliseconds autoSaveDraftInterval: number; // milliseconds
@@ -164,6 +165,7 @@ const DEFAULT_SETTINGS = {
emailsPerPage: 50, emailsPerPage: 50,
externalContentPolicy: 'ask' as ExternalContentPolicy, externalContentPolicy: 'ask' as ExternalContentPolicy,
mailAttachmentAction: 'preview' as MailAttachmentAction, mailAttachmentAction: 'preview' as MailAttachmentAction,
emailAlwaysLightMode: false,
// Composer // Composer
autoSaveDraftInterval: 60000, // 1 minute autoSaveDraftInterval: 60000, // 1 minute