@@ -33,6 +33,7 @@ export default function ScriptGenerator() {
3333 const [ selectedPkg , setSelectedPkg ] = useState < PkgManager > ( "choco" ) ;
3434 const [ selectedTools , setSelectedTools ] = useState < string [ ] > ( [ ] ) ;
3535 const [ loading , setLoading ] = useState ( true ) ;
36+ const [ searchQuery , setSearchQuery ] = useState < string > ( "" ) ;
3637
3738 // Theme
3839 const [ theme , setTheme ] = useState < "light" | "dark" > ( "dark" ) ;
@@ -103,10 +104,39 @@ export default function ScriptGenerator() {
103104 data-theme = { theme }
104105 >
105106 < div className = "max-w-screen mx-auto px-4 py-10" >
106- { /* Header with Add + and theme toggle */ }
107- < h1 className = "flex items-center justify-center text-4xl sm:text-6xl font-extrabold mb-10 relative tracking-tight" >
108- DevSetup
109- < div className = "absolute right-0 top-1/2 -translate-y-1/2 flex gap-3" >
107+ { /* Header with Search, Add + and theme toggle */ }
108+ < div className = "flex items-center justify-between mb-10" >
109+ { /* Left side - Search Box */ }
110+ < div className = "relative w-64 hidden sm:block" >
111+ < input
112+ type = "text"
113+ placeholder = "Search tools..."
114+ value = { searchQuery }
115+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
116+ className = "w-full px-4 py-2 pl-10 bg-[var(--card-bg)] text-[var(--foreground)] border border-gray-600 rounded-full focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all duration-200 text-sm"
117+ />
118+ < svg
119+ className = "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400"
120+ fill = "none"
121+ stroke = "currentColor"
122+ viewBox = "0 0 24 24"
123+ >
124+ < path
125+ strokeLinecap = "round"
126+ strokeLinejoin = "round"
127+ strokeWidth = { 2 }
128+ d = "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
129+ />
130+ </ svg >
131+ </ div >
132+
133+ { /* Center - Title */ }
134+ < h1 className = "text-4xl sm:text-6xl font-extrabold tracking-tight" >
135+ DevSetup
136+ </ h1 >
137+
138+ { /* Right side - Action buttons */ }
139+ < div className = "flex gap-3" >
110140 { /* Add New + button */ }
111141 < a
112142 href = "https://forms.gle/cWfDnzvYo5dBuy7C7"
@@ -117,15 +147,41 @@ export default function ScriptGenerator() {
117147 Add New +
118148 </ a >
119149
120- { /* Theme Toggle button (exactly same size/style as Add +) */ }
150+ { /* Theme Toggle button */ }
121151 < button
122152 onClick = { ( ) => setTheme ( theme === "light" ? "dark" : "light" ) }
123153 className = "px-6 py-3 text-lg bg-gradient-to-r from-gray-500 to-gray-700 hover:from-gray-600 hover:to-gray-800 text-white font-semibold rounded-full shadow-lg transition duration-300"
124154 >
125155 { theme === "light" ? "🌞 Light" : "🌙 Dark" }
126156 </ button >
127157 </ div >
128- </ h1 >
158+ </ div >
159+
160+ { /* Mobile Search Box */ }
161+ < div className = "mb-6 sm:hidden" >
162+ < div className = "relative" >
163+ < input
164+ type = "text"
165+ placeholder = "Search tools..."
166+ value = { searchQuery }
167+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
168+ className = "w-full px-4 py-3 pl-10 bg-[var(--card-bg)] text-[var(--foreground)] border border-gray-600 rounded-full focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all duration-200"
169+ />
170+ < svg
171+ className = "absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400"
172+ fill = "none"
173+ stroke = "currentColor"
174+ viewBox = "0 0 24 24"
175+ >
176+ < path
177+ strokeLinecap = "round"
178+ strokeLinejoin = "round"
179+ strokeWidth = { 2 }
180+ d = "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
181+ />
182+ </ svg >
183+ </ div >
184+ </ div >
129185
130186 { /* OS Selector */ }
131187 < div className = "flex flex-wrap gap-3 justify-center sm:justify-start mb-6" >
@@ -169,35 +225,45 @@ export default function ScriptGenerator() {
169225 </ div >
170226
171227 { /* Tool Categories */ }
172- { toolsData . map ( ( group , i ) => (
173- < div key = { i } className = "mb-10" >
174- < h2 className = "text-2xl font-semibold mb-4 border-b border-gray-700 pb-1" >
175- { group . category }
176- </ h2 >
177- < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 gap-6" >
178- { group . tools . map ( ( tool , index ) => {
179- const isAvailable = ! ! tool . install [ selectedPkg ] ;
180- return (
181- < label
182- key = { index }
183- className = { `relative flex flex-col items-center justify-center rounded-2xl p-4 transition cursor-pointer border border-transparent bg-[var(--card-bg)] ${
184- isAvailable ? "hover:shadow-xl hover:border-indigo-500" : "opacity-50 cursor-not-allowed"
185- } `}
186- >
187- < input
188- type = "checkbox"
189- disabled = { ! isAvailable }
190- checked = { selectedTools . includes ( tool . name ) }
191- onChange = { ( ) => handleToolSelect ( tool . name ) }
192- className = "absolute top-2 right-2 w-5 h-5 accent-indigo-500"
193- />
194- < span className = "text-sm text-center" > { tool . name } </ span >
195- </ label >
196- ) ;
197- } ) }
228+ { toolsData . map ( ( group , i ) => {
229+ // Filter tools based on search query
230+ const filteredTools = group . tools . filter ( tool =>
231+ tool . name . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) )
232+ ) ;
233+
234+ // Only render category if it has matching tools
235+ if ( filteredTools . length === 0 ) return null ;
236+
237+ return (
238+ < div key = { i } className = "mb-10" >
239+ < h2 className = "text-2xl font-semibold mb-4 border-b border-gray-700 pb-1" >
240+ { group . category }
241+ </ h2 >
242+ < div className = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 gap-6" >
243+ { filteredTools . map ( ( tool , index ) => {
244+ const isAvailable = ! ! tool . install [ selectedPkg ] ;
245+ return (
246+ < label
247+ key = { index }
248+ className = { `relative flex flex-col items-center justify-center rounded-2xl p-4 transition cursor-pointer border border-transparent bg-[var(--card-bg)] ${
249+ isAvailable ? "hover:shadow-xl hover:border-indigo-500" : "opacity-50 cursor-not-allowed"
250+ } `}
251+ >
252+ < input
253+ type = "checkbox"
254+ disabled = { ! isAvailable }
255+ checked = { selectedTools . includes ( tool . name ) }
256+ onChange = { ( ) => handleToolSelect ( tool . name ) }
257+ className = "absolute top-2 right-2 w-5 h-5 accent-indigo-500"
258+ />
259+ < span className = "text-sm text-center" > { tool . name } </ span >
260+ </ label >
261+ ) ;
262+ } ) }
263+ </ div >
198264 </ div >
199- </ div >
200- ) ) }
265+ ) ;
266+ } ) }
201267
202268 { /* Script Output */ }
203269 < div className = "mt-12" >
0 commit comments