diff --git a/components/global-search/global-search-palette.tsx b/components/global-search/global-search-palette.tsx index abb66c1a3..865d09e3d 100644 --- a/components/global-search/global-search-palette.tsx +++ b/components/global-search/global-search-palette.tsx @@ -110,8 +110,11 @@ export function GlobalSearchPalette({ proShell = true }: { proShell?: boolean }) if (hits.length === 0 && status.errors.length === 0 && status.status !== 'loading') return null; return (
-
+
{t(`scope_${kind}`)} + + {hits.length} + {status.status === 'loading' && }
{hits.slice(0, MAX_ROWS_PER_KIND).map((hit) => ( diff --git a/components/global-search/palette-result-row.tsx b/components/global-search/palette-result-row.tsx index d85b2b61d..04267a484 100644 --- a/components/global-search/palette-result-row.tsx +++ b/components/global-search/palette-result-row.tsx @@ -1,19 +1,43 @@ "use client"; -import { BookUser, CalendarDays, FileText, Folder, Mail, Repeat } from "lucide-react"; +import { CalendarDays, FileText, Folder, Repeat } from "lucide-react"; import { useTranslations } from "next-intl"; +import { Avatar } from "@/components/ui/avatar"; import { parseSearchSnippet, type SnippetSegment } from "@/lib/search-snippet"; import type { GlobalSearchHit } from "@/lib/global-search/types"; +import { getContactDisplayName, getContactPhotoUri, getContactPrimaryEmail } from "@/stores/contact-store"; import { cn } from "@/lib/utils"; -function HitIcon({ hit }: { hit: GlobalSearchHit }) { - const className = "w-4 h-4 shrink-0 text-muted-foreground"; - switch (hit.kind) { - case 'mail': return ; - case 'contacts': return ; - case 'calendar': return ; - case 'files': return hit.isFolder ? : ; +/** + * Leading visual, mirroring the mail list's identity language: real avatars + * for people (sender / contact), kind-tinted icon chips for events and files. + */ +function LeadingVisual({ hit }: { hit: GlobalSearchHit }) { + if (hit.kind === 'mail') { + const from = hit.email.from?.[0]; + return ; } + if (hit.kind === 'contacts') { + return ( + + ); + } + const [Icon, tint] = hit.kind === 'calendar' + ? [CalendarDays, "bg-violet-500/15 text-violet-600 dark:text-violet-400"] + : hit.isFolder + ? [Folder, "bg-amber-500/15 text-amber-600 dark:text-amber-400"] + : [FileText, "bg-amber-500/15 text-amber-600 dark:text-amber-400"]; + return ( + + + + ); } function Snippet({ value }: { value: string }) { @@ -41,8 +65,9 @@ export interface PaletteResultRowProps { } /** - * One palette hit: icon, title, account · context subtitle, date; the server - * snippet with its ``ed terms when mail search returned one. + * One search hit: avatar/icon, title with the date right-aligned, account · + * context subtitle, and the server snippet with its ``ed terms when + * mail search returned one. Used by both the palette and the search tab. */ export function PaletteResultRow({ hit, onOpen, className }: PaletteResultRowProps) { const t = useTranslations('global_search'); @@ -57,29 +82,31 @@ export function PaletteResultRow({ hit, onOpen, className }: PaletteResultRowPro onClick={() => onOpen(hit)} data-hit-kind={hit.kind} className={cn( - "w-full flex items-start gap-2.5 px-3 py-2 text-left rounded-md", - "hover:bg-muted focus:bg-muted focus:outline-none", + "w-full flex items-start gap-3 px-3 py-2 text-left rounded-md transition-colors duration-150", + "hover:bg-muted/50 focus:bg-muted focus:outline-none", className, )} > - + - - - {hit.kind === 'mail' && hit.snippet?.subject ? : title} + + + + {hit.kind === 'mail' && hit.snippet?.subject ? : title} + + {hit.kind === 'calendar' && hit.isRecurring && ( + + )} - {hit.kind === 'calendar' && hit.isRecurring && ( - + {hit.date && ( + {formatHitDate(hit.date)} )} - {subtitle} + {subtitle} {snippet && ( - + )} - {hit.date && ( - {formatHitDate(hit.date)} - )} ); } diff --git a/components/global-search/search-preview.tsx b/components/global-search/search-preview.tsx index 50fae0f02..9f0ba1169 100644 --- a/components/global-search/search-preview.tsx +++ b/components/global-search/search-preview.tsx @@ -1,12 +1,13 @@ "use client"; -import { BookUser, CalendarDays, ExternalLink, FileText, Folder, Repeat } from "lucide-react"; +import { AlignLeft, Building2, CalendarDays, Clock, ExternalLink, FileText, Folder, HardDrive, Mail, MapPin, Phone, Repeat, Users } from "lucide-react"; import { useTranslations } from "next-intl"; +import { Avatar } from "@/components/ui/avatar"; import { ProEmailView } from "@/components/pro/pro-email-tab-body"; import type { CalendarHit, ContactHit, FileHit, GlobalSearchHit } from "@/lib/global-search/types"; -import { getContactDisplayName } from "@/stores/contact-store"; +import { getContactDisplayName, getContactPhotoUri, getContactPrimaryEmail } from "@/stores/contact-store"; import { useAuthStore } from "@/stores/auth-store"; -import { formatFileSize } from "@/lib/utils"; +import { cn, formatFileSize } from "@/lib/utils"; function formatDateTime(iso: string | null): string { if (!iso) return ''; @@ -20,7 +21,7 @@ function OpenButton({ hit, onOpen, label }: { hit: GlobalSearchHit; onOpen: (hit