1111 {"host" : os .getenv ("TYPESENSE_HOST" ), "port" : "443" , "protocol" : "https" }
1212 ],
1313 "api_key" : os .getenv ("TYPESENSE_SEARCH_API_KEY" ),
14- "connection_timeout_seconds" : 10 ,
14+ "connection_timeout_seconds" : 2 ,
1515}
1616
1717# Enhanced search parameters with component-aware boosting
1818BASE_SEARCH_PARAMS = {
19- "per_page" : 20 ,
19+ "per_page" : 15 ,
2020 "highlight_full_fields" : "title,content,components" ,
21- "snippet_threshold" : 30 ,
21+ "snippet_threshold" : 20 ,
2222 "num_typos" : 2 ,
2323 "typo_tokens_threshold" : 1 ,
2424 "drop_tokens_threshold" : 1 ,
7676)
7777
7878# Styling for highlights
79- HIGHLIGHT_STYLE = '<span style="background-color: var(--violet-3); color: var(--violet-11); padding: 2px 4px; border-radius: 3px;">'
79+ HIGHLIGHT_STYLE = '<span class="bg-violet-3 text-violet-11 px-1 py-0.5 rounded-[3px]">'
80+
81+ client = typesense .Client (TYPESENSE_CONFIG )
8082
8183
8284class TypesenseSearchState (rx .State ):
8385 """Enhanced state management for the Typesense search component."""
8486
8587 # State variables
86- search_query : str = ""
87- search_results : list [dict ] = []
88- is_searching : bool = False
88+ search_query : rx . Field [ str ] = rx . field ( "" )
89+ search_results : rx . Field [ list [dict ]] = rx . field ([])
90+ is_searching : rx . Field [ bool ] = rx . field ( False )
8991 show_results : bool = False
90- show_modal : bool = False
91- selected_filter : str = "All"
92- filter_categories : list [str ] = FILTER_CATEGORIES
93- suggestions : list [dict ] = DEFAULT_SUGGESTIONS
94-
95- def open_modal (self ):
96- """Open the search modal and reset filter state."""
97- self .show_modal = True
98- self .selected_filter = "All"
92+ selected_filter : rx .Field [str ] = rx .field ("All" )
9993
94+ @rx .event (temporal = True )
10095 def close_modal (self ):
10196 """Close the search modal and reset state."""
102- self .show_modal = False
103- self ._reset_search_state ()
104-
105- def _reset_search_state (self ):
106- """Reset all search-related state variables."""
107- self .show_results = False
108- self .is_searching = False
109- self .search_query = ""
110- self .search_results = []
111- self .selected_filter = "All"
97+ self .reset ()
11298
99+ @rx .event (temporal = True )
113100 async def set_filter (self , filter_name : str ):
114101 """Set the selected filter and re-run search if there's an active query."""
115102 self .selected_filter = filter_name
116103 if self .search_query .strip ():
117- await self .search_docs (self .search_query )
104+ yield TypesenseSearchState .search_docs (self .search_query )
118105
119106 def _get_filter_sections (self ) -> list [str ]:
120107 """Get sections for current filter."""
@@ -130,29 +117,35 @@ def _expand_query_variants(self, query: str) -> str:
130117 variants = {cleaned , f"rx.{ cleaned } " , f"reflex.{ cleaned } " }
131118 return " " .join (sorted (variants )) # Order doesn't matter
132119
120+ @rx .event (background = True , temporal = True )
133121 async def search_docs (self , query : str ):
134122 """Enhanced search with component-aware logic."""
135- self .search_query = query
123+ async with self :
124+ self .search_query = query
136125
137- if not query .strip ():
138- self ._clear_search_results ()
139- return
126+ if not query .strip ():
127+ self ._clear_search_results ()
128+ return
140129
141- self .is_searching = True
130+ self .is_searching = True
131+ yield
142132
143133 try :
144134 results = await self ._perform_unified_search (query )
145- self .search_results = self ._format_search_results (results )
146- self .show_results = True
147- except Exception as e :
148- print (f"Search error: { e } " )
149- self ._clear_search_results ()
150-
151- self .is_searching = False
135+ formatted_results = self ._format_search_results (results )
136+ async with self :
137+ self .search_results = formatted_results
138+ self .show_results = True
139+ except Exception :
140+ async with self :
141+ self ._clear_search_results ()
142+
143+ finally :
144+ async with self :
145+ self .is_searching = False
152146
153147 async def _perform_unified_search (self , query : str ) -> dict :
154148 """Perform a single search using is_component metadata for boosting/filtering."""
155- client = typesense .Client (TYPESENSE_CONFIG )
156149
157150 expanded_query = self ._expand_query_variants (query )
158151
@@ -191,20 +184,14 @@ def _format_search_results(self, result: dict) -> list[dict]:
191184 for hit in result ["hits" ]:
192185 doc = hit ["document" ]
193186 components = doc .get ("components" , [])
194- component_info = None
195- if components :
196- component_info = f"Components: { ', ' .join (components )} "
197187 formatted_result = {
198188 "title" : doc ["title" ],
199189 "content" : self ._get_highlighted_content (hit ),
200190 "url" : doc ["url" ],
201191 "path" : doc ["path" ],
202192 "section" : doc .get ("section" , "" ),
203- "subsection" : doc .get ("subsection" , "" ),
204193 "breadcrumb" : doc .get ("breadcrumb" , "" ),
205194 "components" : components ,
206- "component_info" : component_info ,
207- "score" : hit .get ("text_match" , 0 ),
208195 }
209196 formatted_results .append (formatted_result )
210197
@@ -244,24 +231,14 @@ def fix_component_highlighting(text):
244231 )
245232
246233 # Fallback to truncated plain content
247- return self ._truncate_content (hit ["document" ][ "content" ] )
234+ return self ._truncate_content (hit ["document" ]. get ( "content" , "" ) )
248235
249236 def _truncate_content (self , content : str , max_length : int = 150 ) -> str :
250237 """Truncate content to specified length."""
251238 if len (content ) <= max_length :
252239 return content
253240 return content [:max_length ] + "..."
254241
255- def hide_results (self ):
256- """Hide search results."""
257- self .show_results = False
258-
259- def navigate_to_result (self , url : str ):
260- """Navigate to a search result."""
261- self .show_results = False
262- self .show_modal = False
263- return rx .redirect (url )
264-
265242
266243# Component functions (keeping your existing UI components)
267244def filter_pill (filter_name : str ) -> rx .Component :
@@ -284,23 +261,26 @@ def filter_pill(filter_name: str) -> rx.Component:
284261def filter_pills () -> rx .Component :
285262 """Render the filter pills container."""
286263 return rx .box (
287- rx . foreach ( TypesenseSearchState . filter_categories , filter_pill ) ,
264+ * [ filter_pill ( filter_name ) for filter_name in FILTER_CATEGORIES ] ,
288265 class_name = "hidden md:flex md:flex-row gap-x-3 pt-2 overflow-x-auto justify-start w-full" ,
289266 )
290267
291268
292269def suggestion_item (title : str , url : str , icon : str = "book-open" ) -> rx .Component :
293270 """Render a single suggestion item."""
294- return rx .box (
271+ return rx .el . a (
295272 rx .hstack (
296- rx .icon (icon , size = 16 , color = "var(--c- slate-9) " ),
273+ rx .icon (icon , size = 16 , class_name = "!text- slate-9" ),
297274 rx .text (
298- title , font_weight = "500" , color = "var(--c-slate-12)" , font_size = "14px"
275+ title ,
276+ font_weight = "500" ,
277+ class_name = "!text-slate-12" ,
278+ font_size = "14px" ,
299279 ),
300280 spacing = "2" ,
301281 align_items = "center" ,
302282 ),
303- on_click = lambda : TypesenseSearchState . navigate_to_result ( url ) ,
283+ to = url ,
304284 class_name = "w-full border-b border-slate-4 hover:bg-slate-3 cursor-pointer rounded-[6px] py-2 px-2" ,
305285 )
306286
@@ -353,7 +333,7 @@ def suggestions_section() -> rx.Component:
353333
354334def search_result_item (result : rx .Var ) -> rx .Component :
355335 """Enhanced search result item with component information."""
356- return rx .box (
336+ return rx .el . a (
357337 rx .vstack (
358338 rx .text (
359339 result ["breadcrumb" ],
@@ -381,7 +361,7 @@ def search_result_item(result: rx.Var) -> rx.Component:
381361 width = "100%" ,
382362 ),
383363 class_name = "p-2 border border-slate-4 rounded-[8px] cursor-pointer w-full hover:border-slate-5 hover:bg-slate-2 shadow-small" ,
384- on_click = lambda : TypesenseSearchState . navigate_to_result ( result ["url" ]) ,
364+ to = result ["url" ],
385365 )
386366
387367
@@ -466,8 +446,7 @@ def search_trigger() -> rx.Component:
466446 "max_width" : ["6em" , "6em" , "none" ],
467447 "box_shadow" : "0px 24px 12px 0px rgba(28, 32, 36, 0.02), 0px 8px 8px 0px rgba(28, 32, 36, 0.02), 0px 2px 6px 0px rgba(28, 32, 36, 0.02)" ,
468448 },
469- class_name = "w-full hover:bg-slate-3 cursor-pointer flex max-h-[32px] min-h-[32px] border border-slate-5 rounded-[10px] bg-slate-1 transition-bg relative" ,
470- on_click = TypesenseSearchState .open_modal ,
449+ class_name = "w-full hover:bg-slate-3 cursor-pointer flex max-h-[32px] min-h-[32px] border border-slate-5 rounded-[3px] !rounded-[10px] bg-slate-1 transition-bg relative" ,
471450 )
472451
473452
@@ -493,10 +472,8 @@ def typesense_search() -> rx.Component:
493472 rx .dialog .content (
494473 search_modal (),
495474 class_name = "w-full max-w-[640px] mx-auto bg-slate-1 border-none outline-none p-0 lg:!fixed lg:!top-24 lg:!left-1/2 lg:!transform lg:!-translate-x-1/2 lg:!translate-y-0 lg:!m-0" ,
496- on_interact_outside = TypesenseSearchState .close_modal ,
497- on_escape_key_down = TypesenseSearchState .close_modal ,
498475 ),
476+ on_open_change = TypesenseSearchState .close_modal ,
499477 ),
500478 keyboard_shortcut_script (),
501- class_name = "w-full" ,
502479 )
0 commit comments