MessageCard.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { useCallback } from 'react';
  2. import type { LinkProps } from 'next/link';
  3. import { useTranslation } from 'react-i18next';
  4. import ReactMarkdown from 'react-markdown';
  5. import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
  6. import { useRagSearchModal } from '../../../client/stores/rag-search';
  7. import styles from './MessageCard.module.scss';
  8. const moduleClass = styles['message-card'] ?? '';
  9. const userMessageCardModuleClass = styles['user-message-card'] ?? '';
  10. const UserMessageCard = ({ children }: { children: string }): JSX.Element => (
  11. <div className={`card d-inline-flex align-self-end bg-success-subtle bg-info-subtle ${moduleClass} ${userMessageCardModuleClass}`}>
  12. <div className="card-body">
  13. <ReactMarkdown>{children}</ReactMarkdown>
  14. </div>
  15. </div>
  16. );
  17. const assistantMessageCardModuleClass = styles['assistant-message-card'] ?? '';
  18. const NextLinkWrapper = (props: LinkProps & {children: string, href: string}): JSX.Element => {
  19. const { close: closeRagSearchModal } = useRagSearchModal();
  20. const onClick = useCallback(() => {
  21. closeRagSearchModal();
  22. }, [closeRagSearchModal]);
  23. return (
  24. <NextLink href={props.href} onClickNextLink={onClick} className="link-primary">
  25. {props.children}
  26. </NextLink>
  27. );
  28. };
  29. const AssistantMessageCard = ({ children }: { children: string }): JSX.Element => {
  30. const { t } = useTranslation();
  31. return (
  32. <div className={`card border-0 ${moduleClass} ${assistantMessageCardModuleClass}`}>
  33. <div className="card-body d-flex">
  34. <div className="me-2 me-lg-3">
  35. <span className="growi-custom-icons grw-ai-icon rounded-pill">growi_ai</span>
  36. </div>
  37. { children.length > 0
  38. ? (
  39. <ReactMarkdown
  40. components={{ a: NextLinkWrapper }}
  41. >{children}
  42. </ReactMarkdown>
  43. )
  44. : (
  45. <span className="text-thinking">
  46. {t('modal_aichat.progress_label')} <span className="material-symbols-outlined">more_horiz</span>
  47. </span>
  48. )
  49. }
  50. </div>
  51. </div>
  52. );
  53. };
  54. type Props = {
  55. role: 'user' | 'assistant',
  56. children: string,
  57. }
  58. export const MessageCard = (props: Props): JSX.Element => {
  59. const { role, children } = props;
  60. return role === 'user'
  61. ? <UserMessageCard>{children}</UserMessageCard>
  62. : <AssistantMessageCard>{children}</AssistantMessageCard>;
  63. };