1- import { useState } from 'react'
2- import { toast } from 'sonner'
3- import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
4- import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
5- import type { SearchResult } from '../types'
1+ import { useState } from 'react' ;
2+ import { toast } from 'sonner' ;
3+ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' ;
4+ import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism' ;
5+ import { SearchBox } from './search' ;
6+ import type { SearchResult } from '../types' ;
67
78interface SearchPanelProps {
8- repoId : string
9- apiUrl : string
10- apiKey : string
9+ repoId : string ;
10+ apiUrl : string ;
11+ apiKey : string ;
1112}
1213
1314export function SearchPanel ( { repoId, apiUrl, apiKey } : SearchPanelProps ) {
14- const [ query , setQuery ] = useState ( '' )
15- const [ results , setResults ] = useState < SearchResult [ ] > ( [ ] )
16- const [ loading , setLoading ] = useState ( false )
17- const [ searchTime , setSearchTime ] = useState < number | null > ( null )
18- const [ cached , setCached ] = useState ( false )
15+ const [ query , setQuery ] = useState ( '' ) ;
16+ const [ results , setResults ] = useState < SearchResult [ ] > ( [ ] ) ;
17+ const [ loading , setLoading ] = useState ( false ) ;
18+ const [ searchTime , setSearchTime ] = useState < number | null > ( null ) ;
19+ const [ cached , setCached ] = useState ( false ) ;
20+ const [ hasSearched , setHasSearched ] = useState ( false ) ;
1921
20- const handleSearch = async ( e : React . FormEvent ) => {
21- e . preventDefault ( )
22- if ( ! query . trim ( ) ) return
22+ const handleSearch = async ( ) => {
23+ if ( ! query . trim ( ) ) return ;
2324
24- setLoading ( true )
25- const startTime = Date . now ( )
25+ setLoading ( true ) ;
26+ setHasSearched ( true ) ;
27+ const startTime = Date . now ( ) ;
2628
2729 try {
2830 const response = await fetch ( `${ apiUrl } /search` , {
2931 method : 'POST' ,
3032 headers : {
31- ' Authorization' : `Bearer ${ apiKey } ` ,
32- 'Content-Type' : 'application/json'
33+ Authorization : `Bearer ${ apiKey } ` ,
34+ 'Content-Type' : 'application/json' ,
3335 } ,
3436 body : JSON . stringify ( {
3537 query,
3638 repo_id : repoId ,
37- max_results : 10
38- } )
39- } )
39+ max_results : 10 ,
40+ } ) ,
41+ } ) ;
4042
41- const data = await response . json ( )
42- setResults ( data . results || [ ] )
43- setSearchTime ( Date . now ( ) - startTime )
44- setCached ( data . cached || false )
43+ const data = await response . json ( ) ;
44+ setResults ( data . results || [ ] ) ;
45+ setSearchTime ( Date . now ( ) - startTime ) ;
46+ setCached ( data . cached || false ) ;
4547 } catch ( error ) {
46- console . error ( 'Search error:' , error )
48+ console . error ( 'Search error:' , error ) ;
4749 toast . error ( 'Search failed' , {
48- description : 'Please check your query and try again'
49- } )
50+ description : 'Please check your query and try again' ,
51+ } ) ;
5052 } finally {
51- setLoading ( false )
53+ setLoading ( false ) ;
5254 }
53- }
55+ } ;
5456
5557 return (
5658 < div className = "p-6 space-y-6" >
57- { /* Search */ }
58- < div className = "bg-[#0a0a0c] border border-white/5 rounded-xl p-5" >
59- < form onSubmit = { handleSearch } >
60- < div className = "flex gap-3" >
61- < input
62- type = "text"
63- value = { query }
64- onChange = { ( e ) => setQuery ( e . target . value ) }
65- placeholder = "e.g., authentication middleware, React hooks, database queries..."
66- className = "flex-1 px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 focus:ring-blue-500/20 transition-all"
67- disabled = { loading }
68- autoFocus
69- />
70- < button
71- type = "submit"
72- className = "px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-medium rounded-xl transition-all disabled:opacity-50"
73- disabled = { loading }
74- >
75- { loading ? 'Searching...' : 'Search' }
76- </ button >
77- </ div >
78- < p className = "mt-3 text-xs text-gray-500" >
79- Powered by semantic embeddings - finds code by meaning, not just keywords
80- </ p >
81- </ form >
59+ { /* Search Box */ }
60+ < div className = "card p-5" >
61+ < SearchBox
62+ value = { query }
63+ onChange = { setQuery }
64+ onSubmit = { handleSearch }
65+ loading = { loading }
66+ autoFocus
67+ />
8268
8369 { searchTime !== null && (
84- < div className = "mt-4 pt-4 border-t border-white/5 flex items-center gap-4 text-sm text-gray-400 " >
70+ < div className = "mt-4 pt-4 border-t border-border flex items-center gap-4 text-sm text-text-secondary " >
8571 < span >
86- < span className = "font-semibold text-white " > { results . length } </ span > results
72+ < span className = "font-semibold text-text-primary " > { results . length } </ span > results
8773 </ span >
88- < span className = "text-gray-600 " > •</ span >
74+ < span className = "text-text-muted " > •</ span >
8975 < span >
90- < span className = "font-mono font-semibold text-white " > { searchTime } ms</ span >
76+ < span className = "font-mono font-semibold text-text-primary " > { searchTime } ms</ span >
9177 </ span >
9278 { cached && (
9379 < >
94- < span className = "text-gray-600" > •</ span >
95- < span className = "text-xs bg-green-500/10 text-green-400 border border-green-500/20 px-2 py-0.5 rounded-md" >
96- ⚡ Cached
97- </ span >
80+ < span className = "text-text-muted" > •</ span >
81+ < span className = "badge-success" > ⚡ Cached</ span >
9882 </ >
9983 ) }
10084 </ div >
@@ -104,45 +88,48 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
10488 { /* Results */ }
10589 < div className = "space-y-4" >
10690 { results . map ( ( result , idx ) => (
107- < div key = { idx } className = "bg-[#0a0a0c] border border-white/5 rounded-xl p-5 hover:border-white/10 transition-all group" >
91+ < div
92+ key = { idx }
93+ className = "card p-5 hover:border-border-accent transition-all duration-normal group"
94+ >
10895 { /* Header */ }
10996 < div className = "flex items-start justify-between mb-4" >
11097 < div className = "flex-1" >
11198 < div className = "flex items-center gap-2 mb-1" >
112- < h3 className = "font-mono font-semibold text-sm text-white " >
99+ < h3 className = "font-mono font-semibold text-sm text-text-primary " >
113100 { result . name }
114101 </ h3 >
115- < span className = "px-2 py-0.5 text-[10px] uppercase tracking-wide bg-white/5 text-gray-400 border border-white/10 rounded " >
102+ < span className = "badge-neutral text-[10px] uppercase tracking-wide" >
116103 { result . type . replace ( '_' , ' ' ) }
117104 </ span >
118105 </ div >
119- < p className = "text-xs text-gray-500 font-mono" >
106+ < p className = "text-xs text-text-muted font-mono" >
120107 { result . file_path . split ( '/' ) . slice ( - 3 ) . join ( '/' ) }
121108 </ p >
122109 </ div >
123-
110+
124111 < div className = "flex items-center gap-3" >
125112 < div className = "text-right" >
126- < div className = "text-xs font-mono text-gray-500 " > Match</ div >
127- < div className = "text-sm font-mono font-semibold text-blue-400 " >
113+ < div className = "text-xs font-mono text-text-muted " > Match</ div >
114+ < div className = "text-sm font-mono font-semibold text-accent " >
128115 { ( result . score * 100 ) . toFixed ( 0 ) } %
129116 </ div >
130117 </ div >
131118 < button
132119 onClick = { ( e ) => {
133- e . stopPropagation ( )
134- navigator . clipboard . writeText ( result . code )
135- toast . success ( 'Code copied!' )
120+ e . stopPropagation ( ) ;
121+ navigator . clipboard . writeText ( result . code ) ;
122+ toast . success ( 'Code copied!' ) ;
136123 } }
137- className = "px-3 py-1.5 text-sm text-gray-400 hover:text-white bg-white/5 hover:bg-white/10 rounded-lg opacity-0 group-hover:opacity-100 transition-all "
124+ className = "btn-ghost px-3 py-1.5 text-sm opacity-0 group-hover:opacity-100"
138125 title = "Copy code"
139126 >
140127 Copy
141128 </ button >
142129 </ div >
143130 </ div >
144131
145- { /* Code with Syntax Highlighting */ }
132+ { /* Code */ }
146133 < div className = "relative rounded-lg overflow-hidden" >
147134 < SyntaxHighlighter
148135 language = { result . language }
@@ -152,47 +139,45 @@ export function SearchPanel({ repoId, apiUrl, apiKey }: SearchPanelProps) {
152139 borderRadius : '0.5rem' ,
153140 fontSize : '0.75rem' ,
154141 lineHeight : '1.5' ,
155- background : '#0d0d0f ' ,
142+ background : 'var(--color-bg-secondary) ' ,
156143 } }
157144 showLineNumbers
158145 startingLineNumber = { result . line_start }
159146 >
160147 { result . code }
161148 </ SyntaxHighlighter >
162-
149+
163150 < div className = "absolute top-3 right-3" >
164- < span className = "px-2 py-0.5 text-[10px] font-mono uppercase bg-black/50 text-gray-400 backdrop-blur rounded" >
151+ < span className = "px-2 py-0.5 text-[10px] font-mono uppercase glass text-text-muted rounded" >
165152 { result . language }
166153 </ span >
167154 </ div >
168155 </ div >
169156
170- { /* Metadata */ }
171- < div className = "mt-3 flex items-center gap-3 text-xs text-gray-500 " >
157+ { /* Footer */ }
158+ < div className = "mt-3 flex items-center gap-3 text-xs text-text-muted " >
172159 < span className = "font-mono" >
173160 Lines { result . line_start } –{ result . line_end }
174161 </ span >
175- < span className = "text-gray-600" > •</ span >
176- < span className = "text-gray-500 truncate" >
177- { result . file_path }
178- </ span >
162+ < span > •</ span >
163+ < span className = "truncate" > { result . file_path } </ span >
179164 </ div >
180165 </ div >
181166 ) ) }
182167 </ div >
183168
184169 { /* Empty State */ }
185- { results . length === 0 && query && ! loading && (
186- < div className = "bg-[#0a0a0c] border border-white/5 rounded-xl p-16 text-center" >
187- < div className = "w-20 h-20 mx-auto mb-4 rounded-2xl bg-white/5 flex items-center justify-center" >
170+ { results . length === 0 && hasSearched && ! loading && (
171+ < div className = "card p-16 text-center" >
172+ < div className = "w-20 h-20 mx-auto mb-4 rounded-2xl glass flex items-center justify-center" >
188173 < span className = "text-4xl" > 🔍</ span >
189174 </ div >
190- < h3 className = "text-base font-semibold mb-2 text-white " > No results found</ h3 >
191- < p className = "text-sm text-gray-400 " >
175+ < h3 className = "text-base font-semibold mb-2 text-text-primary " > No results found</ h3 >
176+ < p className = "text-sm text-text-secondary " >
192177 Try a different query or check if the repository is fully indexed
193178 </ p >
194179 </ div >
195180 ) }
196181 </ div >
197- )
182+ ) ;
198183}
0 commit comments