99 Info ,
1010 Mail ,
1111 Paperclip ,
12- Plus ,
13- Save ,
1412 Search ,
1513 Star ,
1614 Tag ,
@@ -32,19 +30,15 @@ import {
3230import {
3331 createContext ,
3432 Fragment ,
33+ Suspense ,
3534 useCallback ,
3635 useContext ,
3736 useEffect ,
3837 useMemo ,
3938 useState ,
4039 type ComponentType ,
4140} from 'react' ;
42- import {
43- cn ,
44- getMainSearchTerm ,
45- parseNaturalLanguageDate ,
46- parseNaturalLanguageSearch ,
47- } from '@/lib/utils' ;
41+ import { getMainSearchTerm , parseNaturalLanguageSearch } from '@/lib/utils' ;
4842import { DialogDescription , DialogTitle } from '@/components/ui/dialog' ;
4943import { navigationConfig , type MessageKey } from '@/config/navigation' ;
5044import { useSearchValue } from '@/hooks/use-search-value' ;
@@ -56,11 +50,11 @@ import { Calendar } from '@/components/ui/calendar';
5650import { useMutation } from '@tanstack/react-query' ;
5751import { useThreads } from '@/hooks/use-threads' ;
5852import { useLabels } from '@/hooks/use-labels' ;
59- import { Badge } from '@/components/ui/badge' ;
60- import { Input } from '@/components/ui/input' ;
6153import { Label } from '@/components/ui/label' ;
62- import { format , subDays } from 'date-fns' ;
54+ import { Input } from '@/components/ui/input' ;
55+ import { Badge } from '@/components/ui/badge' ;
6356import { useTranslations } from 'use-intl' ;
57+ import { format , subDays } from 'date-fns' ;
6458import { VisuallyHidden } from 'radix-ui' ;
6559import { Pencil2 } from '../icons/icons' ;
6660import { Button } from '../ui/button' ;
@@ -71,6 +65,8 @@ type CommandPaletteContext = {
7165 open : boolean ;
7266 setOpen : ( open : boolean ) => void ;
7367 openModal : ( ) => void ;
68+ activeFilters : ActiveFilter [ ] ;
69+ clearAllFilters : ( ) => void ;
7470} ;
7571
7672interface CommandItem {
@@ -180,7 +176,7 @@ const deleteSavedSearch = (id: string) => {
180176 }
181177} ;
182178
183- export function CommandPalette ( ) {
179+ export function CommandPalette ( { children } : { children : React . ReactNode } ) {
184180 const [ open , setOpen ] = useState ( false ) ;
185181 const [ , setIsComposeOpen ] = useQueryState ( 'isComposeOpen' ) ;
186182 const [ currentView , setCurrentView ] = useState < CommandView > ( 'main' ) ;
@@ -433,6 +429,19 @@ export function CommandPalette() {
433429 finalQuery = semanticQuery || query ;
434430 }
435431
432+ const isFilterSyntax = / ^ ( f r o m : | t o : | s u b j e c t : | h a s : | i s : | a f t e r : | b e f o r e : | l a b e l : ) / . test (
433+ query . trim ( ) ,
434+ ) ;
435+ if ( query . trim ( ) && ! isFilterSyntax ) {
436+ const searchFilter : ActiveFilter = {
437+ id : `search-${ Date . now ( ) } ` ,
438+ type : 'search' ,
439+ value : query ,
440+ display : `Search: "${ query } "` ,
441+ } ;
442+ addFilter ( searchFilter ) ;
443+ }
444+
436445 const filterQuery = activeFilters . map ( ( f ) => f . value ) . join ( ' ' ) ;
437446 if ( filterQuery ) {
438447 finalQuery = `${ finalQuery } ${ filterQuery } ` . trim ( ) ;
@@ -449,7 +458,7 @@ export function CommandPalette() {
449458 description : finalQuery ,
450459 } ) ;
451460 } ,
452- [ activeFilters , searchValue . folder , setSearchValue ] ,
461+ [ activeFilters , searchValue . folder , setSearchValue , addFilter ] ,
453462 ) ;
454463
455464 const quickFilterOptions = useMemo (
@@ -515,59 +524,6 @@ export function CommandPalette() {
515524 [ addFilter , executeSearch ] ,
516525 ) ;
517526
518- const processSearchQuery = useCallback ( ( query : string ) : string => {
519- let searchTerms = [ ] ;
520-
521- try {
522- if ( query . trim ( ) ) {
523- const searchTerm = query . trim ( ) ;
524-
525- const dateRange = parseNaturalLanguageDate ( searchTerm ) ;
526- if ( dateRange ) {
527- if ( dateRange . from ) {
528- const fromDate = format ( dateRange . from , 'yyyy/MM/dd' ) ;
529- searchTerms . push ( `after:${ fromDate } ` ) ;
530- }
531- if ( dateRange . to ) {
532- const toDate = format ( dateRange . to , 'yyyy/MM/dd' ) ;
533- searchTerms . push ( `before:${ toDate } ` ) ;
534- }
535-
536- const cleanedQuery = searchTerm
537- . replace ( / e m a i l s ? \s + f r o m \s + / i, '' )
538- . replace ( / \b \d { 4 } \b / g, '' )
539- . replace (
540- / \b ( j a n u a r y | f e b r u a r y | m a r c h | a p r i l | m a y | j u n e | j u l y | a u g u s t | s e p t e m b e r | o c t o b e r | n o v e m b e r | d e c e m b e r ) \b / gi,
541- '' ,
542- )
543- . trim ( ) ;
544-
545- if ( cleanedQuery ) {
546- searchTerms . push ( cleanedQuery ) ;
547- }
548- } else {
549- const parsedTerm = parseNaturalLanguageSearch ( searchTerm ) ;
550- if ( parsedTerm !== searchTerm ) {
551- searchTerms . push ( parsedTerm ) ;
552- } else {
553- if ( searchTerm . includes ( '@' ) ) {
554- searchTerms . push ( `from:${ searchTerm } ` ) ;
555- } else {
556- searchTerms . push (
557- `(from:${ searchTerm } OR from:"${ searchTerm } " OR subject:"${ searchTerm } " OR "${ searchTerm } ")` ,
558- ) ;
559- }
560- }
561- }
562- }
563-
564- return searchTerms . join ( ' ' ) ;
565- } catch ( error ) {
566- console . error ( 'Search processing error:' , error ) ;
567- return query ;
568- }
569- } , [ ] ) ;
570-
571527 const handleSearch = useCallback (
572528 async ( query : string , useNaturalLanguage = true ) => {
573529 setIsProcessing ( true ) ;
@@ -581,6 +537,15 @@ export function CommandPalette() {
581537 toast . info ( 'Search applied' , {
582538 description : finalQuery ,
583539 } ) ;
540+
541+ const searchFilter : ActiveFilter = {
542+ id : `ai-search-${ Date . now ( ) } ` ,
543+ type : 'search' ,
544+ value : finalQuery ,
545+ display : `AI Search: "${ query } "` ,
546+ } ;
547+ addFilter ( searchFilter ) ;
548+
584549 return setSearchValue ( {
585550 value : finalQuery ,
586551 highlight : getMainSearchTerm ( query ) ,
@@ -590,6 +555,19 @@ export function CommandPalette() {
590555 } ) ;
591556 }
592557
558+ const isFilterSyntax = / ^ ( f r o m : | t o : | s u b j e c t : | h a s : | i s : | a f t e r : | b e f o r e : | l a b e l : ) / . test (
559+ query . trim ( ) ,
560+ ) ;
561+ if ( query . trim ( ) && ! isFilterSyntax ) {
562+ const searchFilter : ActiveFilter = {
563+ id : `search-${ Date . now ( ) } ` ,
564+ type : 'search' ,
565+ value : query ,
566+ display : `Search: "${ query } "` ,
567+ } ;
568+ addFilter ( searchFilter ) ;
569+ }
570+
593571 const filterQuery = activeFilters . map ( ( f ) => f . value ) . join ( ' ' ) ;
594572 if ( filterQuery ) {
595573 finalQuery = `${ finalQuery } ${ filterQuery } ` . trim ( ) ;
@@ -620,7 +598,7 @@ export function CommandPalette() {
620598 setIsProcessing ( false ) ;
621599 }
622600 } ,
623- [ activeFilters , processSearchQuery , searchValue . folder , setSearchValue , setOpen ] ,
601+ [ activeFilters , searchValue . folder , setSearchValue , setOpen , generateSearchQuery , addFilter ] ,
624602 ) ;
625603
626604 const quickSearchResults = useMemo ( ( ) => {
@@ -799,7 +777,7 @@ export function CommandPalette() {
799777 < Button
800778 variant = "ghost"
801779 size = "sm"
802- className = "h-6 px-2 text-xs"
780+ className = "text-muted-foreground hover:text-foreground h-6 px-2 text-xs"
803781 onClick = { clearAllFilters }
804782 >
805783 Clear All
@@ -1853,25 +1831,17 @@ export function CommandPalette() {
18531831 } ;
18541832
18551833 return (
1856- < >
1857- < Button
1858- variant = "outline"
1859- className = { cn (
1860- 'text-muted-foreground relative h-9 w-full select-none justify-start rounded-[0.5rem] border bg-white text-sm font-normal shadow-none ring-0 focus-visible:ring-0 focus-visible:ring-offset-0 dark:bg-[#141414]' ,
1861- ) }
1862- onClick = { ( ) => setOpen ( true ) }
1863- >
1864- < span className = "hidden lg:inline-flex" > Search & Filters </ span >
1865- < span className = "inline-flex lg:hidden" > Search...</ span >
1866- { activeFilters . length > 0 && (
1867- < Badge variant = "secondary" className = "ml-2 h-5 px-1" >
1868- { activeFilters . length }
1869- </ Badge >
1870- ) }
1871- < kbd className = "bg-muted pointer-events-none absolute right-[0.45rem] top-[0.45rem] hidden h-5 select-none items-center gap-1 rounded border px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex" >
1872- < span className = "text-xs" > ⌘</ span > K
1873- </ kbd >
1874- </ Button >
1834+ < CommandPaletteContext . Provider
1835+ value = { {
1836+ open,
1837+ setOpen,
1838+ openModal : ( ) => {
1839+ setOpen ( true ) ;
1840+ } ,
1841+ activeFilters,
1842+ clearAllFilters,
1843+ } }
1844+ >
18751845 < CommandDialog
18761846 open = { open }
18771847 onOpenChange = { ( isOpen ) => {
@@ -1888,21 +1858,15 @@ export function CommandPalette() {
18881858 </ VisuallyHidden . VisuallyHidden >
18891859 { renderView ( ) }
18901860 </ CommandDialog >
1891- </ >
1861+ { children }
1862+ </ CommandPaletteContext . Provider >
18921863 ) ;
18931864}
18941865
18951866export function CommandPaletteProvider ( { children } : { children : React . ReactNode } ) {
1896- const [ open , setOpen ] = useState ( false ) ;
1897-
1898- const openModal = useCallback ( ( ) => {
1899- setOpen ( true ) ;
1900- } , [ ] ) ;
1901-
19021867 return (
1903- < CommandPaletteContext . Provider value = { { open, setOpen, openModal } } >
1904- { children }
1905- < CommandPalette />
1906- </ CommandPaletteContext . Provider >
1868+ < Suspense >
1869+ < CommandPalette > { children } </ CommandPalette >
1870+ </ Suspense >
19071871 ) ;
19081872}
0 commit comments