|
4 | 4 | import typesense |
5 | 5 |
|
6 | 6 | from reflex.experimental import ClientStateVar |
| 7 | +from .web_ai import Message |
7 | 8 | last_copied = ClientStateVar.create("is_copied", "") |
8 | 9 |
|
9 | 10 | suggestion_items = [ |
@@ -387,39 +388,80 @@ def search_input(): |
387 | 388 | size=14, |
388 | 389 | class_name="absolute left-2 top-1/2 transform -translate-y-1/2 !text-gray-500/40", |
389 | 390 | ), |
390 | | - rx.box( |
391 | | - filter_component(), |
392 | | - rx.link( |
| 391 | + rx.cond( |
| 392 | + web_interface.value == "search", |
| 393 | + rx.box( |
| 394 | + 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 | + # ), |
393 | 407 | ui.button( |
394 | | - ui.icon(icon="SparklesIcon", class_name="shrink-0 size-2"), |
395 | | - "Ask AI", |
396 | | - type="button", |
397 | | - variant="secondary", |
| 408 | + "Esc", |
398 | 409 | size="xs", |
399 | | - class_name="text-sm flex flex-row gap-x-2 items-center", |
400 | | - |
| 410 | + type="button", |
| 411 | + variant="outline", |
| 412 | + on_click=rx.run_script( |
| 413 | + "document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))" |
| 414 | + ), |
401 | 415 | ), |
402 | | - href="https://reflex.dev/docs/ai-builder/integrations/mcp-overview/" |
| 416 | + 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", |
403 | 417 | ), |
404 | | - ui.button( |
405 | | - "Esc", |
406 | | - size="xs", |
407 | | - type="button", |
408 | | - variant="outline", |
409 | | - on_click=rx.run_script( |
410 | | - "document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))" |
| 418 | + rx.box( |
| 419 | + rx.el.div( |
| 420 | + rx.el.p("← Back to search", class_name="text-xs text-slate-9 cursor-pointer", on_click=web_interface.set_value("search")), |
411 | 421 | ), |
| 422 | + rx.el.button( |
| 423 | + rx.cond( |
| 424 | + ConversationalSearch.is_loading, |
| 425 | + 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" |
| 431 | + ), |
| 432 | + class_name="relative flex size-3", |
| 433 | + ), |
| 434 | + rx.icon(tag="arrow-up", size=13, class_name=rx.cond(ConversationalSearch.current_message.length() > 1, "!text-white", "")), |
| 435 | + ), |
| 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 | + on_click=ConversationalSearch.send_message, |
| 439 | + ), |
| 440 | + 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", |
412 | 441 | ), |
413 | | - 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", |
414 | 442 | ), |
415 | | - rx.el.input( |
416 | | - on_change=[ |
417 | | - lambda value: SimpleSearch.user_query(value).debounce(500), |
418 | | - SimpleSearch.perform_search(), |
419 | | - ], |
420 | | - auto_focus=True, |
421 | | - placeholder="Search documentation ...", |
422 | | - 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" |
| 443 | + rx.cond( |
| 444 | + web_interface.value == "search", |
| 445 | + rx.el.input( |
| 446 | + on_change=[ |
| 447 | + lambda value: SimpleSearch.user_query(value).debounce(500), |
| 448 | + SimpleSearch.perform_search(), |
| 449 | + ], |
| 450 | + auto_focus=True, |
| 451 | + placeholder="Search documentation ...", |
| 452 | + 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" |
| 453 | + ), |
| 454 | + rx.form( |
| 455 | + rx.el.input( |
| 456 | + on_change=lambda value: ConversationalSearch.set_current_message(value), |
| 457 | + auto_focus=True, |
| 458 | + 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" |
| 460 | + ), |
| 461 | + enter_key_submit=True, |
| 462 | + on_submit=ConversationalSearch.send_message, |
| 463 | + reset_on_submit=True, |
| 464 | + ), |
423 | 465 | ), |
424 | 466 | class_name="w-full relative focus:outline-none", |
425 | 467 | ), |
@@ -545,7 +587,6 @@ def searching_in_progress(): |
545 | 587 | class_name="w-full flex items-center justify-center text-sm py-4", |
546 | 588 | ) |
547 | 589 |
|
548 | | - |
549 | 590 | def search_content(): |
550 | 591 | return rx.scroll_area( |
551 | 592 | rx.cond( |
@@ -604,14 +645,56 @@ def search_content(): |
604 | 645 | ) |
605 | 646 |
|
606 | 647 |
|
| 648 | +def chat_message(message: Message): |
| 649 | + return rx.cond( |
| 650 | + message.role == "user", |
| 651 | + rx.el.div( |
| 652 | + rx.el.div( |
| 653 | + rx.el.p(message.content, class_name="text-sm"), |
| 654 | + class_name="bg-secondary-3 rounded-md p-2 max-w-xs break-words" |
| 655 | + ), |
| 656 | + class_name="flex justify-end" |
| 657 | + ), |
| 658 | + rx.el.div( |
| 659 | + rx.el.div( |
| 660 | + rx.markdown(message.content), |
| 661 | + class_name="p-2 text-sm" |
| 662 | + ), |
| 663 | + class_name="flex justify-start" |
| 664 | + ) |
| 665 | + ) |
| 666 | + |
| 667 | + |
| 668 | + |
| 669 | +def chat_content(): |
| 670 | + return rx.box( |
| 671 | + rx.auto_scroll( |
| 672 | + rx.foreach( |
| 673 | + ConversationalSearch.messages, chat_message, |
| 674 | + ), |
| 675 | + class_name="h-[57vh] px-1 flex flex-col gap-y-2 [&_.rt-ScrollAreaScrollbar]:right-[0.2875rem] [&_.rt-ScrollAreaScrollbar]:mt-[3rem]" |
| 676 | + ), |
| 677 | + class_name="w-full h-full pt-12" |
| 678 | + ), |
| 679 | + |
| 680 | + |
| 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 | + |
607 | 686 | def typesense_search() -> rx.Component: |
608 | 687 | """Create the main search component for Reflex Web""" |
609 | 688 | return rx.fragment( |
610 | 689 | rx.dialog.root( |
611 | 690 | rx.dialog.trigger(search_trigger(), id="search-trigger"), |
612 | 691 | rx.dialog.content( |
613 | 692 | search_input(), |
614 | | - search_content(), |
| 693 | + rx.cond( |
| 694 | + web_interface.value == "search", |
| 695 | + search_content(), |
| 696 | + chat_content(), |
| 697 | + ), |
615 | 698 | on_interact_outside=SimpleSearch.reset_search, |
616 | 699 | on_escape_key_down=SimpleSearch.reset_search, |
617 | 700 | class_name="w-full max-w-[650px] mx-auto bg-secondary-1 border-none outline-none p-3 lg:!fixed lg:!top-24 lg:!left-1/2 lg:!transform lg:!-translate-x-1/2 lg:!translate-y-0 lg:!m-0 " |
|
0 commit comments