@@ -388,7 +388,10 @@ export function Home() {
388388
389389 // Track which repo dropdown is open
390390 const [ openRepoDropdown , setOpenRepoDropdown ] = useState < string | null > ( null ) ;
391+ const [ repoDropdownPosition , setRepoDropdownPosition ] = useState ( { top : 0 , left : 0 } ) ;
391392 const [ showAddRepo , setShowAddRepo ] = useState ( false ) ;
393+ const [ addRepoButtonRef , setAddRepoButtonRef ] = useState < HTMLButtonElement | null > ( null ) ;
394+ const [ addRepoDropdownPosition , setAddRepoDropdownPosition ] = useState ( { top : 0 , right : 0 } ) ;
392395
393396 // Show loading/error state while GitHub client initializes
394397 if ( ! githubReady ) {
@@ -457,13 +460,25 @@ export function Home() {
457460 < div
458461 role = "button"
459462 tabIndex = { 0 }
460- onClick = { ( ) => {
463+ onClick = { ( e ) => {
464+ if ( ! isOpen ) {
465+ const rect = e . currentTarget . getBoundingClientRect ( ) ;
466+ setRepoDropdownPosition ( {
467+ top : rect . bottom + 4 ,
468+ left : rect . left ,
469+ } ) ;
470+ }
461471 setOpenRepoDropdown ( isOpen ? null : repo . name ) ;
462472 setShowAddRepo ( false ) ;
463473 } }
464474 onKeyDown = { ( e ) => {
465475 if ( e . key === "Enter" || e . key === " " ) {
466476 e . preventDefault ( ) ;
477+ const rect = e . currentTarget . getBoundingClientRect ( ) ;
478+ setRepoDropdownPosition ( {
479+ top : rect . bottom + 4 ,
480+ left : rect . left ,
481+ } ) ;
467482 setOpenRepoDropdown ( isOpen ? null : repo . name ) ;
468483 setShowAddRepo ( false ) ;
469484 }
@@ -499,34 +514,47 @@ export function Home() {
499514 </ div >
500515
501516 { isOpen && (
502- < div className = "absolute top-full left-0 mt-1 w-56 bg-card border border-border rounded-lg shadow-xl z-30 overflow-hidden max-w-[calc(100vw-1rem)] sm:max-w-none" >
503- { availableModes . map ( ( option ) => (
504- < button
505- key = { option . value }
506- onClick = { ( ) => {
507- handleRepoModeChange ( repo . name , option . value ) ;
508- setOpenRepoDropdown ( null ) ;
509- } }
510- className = { cn (
511- "w-full flex items-start gap-2.5 px-3 py-2 hover:bg-muted/50 transition-colors text-left" ,
512- repo . mode === option . value && "bg-muted/50"
513- ) }
514- >
515- < option . icon className = "w-3.5 h-3.5 mt-0.5 shrink-0" />
516- < div className = "flex-1 min-w-0" >
517- < div className = "font-medium text-xs" >
518- { option . label }
519- </ div >
520- < div className = "text-[10px] text-muted-foreground" >
521- { option . description }
517+ < >
518+ { /* Backdrop to close dropdown when clicking outside */ }
519+ < div
520+ className = "fixed inset-0 z-40"
521+ onClick = { ( ) => setOpenRepoDropdown ( null ) }
522+ />
523+ < div
524+ className = "fixed w-56 bg-card border border-border rounded-lg shadow-xl z-50 max-w-[calc(100vw-1rem)] sm:max-w-none"
525+ style = { {
526+ top : repoDropdownPosition . top ,
527+ left : repoDropdownPosition . left ,
528+ } }
529+ >
530+ { availableModes . map ( ( option ) => (
531+ < button
532+ key = { option . value }
533+ onClick = { ( ) => {
534+ handleRepoModeChange ( repo . name , option . value ) ;
535+ setOpenRepoDropdown ( null ) ;
536+ } }
537+ className = { cn (
538+ "w-full flex items-start gap-2.5 px-3 py-2 hover:bg-muted/50 transition-colors text-left" ,
539+ repo . mode === option . value && "bg-muted/50"
540+ ) }
541+ >
542+ < option . icon className = "w-3.5 h-3.5 mt-0.5 shrink-0" />
543+ < div className = "flex-1 min-w-0" >
544+ < div className = "font-medium text-xs" >
545+ { option . label }
546+ </ div >
547+ < div className = "text-[10px] text-muted-foreground" >
548+ { option . description }
549+ </ div >
522550 </ div >
523- </ div >
524- { repo . mode === option . value && (
525- < Check className = "w-3.5 h-3.5 text-primary mt-0.5" />
526- ) }
527- </ button >
528- ) ) }
529- </ div >
551+ { repo . mode === option . value && (
552+ < Check className = "w-3.5 h-3.5 text-primary mt-0.5" />
553+ ) }
554+ </ button >
555+ ) ) }
556+ </ div >
557+ </ >
530558 ) }
531559 </ div >
532560 ) ;
@@ -550,7 +578,15 @@ export function Home() {
550578 { /* Add Repo Button */ }
551579 < div className = "relative shrink-0" >
552580 < button
581+ ref = { setAddRepoButtonRef }
553582 onClick = { ( ) => {
583+ if ( ! showAddRepo && addRepoButtonRef ) {
584+ const rect = addRepoButtonRef . getBoundingClientRect ( ) ;
585+ setAddRepoDropdownPosition ( {
586+ top : rect . bottom + 4 ,
587+ right : window . innerWidth - rect . right ,
588+ } ) ;
589+ }
554590 setShowAddRepo ( ! showAddRepo ) ;
555591 setOpenRepoDropdown ( null ) ;
556592 } }
@@ -567,31 +603,40 @@ export function Home() {
567603
568604 { /* Search Dropdown */ }
569605 { showAddRepo && (
570- < div className = "absolute top-full right-0 mt-1 w-72 max-w-[calc(100vw-1rem)] bg-card border border-border rounded-lg shadow-xl overflow-hidden z-30" >
571- < div className = "p-2 border-b border-border" >
572- < div className = "relative" >
573- < input
574- type = "text"
575- value = { searchQuery }
576- onChange = { ( e ) => setSearchQuery ( e . target . value ) }
577- onBlur = { ( e ) => {
578- // Close dropdown if not clicking inside it
579- if ( ! e . relatedTarget ?. closest ( ".add-repo-dropdown" ) ) {
580- setTimeout ( ( ) => setShowAddRepo ( false ) , 150 ) ;
581- }
582- } }
583- placeholder = "Search repositories..."
584- className = "w-full h-7 pl-7 pr-3 rounded-md border border-border bg-muted/50 text-xs placeholder:text-muted-foreground/60 focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent"
585- autoFocus
586- />
587- < Search className = "absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground" />
588- { searching && (
589- < Loader2 className = "absolute right-2 top-1/2 -translate-y-1/2 w-3 h-3 animate-spin text-muted-foreground" />
590- ) }
606+ < >
607+ { /* Backdrop to close dropdown when clicking outside */ }
608+ < div
609+ className = "fixed inset-0 z-40"
610+ onClick = { ( ) => {
611+ setShowAddRepo ( false ) ;
612+ setSearchQuery ( "" ) ;
613+ } }
614+ />
615+ < div
616+ className = "fixed w-72 max-w-[calc(100vw-1rem)] bg-card border border-border rounded-lg shadow-xl z-50"
617+ style = { {
618+ top : addRepoDropdownPosition . top ,
619+ right : addRepoDropdownPosition . right ,
620+ } }
621+ >
622+ < div className = "p-2 border-b border-border" >
623+ < div className = "relative" >
624+ < input
625+ type = "text"
626+ value = { searchQuery }
627+ onChange = { ( e ) => setSearchQuery ( e . target . value ) }
628+ placeholder = "Search repositories..."
629+ className = "w-full h-7 pl-7 pr-3 rounded-md border border-border bg-muted/50 text-xs placeholder:text-muted-foreground/60 focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent"
630+ autoFocus
631+ />
632+ < Search className = "absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-muted-foreground" />
633+ { searching && (
634+ < Loader2 className = "absolute right-2 top-1/2 -translate-y-1/2 w-3 h-3 animate-spin text-muted-foreground" />
635+ ) }
636+ </ div >
591637 </ div >
592- </ div >
593638
594- < div className = "add-repo-dropdown max-h-64 overflow-auto" >
639+ < div className = "add-repo-dropdown max-h-64 overflow-auto" >
595640 { /* All Repos option - always shown at top when not already added */ }
596641 { ! config . repos . some ( isAllReposFilter ) && ! searchQuery && (
597642 < button
@@ -650,6 +695,7 @@ export function Home() {
650695 ) }
651696 </ div >
652697 </ div >
698+ </ >
653699 ) }
654700 </ div >
655701 </ div >
0 commit comments