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