From 6bce3323a929ca3ce5062dfaf594fecb42f20cc0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:02:26 +0200 Subject: [PATCH] fix: keep previews that only start like a style sheet #1136 The leading-CSS strip emptied or cut ordinary previews: "@media team, the brief is attached", "@Page can you check?" and "@import the CSV ..." became empty, and "Reminder: your appointment {date: 2026-10-01} is confirmed" became "is confirmed". At-rule names now match in lower case only, only `@import` and `@charset` end at a semicolon, an at-rule word with no block after it is prose, and a selector colon must not be followed by a space. The conversation view's collapsed cards, contact activity, the pro folder tab and the list's row-height estimate now use `cleanPreview` too, so they agree with the list rows. The test's invisible characters are written as escapes. --- components/contacts/contact-activity.tsx | 5 +++-- components/email/email-list.tsx | 4 ++-- components/email/thread-conversation-view.tsx | 4 ++-- components/pro/pro-folder-tab-body.tsx | 6 +++--- lib/__tests__/preview-leading-css.test.ts | 12 +++++++++++- lib/utils.ts | 15 +++++++++++---- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/components/contacts/contact-activity.tsx b/components/contacts/contact-activity.tsx index 0285960bb..4a95ffcd2 100644 --- a/components/contacts/contact-activity.tsx +++ b/components/contacts/contact-activity.tsx @@ -8,6 +8,7 @@ import { useAuthStore } from "@/stores/auth-store"; import { useEmailStore } from "@/stores/email-store"; import { useCalendarStore } from "@/stores/calendar-store"; import { Avatar } from "@/components/ui/avatar"; +import { cleanPreview } from "@/lib/utils"; import { Section } from "./contact-detail"; import type { ContactCard, Email, CalendarEvent } from "@/lib/jmap/types"; @@ -244,9 +245,9 @@ export function ContactActivity({ contact }: ContactActivityProps) {
- {email.preview} + {cleanPreview(email.preview)}
)} diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index 359a2817c..0e878a9f6 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -7,7 +7,7 @@ import type { LoadListAttachments } from "@/lib/list-attachments"; import { listRowShowsChips } from "./attachment-chips"; import { listVerificationCode } from "@/lib/verification-code"; import { EmailContextMenu } from "./email-context-menu"; -import { cn } from "@/lib/utils"; +import { cn, cleanPreview } from "@/lib/utils"; import { Trash2, Mail, MailX, MailOpen, Loader2, SearchX, AlertTriangle, CalendarClock, ShieldCheck } from "@/components/icons"; import { useState, useEffect, useLayoutEffect, useRef, useCallback, useMemo } from "react"; import { Button } from "@/components/ui/button"; @@ -246,7 +246,7 @@ export function EmailList({ if (showPreview && density !== 'extra-compact') { // A mail without a preview draws a one-line "No preview available", // a line (23px) shorter than a real one. - const emptyPreview = !!latest && !latest.preview?.trim() && !latest.searchSnippet?.preview; + const emptyPreview = !!latest && !cleanPreview(latest.preview) && !latest.searchSnippet?.preview; size += emptyPreview ? 36 - 23 : 36; } // The chip row (attachments, verification code): a 22px chip plus 6px margin. diff --git a/components/email/thread-conversation-view.tsx b/components/email/thread-conversation-view.tsx index 18a58a6e8..feb52886c 100644 --- a/components/email/thread-conversation-view.tsx +++ b/components/email/thread-conversation-view.tsx @@ -14,7 +14,7 @@ import { transformInlineStyles, transformColorForDarkMode, transformBgColorForDa import { useThemeStore } from "@/stores/theme-store"; import { Avatar } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; -import { formatDate, formatFileSize, cn } from "@/lib/utils"; +import { formatDate, formatFileSize, cn, cleanPreview } from "@/lib/utils"; import { ArrowLeft, ChevronDown, @@ -568,7 +568,7 @@ function EmailCard({ {!isExpanded && density !== 'extra-compact' && (- {email.preview || t('email_viewer.no_preview_available')} + {cleanPreview(email.preview) || t('email_viewer.no_preview_available')}
)} diff --git a/components/pro/pro-folder-tab-body.tsx b/components/pro/pro-folder-tab-body.tsx index c726d29ae..642d730a2 100644 --- a/components/pro/pro-folder-tab-body.tsx +++ b/components/pro/pro-folder-tab-body.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { Folder, Loader2, Paperclip, RefreshCw, Star } from "@/components/icons"; -import { cn, formatDate } from "@/lib/utils"; +import { cn, cleanPreview, formatDate } from "@/lib/utils"; import { localizeMailboxName } from "@/lib/mailbox-label"; import { EmailViewer } from "@/components/email/email-viewer"; import { ProEmailView } from "@/components/pro/pro-email-tab-body"; @@ -240,9 +240,9 @@ export function ProFolderTabBody({ tabId, data }: ProFolderTabBodyProps) { )} - {email.preview && ( + {cleanPreview(email.preview) && (