11import React , { useEffect , useMemo , useRef , useState } from 'react'
2- import cn from "classnames" ;
32import ReactMarkdown , { Components } from "react-markdown" ;
43import rehypeRaw from "rehype-raw" ;
54import remarkGfm from "remark-gfm" ;
65import { makeStyles } from "@material-ui/core" ;
76import { colors } from "@postgres.ai/shared/styles/colors" ;
87import { icons } from "@postgres.ai/shared/styles/icons" ;
98import { DebugDialog } from "../../DebugDialog/DebugDialog" ;
10- import { CodeBlock } from "./CodeBlock" ;
11- import { disallowedHtmlTagsForMarkdown , permalinkLinkBuilder } from "../../utils" ;
9+ import { CodeBlock } from "./CodeBlock/CodeBlock " ;
10+ import { disallowedHtmlTagsForMarkdown } from "../../utils" ;
1211import { MessageStatus , StateMessage } from "../../../../types/api/entities/bot" ;
13- import { MermaidDiagram } from "./MermaidDiagram" ;
12+ import { MermaidDiagram } from "./MermaidDiagram/MermaidDiagram " ;
1413import { useAiBot } from "../../hooks" ;
14+ import { ToolCallRenderer } from "./ToolCallRenderer/ToolCallRenderer" ;
15+ import { transformAllCustomTags } from "../utils" ;
16+ import { ThinkBlockRenderer } from './ThinkingCard/ThinkingCard' ;
17+ import { MessageHeader } from "./MessageHeader/MessageHeader" ;
1518
1619
17- type BaseMessageProps = {
20+ export type BaseMessageProps = {
1821 id : string | null ;
1922 created_at ?: string ;
2023 content ?: string ;
@@ -249,7 +252,6 @@ const useStyles = makeStyles(
249252 '50%' : { borderRightColor : 'black' } ,
250253 } ,
251254 } ) ,
252-
253255)
254256
255257export const Message = React . memo ( ( props : MessageProps ) => {
@@ -302,12 +304,16 @@ export const Message = React.memo((props: MessageProps) => {
302304 } ;
303305 } , [ id , updateMessageStatus , isCurrentStreamMessage , isAi , threadId , status ] ) ;
304306
305- const contentToRender : string = content ?. replace ( / \n / g, ' \n' ) || ''
307+ const contentToRender = useMemo ( ( ) => {
308+ if ( ! content ) return '' ;
309+ return transformAllCustomTags ( content ?. replace ( / \n / g, ' \n' ) ) ;
310+ } , [ content ] ) ;
306311
307312 const toggleDebugDialog = ( ) => {
308313 setDebugVisible ( prevState => ! prevState )
309314 }
310315
316+
311317 const renderers = useMemo < Components > ( ( ) => ( {
312318 p : ( { node, ...props } ) => < div { ...props } /> ,
313319 img : ( { node, ...props } ) => < img style = { { maxWidth : '60%' } } { ...props } /> ,
@@ -325,6 +331,8 @@ export const Message = React.memo((props: MessageProps) => {
325331 return < code { ...props } > { children } </ code >
326332 }
327333 } ,
334+ toolcall : ToolCallRenderer ,
335+ thinkblock : ThinkBlockRenderer ,
328336 } ) , [ ] ) ;
329337
330338 return (
@@ -344,51 +352,17 @@ export const Message = React.memo((props: MessageProps) => {
344352 />
345353 : icons . userChatIcon }
346354 </ div >
347- < div className = { classes . messageHeader } >
348- < span className = { classes . messageAuthor } >
349- { isAi ? 'Postgres.AI' : name }
350- </ span >
351- { created_at && formattedTime &&
352- < span
353- className = { cn ( classes . messageInfo ) }
354- title = { created_at }
355- >
356- { formattedTime }
357- </ span > }
358- < div className = { classes . additionalInfo } >
359- { id && isPublic && < >
360- < span className = { classes . messageInfo } > |</ span >
361- < a
362- className = { cn ( classes . messageInfo , classes . messageInfoActive ) }
363- href = { permalinkLinkBuilder ( id ) }
364- target = "_blank"
365- rel = "noreferrer"
366- >
367- permalink
368- </ a >
369- </ > }
370- { ! isLoading && isAi && id && < >
371- < span className = { classes . messageInfo } > |</ span >
372- < button
373- className = { cn ( classes . messageInfo , classes . messageInfoActive ) }
374- onClick = { toggleDebugDialog }
375- >
376- debug info
377- </ button >
378- </ > }
379- {
380- aiModel && isAi && < >
381- < span className = { classes . messageInfo } > |</ span >
382- < span
383- className = { cn ( classes . messageInfo ) }
384- title = { aiModel }
385- >
386- { aiModel }
387- </ span >
388- </ >
389- }
390- </ div >
391- </ div >
355+ < MessageHeader
356+ name = { name }
357+ createdAt = { created_at }
358+ formattedTime = { formattedTime }
359+ id = { id }
360+ isPublic = { isPublic }
361+ isAi = { isAi }
362+ isLoading = { isLoading }
363+ toggleDebugDialog = { toggleDebugDialog }
364+ aiModel = { aiModel }
365+ />
392366 < div >
393367 { isLoading
394368 ?
@@ -397,16 +371,21 @@ export const Message = React.memo((props: MessageProps) => {
397371 { stateMessage && stateMessage . state ? stateMessage . state : 'Thinking' }
398372 </ div >
399373 </ div >
400- : < ReactMarkdown
401- className = { classes . markdown }
402- children = { contentToRender || '' }
403- rehypePlugins = { isAi ? [ rehypeRaw ] : [ ] }
404- remarkPlugins = { [ remarkGfm ] }
405- linkTarget = '_blank'
406- components = { renderers }
407- disallowedElements = { disallowedHtmlTagsForMarkdown }
408- unwrapDisallowed
409- />
374+ : < >
375+ < ReactMarkdown
376+ className = { classes . markdown }
377+ children = { contentToRender || '' }
378+ rehypePlugins = { isAi ? [ rehypeRaw ] : [ ] }
379+ remarkPlugins = { [ remarkGfm ] }
380+ linkTarget = '_blank'
381+ components = { renderers }
382+ disallowedElements = { disallowedHtmlTagsForMarkdown }
383+ unwrapDisallowed
384+ />
385+ { stateMessage && stateMessage . state && < div className = { classes . loading } >
386+ { stateMessage . state }
387+ </ div > }
388+ </ >
410389 }
411390 </ div >
412391 </ div >
0 commit comments