fix: 优化markdown解析器

This commit is contained in:
imeepos
2025-07-22 17:15:35 +08:00
parent eb9ec73889
commit 8f910c033e
3 changed files with 172 additions and 243 deletions

View File

@@ -37,7 +37,6 @@ interface EnhancedMarkdownRendererProps {
export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> = ({
content,
groundingMetadata,
enableReferences = true,
enableMarkdown = true,
className = '',
showStatistics = false,
@@ -47,8 +46,7 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [validation, setValidation] = useState<ValidationResult | null>(null);
const [selectedGrounding, setSelectedGrounding] = useState<any>(null);
const [showGroundingModal, setShowGroundingModal] = useState(false);
const [expandedGrounding, setExpandedGrounding] = useState<any>(null);
const [previewSource, setPreviewSource] = useState<any>(null);
// 解析Markdown内容
@@ -98,17 +96,16 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
}
}, [parseContent, enableRealTimeParsing]);
// 显示grounding详情
const showGroundingDetails = useCallback((groundingAnalysis: any) => {
setSelectedGrounding(groundingAnalysis);
setShowGroundingModal(true);
}, []);
// 关闭grounding详情
const closeGroundingDetails = useCallback(() => {
setShowGroundingModal(false);
setSelectedGrounding(null);
}, []);
// 切换grounding详情展开状态
const toggleGroundingDetails = useCallback((groundingAnalysis: any) => {
if (expandedGrounding && expandedGrounding === groundingAnalysis) {
// 如果当前已展开相同的内容,则收起
setExpandedGrounding(null);
} else {
// 展开新的内容
setExpandedGrounding(groundingAnalysis);
}
}, [expandedGrounding]);
// 查看大图
const handleViewLarge = useCallback((source: any) => {
@@ -216,22 +213,36 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
}, [groundingMetadata, parseResult]);
// 渲染单个Markdown节点
const renderNode = useCallback((node: MarkdownNode, depth: number = 0, index: number = 0): React.ReactNode => {
const renderNode = useCallback((node: MarkdownNode, depth: number = 0, index: number = 0, showGroundingIndicator: boolean = true): React.ReactNode => {
const key = `${node.range?.start?.line || 0}-${node.range?.start?.column || 0}-${node.range?.start?.offset || 0}-${depth}-${index}`;
// 分析当前节点的引用关联
const groundingAnalysis = analyzeNodeGrounding(node);
// 创建引用指示器组件
const GroundingIndicator = groundingAnalysis ? (
const isExpanded = expandedGrounding === groundingAnalysis;
const GroundingIndicator = (groundingAnalysis && showGroundingIndicator) ? (
<span
className="inline-flex items-center ml-1 px-1 py-0.5 text-xs bg-blue-100 text-blue-700 rounded cursor-help"
title={`引用了 ${groundingAnalysis.groundingInfo.sourceCount} 个来源`}
className={`inline-flex items-center ml-1 px-2 py-1 text-xs rounded cursor-pointer transition-all duration-200 ${
isExpanded
? 'bg-blue-500 text-white shadow-md'
: 'bg-blue-100 text-blue-700 hover:bg-blue-200'
}`}
title={`${isExpanded ? '收起' : '查看'} ${groundingAnalysis.groundingInfo.sourceCount} 个相关素材`}
onClick={() => {
console.log('📚 点击查看引用详情:', groundingAnalysis);
// 显示详细的引用信息,包括图片
showGroundingDetails(groundingAnalysis);
// 切换详细的引用信息展示
toggleGroundingDetails(groundingAnalysis);
}}
>
{groundingAnalysis.groundingInfo.sourceCount}
<span className="mr-1">🖼</span>
<span>{groundingAnalysis.groundingInfo.sourceCount}</span>
<svg
className={`w-3 h-3 ml-1 transition-transform duration-200 ${isExpanded ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</span>
) : null;
@@ -332,7 +343,7 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
case MarkdownNodeType.Strong:
return (
<strong key={key}>
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))}
{node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex, false))}
</strong>
);
@@ -424,78 +435,46 @@ export const EnhancedMarkdownRenderer: React.FC<EnhancedMarkdownRendererProps> =
</div>
</div>
)}
{/* 显示引用来源信息(如果有的话) */}
{enableReferences && groundingMetadata?.sources && groundingMetadata.sources.length > 0 && (
<div className="mt-4 pt-3 border-t border-gray-200">
<div className="text-xs text-gray-500 mb-2">
{groundingMetadata.sources.length}
{/* Grounding详情内联展开 */}
{expandedGrounding && (
<div className="mt-6 border-t border-gray-200 pt-4 animate-in slide-in-from-top-2 duration-300">
<div className="flex items-center justify-between mb-4">
<h3 className="text-base font-semibold text-gray-900 flex items-center">
<span className="mr-2">🖼</span>
({expandedGrounding.groundingInfo?.sourceCount || 0})
</h3>
<button
onClick={() => setExpandedGrounding(null)}
className="text-gray-400 hover:text-gray-600 transition-colors p-2 rounded-full hover:bg-gray-100"
title="收起"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
</svg>
</button>
</div>
<div className="flex flex-wrap gap-1">
{groundingMetadata.sources.slice(0, 5).map((source, index) => (
<span
key={index}
className="inline-flex items-center px-2 py-1 text-xs bg-blue-50 text-blue-700 rounded-full cursor-pointer hover:bg-blue-100"
title={source.title || `来源 ${index + 1}`}
onClick={() => showGroundingDetails({
groundingInfo: {
sourceCount: 1,
sources: [source]
}
})}
>
{index + 1}
</span>
))}
{groundingMetadata.sources.length > 5 && (
<span className="text-xs text-gray-400">
+{groundingMetadata.sources.length - 5}
</span>
)}
</div>
</div>
)}
{/* Grounding详情模态框 */}
{showGroundingModal && selectedGrounding && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white rounded-lg shadow-xl max-w-4xl max-h-[90vh] overflow-hidden">
{/* 模态框头部 */}
<div className="flex items-center justify-between p-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900"></h3>
<button
onClick={closeGroundingDetails}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/* 模态框内容 */}
<div className="p-4 overflow-y-auto max-h-[calc(90vh-8rem)]">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{selectedGrounding.groundingInfo?.sources?.map((source: any, index: number) => (
<div key={index} className="relative">
<div className="bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 shadow-inner">
{expandedGrounding.groundingInfo?.sources && expandedGrounding.groundingInfo.sources.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{expandedGrounding.groundingInfo.sources.map((source: any, index: number) => (
<div key={index} className="relative transform hover:scale-105 transition-transform duration-200">
<ImageCard
source={source}
showDetails={true}
onViewLarge={handleViewLarge}
className="shadow-lg border border-gray-200 hover:shadow-xl transition-shadow"
className="shadow-lg border border-gray-200 hover:shadow-xl transition-all duration-300 bg-white"
/>
</div>
))}
</div>
{/* 如果没有图片,显示提示信息 */}
{(!selectedGrounding.groundingInfo?.sources || selectedGrounding.groundingInfo.sources.length === 0) && (
<div className="text-center py-8 text-gray-500">
<div className="text-4xl mb-2">🖼</div>
<p></p>
</div>
)}
</div>
) : (
<div className="text-center py-12 text-gray-500">
<div className="text-6xl mb-4 opacity-50">🖼</div>
<p className="text-base font-medium mb-2"></p>
<p className="text-sm text-gray-400"></p>
</div>
)}
</div>
</div>
)}