@@ -34,26 +34,27 @@ export default function ScriptGenerator() {
3434 const [ selectedTools , setSelectedTools ] = useState < string [ ] > ( [ ] ) ;
3535 const [ loading , setLoading ] = useState ( true ) ;
3636
37- // Load and parse JSON data on mount
37+ // Theme
38+ const [ theme , setTheme ] = useState < "light" | "dark" > ( "dark" ) ;
3839 useEffect ( ( ) => {
39- const fetchAndParse = async ( ) => {
40- try {
41- const res = await fetch ( "./tools.json" ) ;
42- if ( ! res . ok ) {
43- throw new Error ( 'Failed to fetch JSON file' ) ;
44- }
40+ document . documentElement . setAttribute ( "data-theme" , theme ) ;
41+ } , [ theme ] ) ;
4542
43+ // Fetch tools
44+ useEffect ( ( ) => {
45+ const fetchTools = async ( ) => {
46+ try {
47+ const res = await fetch ( "./tools.json" ) ;
48+ if ( ! res . ok ) throw new Error ( "Failed to fetch JSON" ) ;
4649 const data : ToolCategory [ ] = await res . json ( ) ;
4750 setToolsData ( data ) ;
48- } catch ( error ) {
49- console . error ( "Failed to load JSON data" , error ) ;
50- alert ( 'Failed to load tool data. Please try again later.' ) ;
51+ } catch {
52+ alert ( "Failed to load tool data." ) ;
5153 } finally {
5254 setLoading ( false ) ;
5355 }
5456 } ;
55-
56- fetchAndParse ( ) ;
57+ fetchTools ( ) ;
5758 } , [ ] ) ;
5859
5960 const handleToolSelect = ( toolName : string ) => {
@@ -62,66 +63,69 @@ export default function ScriptGenerator() {
6263 ) ;
6364 } ;
6465
65- const generateScript = ( ) : string => {
66- return toolsData
66+ const generateScript = ( ) =>
67+ toolsData
6768 . flatMap ( ( category ) =>
6869 category . tools
6970 . filter ( ( tool ) => selectedTools . includes ( tool . name ) && tool . install [ selectedPkg ] )
70- . map ( ( tool ) => tool . install [ selectedPkg ] as string )
71+ . map ( ( tool ) => tool . install [ selectedPkg ] ! )
7172 )
7273 . join ( "\n" ) ;
73- } ;
7474
7575 const handleCopy = async ( ) => {
7676 try {
7777 await navigator . clipboard . writeText ( generateScript ( ) ) ;
78- alert ( "Script copied to clipboard !" ) ;
79- } catch ( err ) {
80- alert ( "Failed to copy script: " + err ) ;
78+ alert ( "Script copied!" ) ;
79+ } catch {
80+ alert ( "Failed to copy" ) ;
8181 }
8282 } ;
8383
8484 const handleDownload = ( ) => {
85- const script = generateScript ( ) ;
86- const blob = new Blob ( [ script ] , { type : "text/plain;charset=utf-8" } ) ;
87- const url = URL . createObjectURL ( blob ) ;
85+ const blob = new Blob ( [ generateScript ( ) ] , { type : "text/plain" } ) ;
8886 const link = document . createElement ( "a" ) ;
89- link . href = url ;
87+ link . href = URL . createObjectURL ( blob ) ;
9088 link . download = "install_script.sh" ;
91- document . body . appendChild ( link ) ;
9289 link . click ( ) ;
93- document . body . removeChild ( link ) ;
9490 } ;
9591
96- // Loading UI
97- if ( loading ) {
92+ if ( loading )
9893 return (
99- < div className = "text-white font-sans text-center mt-20" >
94+ < div className = "text-[var(--foreground)] font-sans text-center mt-20" >
10095 < div > Loading tools data...</ div >
101- < div className = "spinner" > ⏳</ div > { /* Optionally add a spinner here */ }
96+ < div className = "spinner" > ⏳</ div >
10297 </ div >
10398 ) ;
104- }
10599
106100 return (
107- < div className = "bg-[#0d1117] min-h-screen text-white font-sans" >
101+ < div
102+ className = "min-h-screen font-sans bg-[var(--background)] text-[var(--foreground)] transition-colors duration-300"
103+ data-theme = { theme }
104+ >
108105 < div className = "max-w-screen mx-auto px-4 py-10" >
109- < h1 className = "relative flex items-center justify-center text-4xl sm:text-6xl font-extrabold mb-10 tracking-tight" >
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" >
110108 DevSetup
111- < a
112- href = "https://forms.gle/cWfDnzvYo5dBuy7C7"
113- target = "_blank"
114- rel = "noopener noreferrer"
115- className = "absolute right-0 top-1/2 -translate-y-1/2 px-6 py-3 text-lg
116- bg-gradient-to-r from-indigo-500 to-purple-600
117- hover:from-indigo-600 hover:to-purple-700
118- text-white font-semibold rounded-full shadow-lg
119- transition duration-200"
120- >
121- Add New +
122- </ a >
123- </ h1 >
109+ < div className = "absolute right-0 top-1/2 -translate-y-1/2 flex gap-3" >
110+ { /* Add New + button */ }
111+ < a
112+ href = "https://forms.gle/cWfDnzvYo5dBuy7C7"
113+ target = "_blank"
114+ rel = "noopener noreferrer"
115+ className = "px-6 py-3 text-lg bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 text-white font-semibold rounded-full shadow-lg transition duration-300"
116+ >
117+ Add New +
118+ </ a >
124119
120+ { /* Theme Toggle button (exactly same size/style as Add +) */ }
121+ < button
122+ onClick = { ( ) => setTheme ( theme === "light" ? "dark" : "light" ) }
123+ 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"
124+ >
125+ { theme === "light" ? "🌞 Light" : "🌙 Dark" }
126+ </ button >
127+ </ div >
128+ </ h1 >
125129
126130 { /* OS Selector */ }
127131 < div className = "flex flex-wrap gap-3 justify-center sm:justify-start mb-6" >
@@ -133,10 +137,10 @@ export default function ScriptGenerator() {
133137 setSelectedPkg ( pkgManagers [ os ] [ 0 ] ) ;
134138 setSelectedTools ( [ ] ) ;
135139 } }
136- className = { `px-4 py-2 rounded-xl text-sm font-semibold transition duration-200 ${
140+ className = { `px-6 py-3 rounded-full text-sm font-semibold transition duration-200 ${
137141 selectedOS === os
138- ? "bg-indigo-600 text-white shadow-lg"
139- : "bg-gray-800 hover:bg-gray-700 text-gray-300 "
142+ ? "bg-gradient-to-r from- indigo-500 to-purple -600 text-white shadow-lg"
143+ : "bg-[var(--button-bg)] text-[var(--button-text)] hover:bg-gray-700"
140144 } `}
141145 >
142146 { os . toUpperCase ( ) }
@@ -153,10 +157,10 @@ export default function ScriptGenerator() {
153157 setSelectedPkg ( pkg ) ;
154158 setSelectedTools ( [ ] ) ;
155159 } }
156- className = { `px-4 py-2 rounded-xl text-sm font-semibold transition duration-200 ${
160+ className = { `px-6 py-3 rounded-full text-sm font-semibold transition duration-200 ${
157161 selectedPkg === pkg
158- ? "bg-green-600 text-white shadow-lg"
159- : "bg-gray-800 hover:bg-gray-700 text-gray-300 "
162+ ? "bg-gradient-to-r from-green-500 to- green-600 text-white shadow-lg"
163+ : "bg-[var(--button-bg)] text-[var(--button-text)] hover:bg-gray-700"
160164 } `}
161165 >
162166 { pkg }
@@ -176,10 +180,8 @@ export default function ScriptGenerator() {
176180 return (
177181 < label
178182 key = { index }
179- className = { `relative flex flex-col items-center justify-center bg-[#161b22] rounded-2xl p-4 transition cursor-pointer border border-transparent ${
180- isAvailable
181- ? "hover:shadow-xl hover:border-indigo-500"
182- : "opacity-40 cursor-not-allowed"
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"
183185 } `}
184186 >
185187 < input
@@ -204,21 +206,21 @@ export default function ScriptGenerator() {
204206 readOnly
205207 value = { generateScript ( ) }
206208 rows = { 6 }
207- className = "w-full bg-[#0d1117 ] text-gray-100 border border-gray-600 rounded-lg p-4 font-mono resize-none"
209+ className = "w-full bg-[var(--card-bg) ] text-[var(--foreground)] border border-gray-600 rounded-lg p-4 font-mono resize-none transition-colors duration-300 "
208210 />
209211 </ div >
210212
211213 { /* Actions */ }
212214 < div className = "mt-6 flex flex-wrap gap-4" >
213215 < button
214216 onClick = { handleCopy }
215- className = "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-xl transition"
217+ className = "px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-full shadow-lg transition duration-300 "
216218 >
217219 Copy to Clipboard
218220 </ button >
219221 < button
220222 onClick = { handleDownload }
221- className = "px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-xl transition"
223+ className = "px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-full shadow-lg transition duration-300 "
222224 >
223225 Download Script
224226 </ button >
0 commit comments