@@ -15,6 +15,7 @@ import {
1515 Check ,
1616 Bot ,
1717 FolderOpen ,
18+ GraduationCap ,
1819 Loader2 ,
1920 Palette ,
2021 Search ,
@@ -194,6 +195,9 @@ function Marketplace() {
194195 const [ installBucket , setInstallBucket ] = useState < InstallBucket > ( "any" ) ;
195196 const [ sort , setSort ] = useState < SortKey > ( "installs_desc" ) ;
196197 const [ filtersOpen , setFiltersOpen ] = useState ( false ) ;
198+ /** Active one-click preset (General-purpose / Most installed / Beginner-friendly). */
199+ const [ quick , setQuick ] = useState < string | null > ( null ) ;
200+ const quickPreset = QUICK_FILTERS . find ( ( f ) => f . value === quick ) ?? null ;
197201
198202 const items = data ?. items ?? [ ] ;
199203 const verticals = data ?. verticals ?? [ ] ;
@@ -213,6 +217,7 @@ function Marketplace() {
213217 const grp = VERTICAL_GROUPS . find ( ( g ) => g . value === verticalGroup ) ;
214218 if ( grp && ( ! p . vertical || ! ( grp . verticals as readonly string [ ] ) . includes ( p . vertical ) ) ) return false ;
215219 }
220+ if ( quickPreset && ! quickPreset . match ( p ) ) return false ;
216221 if ( verifiedOnly && ! p . author_verified ) return false ;
217222 if ( p . install_count < bucket . min || p . install_count > bucket . max ) return false ;
218223 if ( ! needle ) return true ;
@@ -270,7 +275,7 @@ function Marketplace() {
270275 }
271276 } ) ;
272277 return sorted ;
273- } , [ items , type , tags , verticalGroup , q , verifiedOnly , installBucket , sort ] ) ;
278+ } , [ items , type , tags , verticalGroup , q , verifiedOnly , installBucket , sort , quickPreset ] ) ;
274279
275280 const typeCounts = useMemo ( ( ) => {
276281 const base : Record < TypeFilter , number > = {
@@ -310,6 +315,7 @@ function Marketplace() {
310315 verticalGroup !== "all" ||
311316 verifiedOnly ||
312317 installBucket !== "any" ||
318+ quick !== null ||
313319 sort !== "installs_desc" ;
314320
315321 const activeChips : { key : string ; label : string ; clear : ( ) => void } [ ] = [
@@ -325,6 +331,9 @@ function Marketplace() {
325331 } ,
326332 ]
327333 : [ ] ) ,
334+ ...( quickPreset
335+ ? [ { key : "quick" , label : quickPreset . label , clear : ( ) => setQuick ( null ) } ]
336+ : [ ] ) ,
328337 ...( verifiedOnly
329338 ? [ { key : "verified" , label : "Verified authors" , clear : ( ) => setVerifiedOnly ( false ) } ]
330339 : [ ] ) ,
@@ -347,6 +356,14 @@ function Marketplace() {
347356 setVerifiedOnly ( false ) ;
348357 setInstallBucket ( "any" ) ;
349358 setSort ( "installs_desc" ) ;
359+ setQuick ( null ) ;
360+ }
361+
362+ function toggleQuick ( value : string ) {
363+ const next = quick === value ? null : value ;
364+ setQuick ( next ) ;
365+ const preset = QUICK_FILTERS . find ( ( f ) => f . value === next ) ;
366+ if ( preset ?. sort ) setSort ( preset . sort ) ;
350367 }
351368
352369
@@ -461,6 +478,33 @@ function Marketplace() {
461478 </ label >
462479 </ div >
463480 </ div >
481+ < div className = "mx-auto flex max-w-7xl flex-wrap items-center gap-2 px-6 pb-3" >
482+ < span className = "font-mono text-[11px] uppercase tracking-[0.18em] text-muted-foreground" >
483+ Quick filters
484+ </ span >
485+ { QUICK_FILTERS . map ( ( f ) => {
486+ const active = quick === f . value ;
487+ return (
488+ < button
489+ key = { f . value }
490+ onClick = { ( ) => toggleQuick ( f . value ) }
491+ aria-pressed = { active }
492+ title = { f . hint }
493+ className = { `inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-xs font-medium transition-colors ${
494+ active
495+ ? "border-primary bg-primary/10 text-primary"
496+ : "border-border bg-card text-muted-foreground hover:border-primary/40 hover:text-foreground"
497+ } `}
498+ >
499+ < f . icon className = "h-3.5 w-3.5" aria-hidden />
500+ { f . label }
501+ </ button >
502+ ) ;
503+ } ) }
504+ { quickPreset && (
505+ < span className = "text-[11px] text-muted-foreground" > { quickPreset . hint } </ span >
506+ ) }
507+ </ div >
464508 { activeChips . length > 0 && (
465509 < div className = "mx-auto flex max-w-7xl flex-wrap items-center gap-2 px-6 pb-3" >
466510 { activeChips . map ( ( c ) => (
0 commit comments