44import typesense
55
66from reflex .experimental import ClientStateVar
7- from .web_ai import Message
7+ from .web_ai import Message , ConversationalSearch
8+
9+ web_interface = ClientStateVar .create ("web_interface" , "search" )
10+ is_processing_prompt = ClientStateVar .create ("is_processing_prompt" , False )
811last_copied = ClientStateVar .create ("is_copied" , "" )
912
1013suggestion_items = [
@@ -392,18 +395,15 @@ def search_input():
392395 web_interface .value == "search" ,
393396 rx .box (
394397 filter_component (),
395- # rx.link(
396- ui .button (
397- ui .icon (icon = "SparklesIcon" , class_name = "shrink-0 size-2" ),
398- "Ask AI" ,
399- type = "button" ,
400- variant = "secondary" ,
401- size = "xs" ,
402- class_name = "text-sm flex flex-row gap-x-2 items-center" ,
403- on_click = web_interface .set_value ("ai_chat" ),
404- ),
405- # href="https://reflex.dev/docs/ai-builder/integrations/mcp-overview/"
406- # ),
398+ ui .button (
399+ ui .icon (icon = "SparklesIcon" , class_name = "shrink-0 size-2" ),
400+ "Ask AI" ,
401+ type = "button" ,
402+ variant = "secondary" ,
403+ size = "xs" ,
404+ class_name = "text-sm flex flex-row gap-x-2 items-center" ,
405+ on_click = web_interface .set_value ("ai_chat" ),
406+ ),
407407 ui .button (
408408 "Esc" ,
409409 size = "xs" ,
@@ -423,18 +423,31 @@ def search_input():
423423 rx .cond (
424424 ConversationalSearch .is_loading ,
425425 rx .box (
426- rx .box (
427- class_name = "absolute inline-flex h-full w-full animate-ping rounded-md bg-orange-4 opacity-75"
428- ),
429- rx .box (
430- class_name = "relative inline-flex size-3 rounded-md bg-orange-5"
426+ class_name = "flex size-3 bg-white rounded-[15px]" ,
427+ ),
428+ rx .icon (
429+ tag = "arrow-up" ,
430+ size = 13 ,
431+ class_name = rx .cond (
432+ ConversationalSearch .current_message .length () > 1 ,
433+ "!text-white" ,
434+ "" ,
431435 ),
432- class_name = "relative flex size-3" ,
433436 ),
434- rx .icon (tag = "arrow-up" , size = 13 , class_name = rx .cond (ConversationalSearch .current_message .length () > 1 , "!text-white" , "" )),
435437 ),
436- class_name = "p-2 rounded-md cursor-pointer disabled:cursor-not-allowed overflow-hidden "
437- + rx .cond (ConversationalSearch .current_message .length () > 1 , "bg-violet-9" , "bg-secondary-3" ),
438+ class_name = (
439+ "p-2 rounded-md cursor-pointer disabled:cursor-not-allowed overflow-hidden "
440+ +
441+ rx .cond (
442+ ConversationalSearch .is_loading ,
443+ "bg-violet-9" ,
444+ rx .cond (
445+ ConversationalSearch .current_message .length () > 1 ,
446+ "bg-violet-9" ,
447+ "bg-secondary-3" ,
448+ )
449+ )
450+ ),
438451 on_click = ConversationalSearch .send_message ,
439452 ),
440453 class_name = "hidden md:flex absolute right-2 top-1/2 transform -translate-y-1/2 text-sm flex-row items-center gap-x-2" ,
@@ -456,7 +469,7 @@ def search_input():
456469 on_change = lambda value : ConversationalSearch .set_current_message (value ),
457470 auto_focus = True ,
458471 placeholder = "Ask the AI about Reflex ..." ,
459- class_name = "py-2 pl-7 md:pr-[310px ] w-full placeholder:text-sm text-sm rounded-lg outline-none focus:outline-none border border-secondary-a4 bg-secondary-1 text-secondary-12 resize-none"
472+ class_name = "py-2 pl-7 md:pr-[100px ] w-full placeholder:text-sm text-sm rounded-lg outline-none focus:outline-none border border-secondary-a4 bg-secondary-1 text-secondary-12 resize-none"
460473 ),
461474 enter_key_submit = True ,
462475 on_submit = ConversationalSearch .send_message ,
@@ -645,6 +658,33 @@ def search_content():
645658 )
646659
647660
661+ @rx .memo
662+ def markdown_copy_button (
663+ content : str ,
664+ ):
665+ markdown_copy_state = ClientStateVar .create (
666+ "content_id" , default = False , global_ref = False
667+ )
668+
669+ return rx .el .button (
670+ rx .cond (
671+ markdown_copy_state .value ,
672+ rx .icon (tag = "check" , size = 13 , class_name = "!text-slate-9" ),
673+ rx .icon (tag = "copy" , size = 13 , class_name = "!text-slate-9" ),
674+ ),
675+ cursor = "pointer" ,
676+ position = "absolute" ,
677+ right = "15px" ,
678+ top = "35px" ,
679+ on_click = [
680+ rx .call_function (markdown_copy_state .set_value (True )),
681+ rx .set_clipboard (content ),
682+ ],
683+ on_mouse_down = rx .call_function (markdown_copy_state .set_value (False )).debounce (
684+ 1500
685+ ),
686+ )
687+
648688def chat_message (message : Message ):
649689 return rx .cond (
650690 message .role == "user" ,
@@ -657,7 +697,45 @@ def chat_message(message: Message):
657697 ),
658698 rx .el .div (
659699 rx .el .div (
660- rx .markdown (message .content ),
700+ rx .markdown (
701+ message .content ,
702+ component_map = {
703+ "h1" : lambda value : rx .el .h1 (value ),
704+ "h2" : lambda value : rx .el .h2 (value ),
705+ "h3" : lambda value : rx .el .h3 (value ),
706+ "h4" : lambda value : rx .el .h4 (value ),
707+ "h5" : lambda value : rx .el .h5 (value ),
708+ "h6" : lambda value : rx .el .h6 (value ),
709+ "p" : lambda value : rx .el .p (value , class_name = "leading-7" ),
710+ "strong" : lambda value : rx .el .strong (
711+ value , class_name = "text-secondary-12"
712+ ),
713+ "ul" : lambda value : rx .el .ul (value ),
714+ "ol" : lambda value : rx .el .ol (value ),
715+ "li" : lambda value : rx .el .li (value ),
716+ "a" : lambda value : rx .el .a (
717+ value ,
718+ class_name = "underline" ,
719+ target = "_blank" ,
720+ ),
721+ "pre" : lambda value : rx .el .pre (value , class_name = "not-prose" ),
722+ "codeblock" : lambda value , ** props : rx .el .div (
723+ rx .code_block (
724+ value ,
725+ ** props ,
726+ wrap_long_lines = False ,
727+ class_name = "code-block !text-xs max-h-[300px] overflow-auto" ,
728+ ),
729+ markdown_copy_button (content = value ),
730+ class_name = "flex flex-row relative py-2" ,
731+ ),
732+ "code" : lambda value , ** props : rx .el .code (
733+ value ,
734+ ** props ,
735+ class_name = "font-mono border border-slate-4 bg-slate-3 px-1 rounded-[0.35rem] font-normal not-prose text-sm text-[12.5px] text-slate-12" ,
736+ ),
737+ },
738+ ),
661739 class_name = "p-2 text-sm"
662740 ),
663741 class_name = "flex justify-start"
@@ -678,11 +756,6 @@ def chat_content():
678756 ),
679757
680758
681- from .web_ai import ConversationalSearch
682-
683- web_interface = ClientStateVar .create ("web_interface" , "search" )
684- is_processing_prompt = ClientStateVar .create ("is_processing_prompt" , False )
685-
686759def typesense_search () -> rx .Component :
687760 """Create the main search component for Reflex Web"""
688761 return rx .fragment (
0 commit comments