4747 "Blogs" : ["Blog" ]
4848}
4949
50- SECTION_DISPLAY_NAMES = {
51- 'getting_started' : 'Getting Started' ,
52- 'library' : 'Components' ,
53- 'api-reference' : 'API Reference' ,
54- 'hosting' : 'Hosting' ,
55- 'events' : 'Events' ,
56- 'styling' : 'Styling' ,
57- 'state' : 'State' ,
58- 'vars' : 'Variables' ,
59- 'database' : 'Database' ,
60- 'authentication' : 'Authentication' ,
61- 'custom-components' : 'Custom Components' ,
62- 'wrapping-react' : 'Wrapping React' ,
63- 'ai_builder' : 'AI Builder' ,
64- 'recipes' : 'Recipes' ,
65- 'advanced_onboarding' : 'Advanced' ,
66- 'enterprise' : 'Enterprise' ,
67- 'utility_methods' : 'Utilities' ,
68- 'client_storage' : 'Client Storage' ,
69- 'components' : 'Components' ,
70- 'pages' : 'Pages' ,
71- 'assets' : 'Assets' ,
72- 'api-routes' : 'API Routes' ,
73- 'ui' : 'UI' ,
74- 'state_structure' : 'State Structure' ,
75- 'Blog' : 'Blog'
76- }
77-
7850DEFAULT_SUGGESTIONS = [
7951 {"title" : "Getting Started with Reflex" , "url" : "/docs/getting-started/introduction" },
8052 {"title" : "Components Overview" , "url" : "/docs/library" },
8456 {"title" : "Deployment Guide" , "url" : "/docs/hosting/deploy-quick-start" },
8557]
8658
59+ # Precompiled regex patterns for component highlighting fixes
60+ PATTERN_PARTIAL_WORD = re .compile (r'<mark>([^<]*?)</mark>([a-zA-Z0-9_]*)' )
61+ PATTERN_COMPONENT_NAME = re .compile (r'((?:rx|reflex)\.)<mark>([^<]*?)</mark>' )
62+ PATTERN_NAMESPACE = re .compile (r'<mark>(rx|reflex)</mark>\.([a-zA-Z0-9_]+)' )
63+ PATTERN_CHAINED = re .compile (r'<mark>((?:rx|reflex)\.[a-zA-Z0-9_]+)</mark>\.([a-zA-Z0-9_]+)' )
64+
65+ # Styling for highlights
66+ HIGHLIGHT_STYLE = '<span style="background-color: var(--violet-3); color: var(--violet-11); padding: 2px 4px; border-radius: 3px;">'
67+
8768
8869class TypesenseSearchState (rx .State ):
8970 """Enhanced state management for the Typesense search component."""
@@ -232,94 +213,57 @@ async def _perform_regular_search(self, query: str) -> dict:
232213 return client .collections ['docs' ].documents .search (search_parameters )
233214
234215 def _format_search_results (self , result : dict ) -> list [dict ]:
235- """Format search results for display with enhanced component info."""
236- formatted_results = []
237-
238- for hit in result ['hits' ]:
239- doc = hit ['document' ]
240-
241- # Extract component information
242- components = doc .get ('components' , [])
243- component_info = None
244- if components :
245- component_info = f"Components: { ', ' .join (components )} "
246-
247- formatted_result = {
248- 'title' : doc ['title' ],
249- 'content' : self ._get_highlighted_content (hit ),
250- 'url' : doc ['url' ],
251- 'path' : doc ['path' ],
252- 'section' : doc .get ('section' , '' ),
253- 'subsection' : doc .get ('subsection' , '' ),
254- 'breadcrumb' : self ._create_breadcrumb (doc ),
255- 'components' : components ,
256- 'component_info' : component_info ,
257- 'score' : hit .get ('text_match' , 0 ) # Include relevance score
258- }
259-
260- formatted_results .append (formatted_result )
261-
262- return formatted_results
216+ """Format search results for display with enhanced component info."""
217+ formatted_results = []
218+
219+ for hit in result ['hits' ]:
220+ doc = hit ['document' ]
221+ components = doc .get ('components' , [])
222+ component_info = None
223+ if components :
224+ component_info = f"Components: { ', ' .join (components )} "
225+ formatted_result = {
226+ 'title' : doc ['title' ],
227+ 'content' : self ._get_highlighted_content (hit ),
228+ 'url' : doc ['url' ],
229+ 'path' : doc ['path' ],
230+ 'section' : doc .get ('section' , '' ),
231+ 'subsection' : doc .get ('subsection' , '' ),
232+ 'breadcrumb' : doc .get ('breadcrumb' , '' ),
233+ 'components' : components ,
234+ 'component_info' : component_info ,
235+ 'score' : hit .get ('text_match' , 0 )
236+ }
237+ formatted_results .append (formatted_result )
238+
239+ return formatted_results
263240
264241 def _get_highlighted_content (self , hit : dict ) -> str :
265242 """Get highlighted content snippet with component-aware highlighting."""
266243 highlights = hit .get ('highlights' , [])
267244
268245 def fix_component_highlighting (text ):
269246 """Fix incomplete word and component highlighting patterns."""
270- import re
271-
272- # Fix 1: Complete partial words (e.g., Form -> Forms)
273- text = re .sub (r'<mark>([^<]*?)</mark>([a-zA-Z0-9_]*)' , r'<mark>\1\2</mark>' , text )
274-
275- # Fix 2: Handle component patterns where only component name is highlighted
276- # rx.<mark>form</mark> -> <mark>rx.form</mark>
277- text = re .sub (r'((?:rx|reflex)\.)<mark>([^<]*?)</mark>' , r'<mark>\1\2</mark>' , text )
278-
279- # Fix 3: Handle reverse case where namespace is highlighted
280- # <mark>rx</mark>.form -> <mark>rx.form</mark>
281- text = re .sub (r'<mark>(rx|reflex)</mark>\.([a-zA-Z0-9_]+)' , r'<mark>\1.\2</mark>' , text )
282-
283- # Fix 4: Handle chained components/methods
284- # <mark>rx.component</mark>.method -> <mark>rx.component.method</mark>
285- text = re .sub (r'<mark>((?:rx|reflex)\.[a-zA-Z0-9_]+)</mark>\.([a-zA-Z0-9_]+)' , r'<mark>\1.\2</mark>' , text )
286-
247+ text = PATTERN_PARTIAL_WORD .sub (r'<mark>\1\2</mark>' , text )
248+ text = PATTERN_COMPONENT_NAME .sub (r'<mark>\1\2</mark>' , text )
249+ text = PATTERN_NAMESPACE .sub (r'<mark>\1.\2</mark>' , text )
250+ text = PATTERN_CHAINED .sub (r'<mark>\1.\2</mark>' , text )
287251 return text
288252
289- if highlights :
290- # Prioritize component field highlights
291- for highlight in highlights :
292- if highlight .get ('field' ) == 'components' :
293- values = highlight .get ('values' , [])
294- if values :
295- # Apply highlighting fix to component values too
296- fixed_values = [fix_component_highlighting (value ) for value in values ]
297- highlighted_components = ', ' .join (fixed_values )
298- return f"<span style='font-weight: 600;'>Components:</span> { highlighted_components } "
299-
300- # Then look for content highlights
301- for highlight in highlights :
302- if highlight .get ('field' ) == 'content' :
303- content = highlight .get ('snippet' ) or highlight .get ('value' )
304- if content and '<mark>' in content :
305- # Apply comprehensive highlighting fix
306- content = fix_component_highlighting (content )
307- content = content .replace (
308- '<mark>' , '<span style="background-color: var(--violet-3); color: var(--violet-11); padding: 2px 4px; border-radius: 3px;">'
309- ).replace ('</mark>' , '</span>' )
310- return content
311-
312- # Finally, title highlights
313- for highlight in highlights :
314- if highlight .get ('field' ) == 'title' :
315- content = highlight .get ('snippet' ) or highlight .get ('value' )
316- if content and '<mark>' in content :
317- # Apply comprehensive highlighting fix
318- content = fix_component_highlighting (content )
319- content = content .replace (
320- '<mark>' , '<span style="background-color: var(--violet-3); color: var(--violet-11); padding: 2px 4px; border-radius: 3px;">'
321- ).replace ('</mark>' , '</span>' )
322- return content
253+ for highlight in highlights :
254+ field = highlight .get ('field' )
255+ if field == 'components' :
256+ values = highlight .get ('values' , [])
257+ if values :
258+ fixed_values = [fix_component_highlighting (value ) for value in values ]
259+ highlighted_components = ', ' .join (fixed_values )
260+ styled = f"<span style='font-weight: 600;'>Components:</span> { highlighted_components } "
261+ return styled .replace ('<mark>' , HIGHLIGHT_STYLE ).replace ('</mark>' , '</span>' )
262+ elif field in ['content' , 'title' ]:
263+ content = highlight .get ('snippet' ) or highlight .get ('value' , '' )
264+ if content and '<mark>' in content :
265+ content = fix_component_highlighting (content )
266+ return content .replace ('<mark>' , HIGHLIGHT_STYLE ).replace ('</mark>' , '</span>' )
323267
324268 # Fallback to truncated plain content
325269 return self ._truncate_content (hit ['document' ]['content' ])
@@ -330,32 +274,6 @@ def _truncate_content(self, content: str, max_length: int = 150) -> str:
330274 return content
331275 return content [:max_length ] + '...'
332276
333- def _create_breadcrumb (self , document : dict ) -> str :
334- """Create a breadcrumb string from document metadata."""
335- parts = []
336-
337- # Add section
338- section = document .get ('section' , '' )
339- if section :
340- section_display = SECTION_DISPLAY_NAMES .get (
341- section ,
342- section .replace ('-' , ' ' ).replace ('_' , ' ' ).title ()
343- )
344- parts .append (section_display )
345-
346- # Add subsection
347- subsection = document .get ('subsection' , '' )
348- if subsection :
349- subsection_display = subsection .replace ('-' , ' ' ).replace ('_' , ' ' ).title ()
350- parts .append (subsection_display )
351-
352- # Add title if different from last part
353- title = document .get ('title' , '' )
354- if title and (not parts or title .lower () != parts [- 1 ].lower ()):
355- parts .append (title )
356-
357- return ' › ' .join (parts )
358-
359277 def hide_results (self ):
360278 """Hide search results."""
361279 self .show_results = False
0 commit comments