// Renders an assistant message: markdown for prose, plus pills for any // command-bus action blocks or native tool calls attached to the message. // Tool-role messages (results from a function call) render as a JSON card. import { useMemo } from "react" import ReactMarkdown from "react-markdown" import { Sparkles, Wrench } from "lucide-react" import { extractActionBlocks } from "@crema/action-bus" import { stripToolCallTags, type ToolCall } from "@crema/llm-ui" const ACTION_BLOCK_RE = /```action\s*\n[\s\S]*?```/g export type MessageBodyProps = { content: string /** When set, render as a tool-result card. */ isToolResult?: boolean /** Native tool calls attached to this assistant message, if any. */ toolCalls?: ToolCall[] } export function MessageBody({ content, isToolResult, toolCalls }: MessageBodyProps) { const { prose, actionCount } = useMemo(() => { const blocks = extractActionBlocks(content) const cleaned = stripToolCallTags(content).replace(ACTION_BLOCK_RE, "").trim() return { prose: cleaned, actionCount: blocks.length, } }, [content]) if (isToolResult) { return (
Tool result
          {content}
        
) } return (
{prose && (

{children}

, code: ({ children, className }) => { const isBlock = className?.startsWith("language-") if (isBlock) { return (
                    {children}
                  
) } return ( {children} ) }, ul: ({ children }) => , ol: ({ children }) =>
    {children}
, li: ({ children }) =>
  • {children}
  • , a: ({ children, href }) => ( {children} ), }} > {prose}
    )} {actionCount > 0 && ( Ran {actionCount} action{actionCount > 1 ? "s" : ""} )} {toolCalls && toolCalls.length > 0 && ( Called {toolCalls.map((c) => c.name).join(", ")} )}
    ) }