@@ -21,7 +21,9 @@ import { math } from "@streamdown/math";
2121import { mermaid } from "@streamdown/mermaid" ;
2222import { ChevronLeftIcon , ChevronRightIcon } from "lucide-react" ;
2323import {
24+ type ReactNode ,
2425 createContext ,
26+ isValidElement ,
2527 memo ,
2628 useCallback ,
2729 useContext ,
@@ -326,13 +328,49 @@ const streamdownPlugins = { cjk, code, math, mermaid };
326328
327329const linkSafetyOff = { enabled : false } as const ;
328330
331+ function hasImageChild ( children : ReactNode ) : boolean {
332+ const arr : ReactNode [ ] = Array . isArray ( children ) ? children : [ children ] ;
333+ return arr . some (
334+ ( c ) =>
335+ isValidElement ( c ) &&
336+ ( c . props as Record < string , unknown > ) ?. node &&
337+ ( ( c . props as Record < string , unknown > ) . node as { tagName ?: string } )
338+ ?. tagName === "img"
339+ ) ;
340+ }
341+
342+ const SafeParagraph = memo (
343+ ( { children, node, ...rest } : Record < string , unknown > & { children ?: ReactNode ; node ?: unknown } ) => {
344+ const kids = ( Array . isArray ( children ) ? children : [ children ] ) . filter (
345+ ( c ) => c != null && c !== ""
346+ ) as ReactNode [ ] ;
347+
348+ if ( kids . length === 1 && isValidElement ( kids [ 0 ] ) ) {
349+ const tag = ( kids [ 0 ] . props as Record < string , unknown > ) ?. node as { tagName ?: string } | undefined ;
350+ if ( tag ?. tagName === "img" ) return < > { children } </ > ;
351+ if ( tag ?. tagName === "code" && "data-block" in ( kids [ 0 ] . props as Record < string , unknown > ) )
352+ return < > { children } </ > ;
353+ }
354+
355+ if ( hasImageChild ( children ) ) {
356+ return < div { ...( rest as React . HTMLAttributes < HTMLDivElement > ) } > { children } </ div > ;
357+ }
358+
359+ return < p { ...( rest as React . HTMLAttributes < HTMLParagraphElement > ) } > { children } </ p > ;
360+ }
361+ ) ;
362+ SafeParagraph . displayName = "SafeParagraph" ;
363+
364+ const streamdownComponents = { p : SafeParagraph } as ComponentProps < typeof Streamdown > [ "components" ] ;
365+
329366export const MessageResponse = memo (
330367 ( { className, ...props } : MessageResponseProps ) => (
331368 < Streamdown
332369 className = { cn (
333370 "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0" ,
334371 className
335372 ) }
373+ components = { streamdownComponents }
336374 linkSafety = { linkSafetyOff }
337375 plugins = { streamdownPlugins }
338376 { ...props }
0 commit comments