| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  | import ReactMarkdown from 'react-markdown' | 
					
						
							|  |  |  | import 'katex/dist/katex.min.css' | 
					
						
							|  |  |  | import RemarkMath from 'remark-math' | 
					
						
							|  |  |  | import RemarkBreaks from 'remark-breaks' | 
					
						
							|  |  |  | import RehypeKatex from 'rehype-katex' | 
					
						
							|  |  |  | import RemarkGfm from 'remark-gfm' | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | import SyntaxHighlighter from 'react-syntax-highlighter' | 
					
						
							|  |  |  | import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs' | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  | import type { RefObject } from 'react' | 
					
						
							|  |  |  | import { useEffect, useRef, useState } from 'react' | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  | import cn from 'classnames' | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  | import CopyBtn from '@/app/components/app/chat/copy-btn' | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  | import SVGBtn from '@/app/components/app/chat/svg' | 
					
						
							|  |  |  | import Flowchart from '@/app/components/app/chat/mermaid' | 
					
						
							|  |  |  | import s from '@/app/components/app/chat/style.module.css' | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-04 11:31:25 +08:00
										 |  |  | // Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
 | 
					
						
							|  |  |  | const capitalizationLanguageNameMap: Record<string, string> = { | 
					
						
							|  |  |  |   sql: 'SQL', | 
					
						
							|  |  |  |   javascript: 'JavaScript', | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  |   java: 'Java', | 
					
						
							| 
									
										
										
										
											2023-09-04 11:31:25 +08:00
										 |  |  |   typescript: 'TypeScript', | 
					
						
							|  |  |  |   vbscript: 'VBScript', | 
					
						
							|  |  |  |   css: 'CSS', | 
					
						
							|  |  |  |   html: 'HTML', | 
					
						
							|  |  |  |   xml: 'XML', | 
					
						
							|  |  |  |   php: 'PHP', | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  |   python: 'Python', | 
					
						
							|  |  |  |   yaml: 'Yaml', | 
					
						
							|  |  |  |   mermaid: 'Mermaid', | 
					
						
							|  |  |  |   markdown: 'MarkDown', | 
					
						
							|  |  |  |   makefile: 'MakeFile', | 
					
						
							| 
									
										
										
										
											2023-09-04 11:31:25 +08:00
										 |  |  | } | 
					
						
							|  |  |  | const getCorrectCapitalizationLanguageName = (language: string) => { | 
					
						
							|  |  |  |   if (!language) | 
					
						
							|  |  |  |     return 'Plain' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (language in capitalizationLanguageNameMap) | 
					
						
							|  |  |  |     return capitalizationLanguageNameMap[language] | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-04 11:31:25 +08:00
										 |  |  |   return language.charAt(0).toUpperCase() + language.substring(1) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | export function PreCode(props: { children: any }) { | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |   const ref = useRef<HTMLPreElement>(null) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <pre ref={ref}> | 
					
						
							|  |  |  |       <span | 
					
						
							|  |  |  |         className="copy-code-button" | 
					
						
							|  |  |  |         onClick={() => { | 
					
						
							|  |  |  |           if (ref.current) { | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |             const code = ref.current.innerText | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |             // copyToClipboard(code);
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }} | 
					
						
							|  |  |  |       ></span> | 
					
						
							|  |  |  |       {props.children} | 
					
						
							|  |  |  |     </pre> | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |   ) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const useLazyLoad = (ref: RefObject<Element>): boolean => { | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |   const [isIntersecting, setIntersecting] = useState<boolean>(false) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     const observer = new IntersectionObserver(([entry]) => { | 
					
						
							|  |  |  |       if (entry.isIntersecting) { | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |         setIntersecting(true) | 
					
						
							|  |  |  |         observer.disconnect() | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |     }) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |     if (ref.current) | 
					
						
							|  |  |  |       observer.observe(ref.current) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return () => { | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |       observer.disconnect() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, [ref]) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |   return isIntersecting | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 15:41:24 +08:00
										 |  |  | export function Markdown(props: { content: string; className?: string }) { | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  |   const [isCopied, setIsCopied] = useState(false) | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  |   const [isSVG, setIsSVG] = useState(false) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2023-12-18 15:41:24 +08:00
										 |  |  |     <div className={cn(props.className, 'markdown-body')}> | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |       <ReactMarkdown | 
					
						
							| 
									
										
										
										
											2024-01-10 19:55:50 +08:00
										 |  |  |         remarkPlugins={[[RemarkMath, { singleDollarTextMath: false }], RemarkGfm, RemarkBreaks]} | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |         rehypePlugins={[ | 
					
						
							|  |  |  |           RehypeKatex, | 
					
						
							|  |  |  |         ]} | 
					
						
							|  |  |  |         components={{ | 
					
						
							|  |  |  |           code({ node, inline, className, children, ...props }) { | 
					
						
							|  |  |  |             const match = /language-(\w+)/.exec(className || '') | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  |             const language = match?.[1] | 
					
						
							| 
									
										
										
										
											2023-09-04 11:31:25 +08:00
										 |  |  |             const languageShowName = getCorrectCapitalizationLanguageName(language || '') | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |             return (!inline && match) | 
					
						
							|  |  |  |               ? ( | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  |                 <div> | 
					
						
							|  |  |  |                   <div | 
					
						
							|  |  |  |                     className='flex justify-between h-8 items-center p-1 pl-3 border-b' | 
					
						
							|  |  |  |                     style={{ | 
					
						
							|  |  |  |                       borderColor: 'rgba(0, 0, 0, 0.05)', | 
					
						
							|  |  |  |                     }} | 
					
						
							|  |  |  |                   > | 
					
						
							|  |  |  |                     <div className='text-[13px] text-gray-500 font-normal'>{languageShowName}</div> | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  |                     <div style={{ display: 'flex' }}> | 
					
						
							|  |  |  |                       {language === 'mermaid' | 
					
						
							|  |  |  |                         && <SVGBtn | 
					
						
							|  |  |  |                           isSVG={isSVG} | 
					
						
							|  |  |  |                           setIsSVG={setIsSVG} | 
					
						
							|  |  |  |                         /> | 
					
						
							|  |  |  |                       } | 
					
						
							|  |  |  |                       <CopyBtn | 
					
						
							|  |  |  |                         className={cn(s.copyBtn, 'mr-1')} | 
					
						
							|  |  |  |                         value={String(children).replace(/\n$/, '')} | 
					
						
							|  |  |  |                         isPlain | 
					
						
							|  |  |  |                       /> | 
					
						
							|  |  |  |                     </div> | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  |                   </div> | 
					
						
							| 
									
										
										
										
											2023-12-18 15:41:24 +08:00
										 |  |  |                   {(language === 'mermaid' && isSVG) | 
					
						
							| 
									
										
										
										
											2023-09-14 14:09:23 +08:00
										 |  |  |                     ? (<Flowchart PrimitiveCode={String(children).replace(/\n$/, '')} />) | 
					
						
							|  |  |  |                     : (<SyntaxHighlighter | 
					
						
							|  |  |  |                       {...props} | 
					
						
							|  |  |  |                       style={atelierHeathLight} | 
					
						
							|  |  |  |                       customStyle={{ | 
					
						
							|  |  |  |                         paddingLeft: 12, | 
					
						
							|  |  |  |                         backgroundColor: '#fff', | 
					
						
							|  |  |  |                       }} | 
					
						
							|  |  |  |                       language={match[1]} | 
					
						
							|  |  |  |                       showLineNumbers | 
					
						
							|  |  |  |                       PreTag="div" | 
					
						
							|  |  |  |                     > | 
					
						
							|  |  |  |                       {String(children).replace(/\n$/, '')} | 
					
						
							|  |  |  |                     </SyntaxHighlighter>)} | 
					
						
							| 
									
										
										
										
											2023-08-30 18:08:47 +08:00
										 |  |  |                 </div> | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |               ) | 
					
						
							|  |  |  |               : ( | 
					
						
							|  |  |  |                 <code {...props} className={className}> | 
					
						
							|  |  |  |                   {children} | 
					
						
							|  |  |  |                 </code> | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |           }, | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |         }} | 
					
						
							| 
									
										
										
										
											2024-02-02 15:42:42 +08:00
										 |  |  |         linkTarget='_blank' | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |       > | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |         {/* Markdown detect has problem. */} | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |         {props.content} | 
					
						
							|  |  |  |       </ReactMarkdown> | 
					
						
							|  |  |  |     </div> | 
					
						
							| 
									
										
										
										
											2023-07-27 13:27:34 +08:00
										 |  |  |   ) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | } |