1+ import { useState } from 'react'
12import { Link } from 'react-router-dom'
2- import { TEMPLATES } from '../lib/templates'
3+ import { TEMPLATES , Template } from '../lib/templates'
34import { encodeWorkspace } from '../lib/hash'
45import { ArrowRight , FileCode , BarChart3 , Sparkles , Plug , File , Atom , X , Palette , Timer , Type , Layout , Cloud , Gamepad2 } from 'lucide-react'
56
@@ -18,36 +19,55 @@ const iconMap: Record<string, React.ReactNode> = {
1819 weather : < Cloud className = "w-5 h-5" /> ,
1920}
2021
22+ const CATEGORIES = [
23+ { id : 'all' , label : 'All' } ,
24+ { id : 'react' , label : 'React' } ,
25+ { id : 'interactive' , label : 'Interactive' } ,
26+ { id : 'layout' , label : 'Layouts' } ,
27+ { id : 'data' , label : 'Data & APIs' } ,
28+ { id : 'languages' , label : 'Languages' } ,
29+ ] as const
30+
31+ function filterTemplates ( category : string ) : Template [ ] {
32+ if ( category === 'all' ) return TEMPLATES
33+ if ( category === 'languages' ) return TEMPLATES . filter ( t => t . category === 'python' || t . category === 'typescript' )
34+ return TEMPLATES . filter ( t => t . category === category || ( category === 'all' && true ) )
35+ }
36+
2137interface TemplateGalleryProps {
2238 isOpen : boolean
2339 onClose : ( ) => void
2440 onSelect ?: ( workspace : unknown ) => void
2541}
2642
2743export default function TemplateGallery ( { isOpen, onClose } : TemplateGalleryProps ) {
44+ const [ activeCategory , setActiveCategory ] = useState ( 'all' )
45+
2846 if ( ! isOpen ) return null
29-
47+
48+ const filtered = filterTemplates ( activeCategory )
49+
3050 return (
3151 < div className = "fixed inset-0 z-50 flex items-center justify-center p-4" >
3252 { /* Backdrop */ }
33- < div
53+ < div
3454 className = "absolute inset-0 bg-black/80 backdrop-blur-sm animate-[fadeIn_0.1s_ease-out]"
3555 onClick = { onClose }
3656 />
37-
57+
3858 { /* Modal */ }
3959 < div className = "relative w-full max-w-4xl max-h-[80vh] overflow-hidden bg-[#0A0A0F] border border-purple-500/30 chamfer" >
4060 { /* Scanlines */ }
4161 < div className = "absolute inset-0 pointer-events-none opacity-30 scanlines" />
42-
62+
4363 { /* Header */ }
4464 < div className = "flex items-center justify-between p-6 border-b border-purple-500/20" >
4565 < div >
4666 < h2 className = "text-xl font-bold uppercase tracking-wider neon-text-purple" >
4767 Select Template
4868 </ h2 >
4969 < p className = "text-sm text-gray-500 mt-1 font-mono" >
50- { '>' } Choose a starting point for your project
70+ { '>' } { filtered . length } template { filtered . length !== 1 ? 's' : '' } available
5171 </ p >
5272 </ div >
5373 < button
@@ -57,10 +77,33 @@ export default function TemplateGallery({ isOpen, onClose }: TemplateGalleryProp
5777 < X className = "w-5 h-5" />
5878 </ button >
5979 </ div >
60-
80+
81+ { /* Category tabs */ }
82+ < div className = "flex items-center gap-1 px-6 py-3 border-b border-purple-500/10 overflow-x-auto" >
83+ { CATEGORIES . map ( ( cat ) => {
84+ const count = cat . id === 'all' ? TEMPLATES . length : filterTemplates ( cat . id ) . length
85+ return (
86+ < button
87+ key = { cat . id }
88+ onClick = { ( ) => setActiveCategory ( cat . id ) }
89+ className = { `flex items-center gap-1.5 px-3 py-1.5 rounded text-sm font-mono whitespace-nowrap transition ${
90+ activeCategory === cat . id
91+ ? 'bg-purple-500/20 text-purple-400 border border-purple-500/30'
92+ : 'text-gray-500 hover:text-gray-300 hover:bg-white/5 border border-transparent'
93+ } `}
94+ >
95+ { cat . label }
96+ < span className = { `text-xs ${ activeCategory === cat . id ? 'text-purple-400/70' : 'text-gray-600' } ` } >
97+ { count }
98+ </ span >
99+ </ button >
100+ )
101+ } ) }
102+ </ div >
103+
61104 { /* Grid */ }
62- < div className = "p-6 overflow-y-auto max-h-[60vh ] grid sm:grid-cols-2 lg:grid-cols-3 gap-4" >
63- { TEMPLATES . map ( ( template ) => (
105+ < div className = "p-6 overflow-y-auto max-h-[55vh ] grid sm:grid-cols-2 lg:grid-cols-3 gap-4" >
106+ { filtered . map ( ( template ) => (
64107 < Link
65108 key = { template . id }
66109 to = { `/ide#${ encodeWorkspace ( template . workspace ) } ` }
0 commit comments