import { OpenAIContentSchema, type OpenAIOutputAudioType, } from "@langfuse/shared"; import { StringOrMarkdownSchema } from "@/src/components/schemas/MarkdownSchema"; import { Button } from "@/src/components/ui/button"; import { PrettyJsonView } from "@/src/components/ui/PrettyJsonView"; import { MarkdownView } from "@/src/components/ui/MarkdownViewer"; import { type MediaReturnType } from "@/src/features/media/validation"; import { Check, Copy } from "lucide-react"; import { useMemo, useState } from "react"; import { type z } from "zod/v4"; import { MARKDOWN_RENDER_CHARACTER_LIMIT } from "@/src/utils/constants"; type MarkdownJsonViewHeaderProps = { title: string | React.ReactNode; titleIcon?: React.ReactNode; handleOnValueChange: () => void; handleOnCopy: (event?: React.MouseEvent) => void; canEnableMarkdown?: boolean; controlButtons?: React.ReactNode; }; export function MarkdownJsonViewHeader({ title, titleIcon, handleOnValueChange: _handleOnValueChange, handleOnCopy, canEnableMarkdown: _canEnableMarkdown = true, controlButtons, }: MarkdownJsonViewHeaderProps) { const [isCopied, setIsCopied] = useState(false); return (
{titleIcon} {title}
{controlButtons}
); } const isSupportedMarkdownFormat = ( content: unknown, contentValidation: z.ZodSafeParseResult>, ): content is z.infer => { if (!contentValidation.success) return false; // Don't render if markdown content is huge const contentSize = JSON.stringify(content || {}).length; if (contentSize > MARKDOWN_RENDER_CHARACTER_LIMIT) { return false; } return true; }; // MarkdownJsonView will render markdown if `isMarkdownEnabled` (global context) is true and the content is valid markdown // otherwise, if content is valid markdown will render JSON with switch to enable markdown globally export function MarkdownJsonView({ content, title, titleIcon, className, customCodeHeaderClassName, audio, media, controlButtons, afterHeader, }: { content?: unknown; title?: string; titleIcon?: React.ReactNode; className?: string; customCodeHeaderClassName?: string; audio?: OpenAIOutputAudioType; media?: MediaReturnType[]; controlButtons?: React.ReactNode; /** Content to render between header and main content (e.g., thinking blocks) */ afterHeader?: React.ReactNode; }) { const stringOrValidatedMarkdown = useMemo( () => StringOrMarkdownSchema.safeParse(content), [content], ); const validatedOpenAIContent = useMemo( () => OpenAIContentSchema.safeParse(content), [content], ); const canEnableMarkdown = isSupportedMarkdownFormat( content, validatedOpenAIContent, ); return ( <> {canEnableMarkdown ? ( ) : ( )} ); }