feat(代码块): 添加 HTML 代码预览功能
- 在代码块工具栏添加预览按钮 - 支持 HTML/HTM 类型代码的实时预览 - 集成 HtmlPreviewModal 模态框组件
This commit is contained in:
parent
959fedf1d0
commit
1c114a764e
@ -1,9 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState, useMemo } from 'react';
|
||||
import { Copy, Check } from 'lucide-react';
|
||||
import { Copy, Check, Eye } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import Prism from 'prismjs';
|
||||
import { HtmlPreviewModal } from '@/components/ui/HtmlPreviewModal';
|
||||
import 'prismjs/components/prism-javascript';
|
||||
import 'prismjs/components/prism-typescript';
|
||||
import 'prismjs/components/prism-jsx';
|
||||
@ -52,10 +53,15 @@ export function CodeBlock({
|
||||
className,
|
||||
}: CodeBlockProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [previewOpen, setPreviewOpen] = useState(false);
|
||||
|
||||
// 规范化语言名称
|
||||
const normalizedLanguage = languageAliases[language.toLowerCase()] || language.toLowerCase();
|
||||
|
||||
// 判断是否支持 HTML 预览
|
||||
const isHtmlPreviewable = ['html', 'htm', 'markup'].includes(normalizedLanguage) ||
|
||||
['html', 'htm'].includes(language.toLowerCase());
|
||||
|
||||
// 使用 useMemo 缓存高亮后的 HTML,避免频繁重新高亮
|
||||
const highlightedCode = useMemo(() => {
|
||||
const grammar = Prism.languages[normalizedLanguage];
|
||||
@ -78,27 +84,41 @@ export function CodeBlock({
|
||||
const lines = code.split('\n');
|
||||
|
||||
return (
|
||||
<div className={cn('relative group rounded-lg overflow-hidden my-4', className)}>
|
||||
<div className={cn('relative group rounded overflow-hidden my-4', className)}>
|
||||
{/* 顶部工具栏 */}
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-[#2d2d2d] text-gray-400 text-sm">
|
||||
<span className="font-mono">{language || 'code'}</span>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="inline-flex items-center gap-1.5 px-2 py-1 rounded hover:bg-white/10 transition-colors"
|
||||
title="Copy code"
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check size={14} className="text-green-400" />
|
||||
<span className="text-green-400">Copied!</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy size={14} />
|
||||
<span>Copy</span>
|
||||
</>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* HTML 预览按钮 */}
|
||||
{isHtmlPreviewable && (
|
||||
<button
|
||||
onClick={() => setPreviewOpen(true)}
|
||||
className="inline-flex items-center gap-1.5 px-2 py-1 rounded hover:bg-white/10 transition-colors text-blue-400 hover:text-blue-300"
|
||||
title="预览 HTML"
|
||||
>
|
||||
<Eye size={14} />
|
||||
<span>预览</span>
|
||||
</button>
|
||||
)}
|
||||
</button>
|
||||
{/* 复制按钮 */}
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="inline-flex items-center gap-1.5 px-2 py-1 rounded hover:bg-white/10 transition-colors"
|
||||
title="Copy code"
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check size={14} className="text-green-400" />
|
||||
<span className="text-green-400">Copied!</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy size={14} />
|
||||
<span>Copy</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 代码区域 */}
|
||||
@ -130,6 +150,16 @@ export function CodeBlock({
|
||||
/>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* HTML 预览模态框 */}
|
||||
{isHtmlPreviewable && (
|
||||
<HtmlPreviewModal
|
||||
htmlCode={code}
|
||||
isOpen={previewOpen}
|
||||
onClose={() => setPreviewOpen(false)}
|
||||
language={language}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user