From 3453cc9b1eec556bd914cb5be3db9830d852e454 Mon Sep 17 00:00:00 2001 From: aliraza556 Date: Mon, 16 Dec 2024 02:40:55 +0500 Subject: [PATCH] feat(ai-chat): add markdown rendering to chat messages --- .../App/SideBar/AiSummary/AiAnswer/index.tsx | 12 +- .../App/SideBar/AiSummary/index.tsx | 18 ++- .../AiSummaryHighlight/markdown/index.tsx | 135 ++++++++++++++++++ 3 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 src/components/App/SideBar/AiSummary/utils/AiSummaryHighlight/markdown/index.tsx diff --git a/src/components/App/SideBar/AiSummary/AiAnswer/index.tsx b/src/components/App/SideBar/AiSummary/AiAnswer/index.tsx index 14c80410e..f5ad05253 100644 --- a/src/components/App/SideBar/AiSummary/AiAnswer/index.tsx +++ b/src/components/App/SideBar/AiSummary/AiAnswer/index.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import styled from 'styled-components' import { highlightAiSummary } from '~/components/App/SideBar/AiSummary/utils/AiSummaryHighlight' +import { StyledMarkdown } from '~/components/App/SideBar/AiSummary/utils/AiSummaryHighlight/markdown' import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' import { useDataStore } from '~/stores/useDataStore' @@ -24,10 +25,15 @@ const Wrapper = styled(Flex).attrs({ word-break: break-word; ` -const SummaryText = styled(Text)` +const StyledAnswerMarkdown = styled(Text)` font-size: 14px; font-weight: 400; line-height: 19.6px; + + ${StyledMarkdown} { + margin: 0; + padding: 0; + } ` export const AiAnswer = ({ answer, entities, handleLoaded, hasBeenRendered }: Props) => { @@ -90,7 +96,9 @@ export const AiAnswer = ({ answer, entities, handleLoaded, hasBeenRendered }: Pr return ( - {responseTextDisplay} + + {entities?.length ? responseTextDisplay : {displayedText}} + ) } diff --git a/src/components/App/SideBar/AiSummary/index.tsx b/src/components/App/SideBar/AiSummary/index.tsx index 7fcf665ae..edc3077ef 100644 --- a/src/components/App/SideBar/AiSummary/index.tsx +++ b/src/components/App/SideBar/AiSummary/index.tsx @@ -6,7 +6,6 @@ import AiPlayIcon from '~/components/Icons/AiPlayIcon' import ChevronDownIcon from '~/components/Icons/ChevronDownIcon' import ChevronUpIcon from '~/components/Icons/ChevronUpIcon' import { Flex } from '~/components/common/Flex' -import { Text } from '~/components/common/Text' import { useAiSummaryStore } from '~/stores/useAiSummaryStore' import { useAppStore } from '~/stores/useAppStore' import { AIEntity } from '~/types' @@ -16,6 +15,7 @@ import { AiAnswer } from './AiAnswer' import { AiQuestions } from './AiQuestions' import { AiSources } from './AiSources' import { AiSummarySkeleton } from './AiSummarySkeleton' +import { StyledMarkdown } from './utils/AiSummaryHighlight/markdown' type Props = { question: string @@ -23,14 +23,18 @@ type Props = { refId: string } -const Title = styled(Text)` - font-size: 20px; - font-weight: 600; +const TitleContainer = styled.div` flex-grow: 1; overflow-wrap: break-word; white-space: normal; word-break: break-word; - margin-right: 10px; + margin-top: calc(100px - 104px); +` + +const TitleMarkdown = styled(StyledMarkdown)` + font-size: 16px; + font-weight: normal; + line-height: 1.5; ` const TitleWrapper = styled(Flex).attrs({ @@ -111,7 +115,9 @@ export const AiSummary = ({ question, response, refId }: Props) => { return ( - {question} + + {question} + {response.audio_en && ( {currentPlayingAudio?.current === audioRef.current && !audioRef.current?.paused ? ( diff --git a/src/components/App/SideBar/AiSummary/utils/AiSummaryHighlight/markdown/index.tsx b/src/components/App/SideBar/AiSummary/utils/AiSummaryHighlight/markdown/index.tsx new file mode 100644 index 000000000..9d08e3ca5 --- /dev/null +++ b/src/components/App/SideBar/AiSummary/utils/AiSummaryHighlight/markdown/index.tsx @@ -0,0 +1,135 @@ +import ReactMarkdown from 'react-markdown' +import styled from 'styled-components' +import { colors } from '~/utils/colors' + +export const StyledMarkdown = styled(ReactMarkdown)` + color: ${colors.white}; + font-family: 'Barlow', sans-serif; + line-height: 1.5; + + h1, + h2, + h3, + h4, + h5, + h6 { + margin: 1em 0 0.5em; + line-height: 1.3; + font-weight: 600; + margin-top: 1px; + } + + h1 { + font-size: 24px; + font-weight: 600; + margin-bottom: 16px; + } + + h2 { + font-size: 20px; + font-weight: 600; + margin-bottom: 12px; + } + + h3 { + font-size: 1.3em; + } + h4 { + font-size: 1.2em; + } + h5 { + font-size: 1.1em; + } + h6 { + font-size: 1em; + } + + p { + font-weight: normal; + margin: 8px 0; + } + + code { + font-family: 'Courier New', monospace; + background: ${colors.BG1}; + padding: 2px 4px; + border-radius: 4px; + font-weight: normal; + } + + pre { + background: ${colors.BG1}; + padding: 16px; + border-radius: 8px; + overflow-x: auto; + margin: 1em 0; + + code { + background: none; + padding: 0; + border-radius: 0; + } + } + + ul, + ol { + font-weight: normal; + margin: 8px 0; + padding-left: 20px; + } + + li { + font-weight: normal; + margin: 4px 0; + } + + a { + color: #0bf; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + blockquote { + border-left: 4px solid ${colors.modalShield}; + padding-left: 16px; + margin: 16px 0; + font-weight: normal; + } + + table { + border-collapse: collapse; + width: auto; + margin: 1em 0; + background: ${colors.BG1}; + border-radius: 4px; + border: 1px solid ${colors.modalShield}; + } + + th, + td { + border: 1px solid ${colors.modalShield}; + padding: 8px 12px; + text-align: left; + font-weight: normal; + min-width: 100px; + } + + th { + font-weight: 600; + background: ${colors.BG2}; + } + + img { + max-width: 100%; + height: auto; + } + + hr { + border: none; + border-top: 1px solid ${colors.modalShield}; + margin: 1em 0; + } +`