11"use client" ;
22
3- import { useState , useEffect } from "react" ;
3+ import { useState , useEffect , useRef } from "react" ;
4+ import Link from "next/link" ;
45import { useRouter } from "next/navigation" ;
56import SavedTestsList from "@/components/SavedTestsList" ;
67import { useToast } from "@/components/ToastProvider" ;
78import { useConfirmDialog } from "@/components/ConfirmDialogProvider" ;
89import { buildAutoContext } from "@/lib/autoContext" ;
910import { useSettings } from "@/components/SettingsProvider" ;
1011import { filterTestCasesBySettings } from "@/lib/testCaseUtils" ;
12+ import { useThemeMode } from "@/components/ThemeProvider" ;
1113
1214export default function Home ( ) {
1315 const [ input , setInput ] = useState ( "" ) ;
@@ -18,10 +20,23 @@ export default function Home() {
1820 const [ needsManualContext , setNeedsManualContext ] = useState ( false ) ;
1921 const [ requirements , setRequirements ] = useState ( "" ) ;
2022 const [ showRequirementsInput , setShowRequirementsInput ] = useState ( false ) ;
23+ const [ showDesktopMenu , setShowDesktopMenu ] = useState ( false ) ;
24+ const controlsRef = useRef < HTMLDivElement | null > ( null ) ;
2125 const router = useRouter ( ) ;
2226 const { addToast } = useToast ( ) ;
2327 const { confirm } = useConfirmDialog ( ) ;
2428 const { settings } = useSettings ( ) ;
29+ const { themeMode, themeLabel, toggleTheme, mounted } = useThemeMode ( ) ;
30+
31+ useEffect ( ( ) => {
32+ const handleClickOutside = ( event : MouseEvent ) => {
33+ if ( controlsRef . current && ! controlsRef . current . contains ( event . target as Node ) ) {
34+ setShowDesktopMenu ( false ) ;
35+ }
36+ } ;
37+ document . addEventListener ( "mousedown" , handleClickOutside ) ;
38+ return ( ) => document . removeEventListener ( "mousedown" , handleClickOutside ) ;
39+ } , [ ] ) ;
2540
2641 useEffect ( ( ) => {
2742 const savedKey = localStorage . getItem ( "openai_api_key" ) ;
@@ -197,18 +212,61 @@ export default function Home() {
197212 } ;
198213
199214 return (
200- < div className = "min-h-screen flex items-center justify-center px-4 bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-slate-900 dark:to-slate-800" >
201- < div className = "max-w-2xl w-full" >
202- < div className = "bg-white dark:bg-slate-800 rounded-2xl shadow-2xl p-8 md:p -12" >
215+ < div className = "relative min-h-[100dvh] bg-gradient-to-br from-blue-50 to-indigo-100 px-0 dark:from-slate-900 dark:to-slate-800" >
216+ < div className = "mx-auto flex min-h-[100dvh] w-full max-w-3xl items-stretch px-0 pb-[env(safe-area-inset-bottom)] pt-[env(safe-area-inset-top)] sm:px-6 sm:pb-12 sm:pt-12 " >
217+ < div className = "relative flex w-full flex-1 flex-col rounded-none bg-white px-5 py-7 shadow-none dark:bg-slate-800 sm: rounded-2xl sm:px-8 sm:py-10 sm:shadow-2xl md:px -12" >
203218 { /* Header */ }
204- < div className = "text-center mb-8 " >
205- < h1 className = "text-4xl md:text-5xl font-bold bg-gradient-to-r from-blue-600 to-indigo-600 dark:from-blue-400 dark:to-indigo-400 bg-clip-text text-transparent mb-4 " >
219+ < div className = "text-center mb-6 " >
220+ < h1 className = "text-4xl md:text-5xl font-bold bg-gradient-to-r from-blue-600 to-indigo-600 dark:from-blue-400 dark:to-indigo-400 bg-clip-text text-transparent mb-3 " >
206221 Assertify
207222 </ h1 >
208223 < p className = "text-lg text-slate-600 dark:text-slate-300" >
209224 AI-powered test case generation with boilerplate code
210225 </ p >
211226 </ div >
227+ { mounted && (
228+ < div
229+ ref = { controlsRef }
230+ className = "absolute right-4 top-4 hidden sm:flex flex-col items-end gap-2"
231+ >
232+ < button
233+ onClick = { ( ) => setShowDesktopMenu ( ( prev ) => ! prev ) }
234+ className = "flex h-10 w-10 items-center justify-center rounded-full border border-slate-200 bg-white/90 text-slate-700 shadow-sm backdrop-blur transition-colors hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800/90 dark:text-slate-200 dark:hover:bg-slate-700"
235+ aria-label = "Open controls"
236+ title = "Settings"
237+ >
238+ < i className = "fas fa-gear text-base" aria-hidden = "true" > </ i >
239+ </ button >
240+ { showDesktopMenu && (
241+ < div className = "flex items-center gap-2 rounded-full border border-slate-200 bg-white/95 px-2 py-2 text-sm shadow-xl backdrop-blur dark:border-slate-700 dark:bg-slate-800/95" >
242+ < Link
243+ href = "/settings"
244+ className = "flex h-10 w-10 items-center justify-center rounded-full text-slate-700 transition-colors hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-700"
245+ aria-label = "Open settings"
246+ title = "Settings"
247+ >
248+ < i className = "fas fa-gear" aria-hidden = "true" > </ i >
249+ </ Link >
250+ < button
251+ onClick = { ( ) => {
252+ toggleTheme ( ) ;
253+ } }
254+ className = "flex h-10 w-10 items-center justify-center rounded-full text-slate-700 transition-colors hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-700"
255+ title = { `Switch theme (current: ${ themeLabel } )` }
256+ aria-label = "Toggle theme mode"
257+ >
258+ { themeMode === "system" ? (
259+ < i className = "fas fa-circle-half-stroke" aria-hidden = "true" > </ i >
260+ ) : themeMode === "dark" ? (
261+ < i className = "fas fa-moon" aria-hidden = "true" > </ i >
262+ ) : (
263+ < i className = "fas fa-sun" aria-hidden = "true" > </ i >
264+ ) }
265+ </ button >
266+ </ div >
267+ ) }
268+ </ div >
269+ ) }
212270
213271 { /* API Key Section */ }
214272 < div className = "mb-8 p-4 bg-blue-50 dark:bg-blue-900 rounded-lg border-l-4 border-blue-600" >
@@ -366,20 +424,20 @@ export default function Home() {
366424 </ button >
367425
368426 { /* Features */ }
369- < div className = "grid grid-cols-3 gap-4 mt-12 pt-8 border-t border-slate-200 dark:border-slate-700" >
370- < div className = "text-center" >
427+ < div className = "grid grid-cols-1 gap-4 mt-12 pt-8 border-t border-slate-200 dark:border-slate-700 sm:grid-cols-3 " >
428+ < div className = "text-center rounded-xl bg-slate-50/60 p-4 dark:bg-slate-900/30 sm:bg-transparent sm:p-0 " >
371429 < div className = "text-3xl mb-2 text-blue-600 dark:text-blue-400" >
372430 < i className = "fas fa-flask" > </ i >
373431 </ div >
374432 < p className = "text-sm text-slate-600 dark:text-slate-400" > 5 Test Types</ p >
375433 </ div >
376- < div className = "text-center" >
434+ < div className = "text-center rounded-xl bg-slate-50/60 p-4 dark:bg-slate-900/30 sm:bg-transparent sm:p-0 " >
377435 < div className = "text-3xl mb-2 text-green-600 dark:text-green-400" >
378436 < i className = "fas fa-code" > </ i >
379437 </ div >
380438 < p className = "text-sm text-slate-600 dark:text-slate-400" > 8 Frameworks</ p >
381439 </ div >
382- < div className = "text-center" >
440+ < div className = "text-center rounded-xl bg-slate-50/60 p-4 dark:bg-slate-900/30 sm:bg-transparent sm:p-0 " >
383441 < div className = "text-3xl mb-2 text-yellow-600 dark:text-yellow-400" >
384442 < i className = "fas fa-bolt" > </ i >
385443 </ div >
0 commit comments