Spaces:
Sleeping
Sleeping
File size: 4,490 Bytes
471f166 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
/**
* BlockRenderer - Renders structured message blocks
* NO hardcoded parsing - only handles proper JSON blocks from backend
*/
import { useMemo } from 'react'
import { BlockMath, InlineMath } from 'react-katex'
import 'katex/dist/katex.min.css'
/**
* Render a TextBlock - plain text
*/
const TextBlock = ({ content }) => {
if (!content) return null
return <p className="text-block">{content}</p>
}
/**
* Render a MathBlock - KaTeX for math expressions
* Accepts either 'latex' or 'content' prop for compatibility
*/
const MathBlockRenderer = ({ latex, content, display = 'block' }) => {
// Support both 'latex' and 'content' field names
const mathContent = latex || content
if (!mathContent) return null
try {
if (display === 'inline') {
return <InlineMath math={mathContent} />
}
return (
<div className="math-block-container">
<BlockMath math={mathContent} />
</div>
)
} catch (error) {
console.error('KaTeX render error:', error)
return <code className="math-error">{mathContent}</code>
}
}
/**
* Render a ListBlock - ordered or unordered list
*/
const ListBlockRenderer = ({ items, ordered = false }) => {
if (!items || !items.length) return null
const ListTag = ordered ? 'ol' : 'ul'
return (
<ListTag className="list-block">
{items.map((item, idx) => (
<li key={idx}>{item}</li>
))}
</ListTag>
)
}
/**
* Render a CodeBlock - syntax highlighted code
*/
const CodeBlockRenderer = ({ content, language = 'python' }) => {
if (!content) return null
return (
<pre className="code-block">
<code className={`language-${language}`}>{content}</code>
</pre>
)
}
/**
* Render a StepBlock - solution step with nested blocks
*/
const StepBlockRenderer = ({ index, title, blocks }) => {
return (
<div className="step-block">
<div className="step-header">
<span className="step-number">{index}</span>
<span className="step-title">{title}</span>
</div>
<div className="step-content">
{blocks && blocks.map((block, idx) => (
<BlockRenderer key={idx} block={block} />
))}
</div>
</div>
)
}
/**
* Main BlockRenderer - dispatches to appropriate renderer based on block type
*/
const BlockRenderer = ({ block }) => {
if (!block || !block.type) return null
switch (block.type) {
case 'text':
return <TextBlock content={block.content} />
case 'math':
// Support both 'latex' and 'content' fields
return <MathBlockRenderer latex={block.latex} content={block.content} display={block.display} />
case 'list':
return <ListBlockRenderer items={block.items} ordered={block.ordered} />
case 'code':
return <CodeBlockRenderer content={block.content} language={block.language} />
case 'step':
return <StepBlockRenderer index={block.index} title={block.title} blocks={block.blocks} />
default:
console.warn('Unknown block type:', block.type)
return <p className="text-block">{JSON.stringify(block)}</p>
}
}
/**
* MessageBlocks - Renders an array of blocks for a message
* Only parses JSON - no hardcoded fallback parsing
*/
export const MessageBlocks = ({ content }) => {
const blocks = useMemo(() => {
if (!content) return []
try {
// Parse JSON blocks from backend
const parsed = typeof content === 'string' ? JSON.parse(content) : content
if (parsed.blocks && Array.isArray(parsed.blocks)) {
return parsed.blocks
}
// If no blocks array, wrap as single text block
return [{ type: 'text', content: String(content) }]
} catch {
// JSON parse failed - backend didn't return proper format
// Show as plain text (no regex processing)
return [{ type: 'text', content: String(content) }]
}
}, [content])
if (!blocks.length) return null
return (
<div className="message-blocks">
{blocks.map((block, idx) => (
<BlockRenderer key={idx} block={block} />
))}
</div>
)
}
export default BlockRenderer
|