import { useMemo, useState } from 'react'; import { message } from 'antd'; import { CheckOutlined, CopyOutlined } from '@ant-design/icons'; import { ClipboardManager } from '@/utils'; import './CodeBlock.css'; interface CodeBlockProps { code?: string; lang?: string; } function escapeHtml(str: string): string { return str.replace(/&/g, '&').replace(//g, '>'); } function highlightJson(str: string): string { const escaped = escapeHtml(str); return escaped.replace( /("(?:[^"\\]|\\.)*")\s*(:)|("(?:[^"\\]|\\.)*")|(-?\d+\.?\d*(?:[eE][+-]?\d+)?)\b|(true|false)|(null)|([{}[\]])/g, (_m, key, colon, string, number, bool, nil) => { if (colon) return `${key}${colon}`; if (string) return `${string}`; if (number) return `${number}`; if (bool) return `${bool}`; if (nil) return `${nil}`; return _m; }, ); } export default function CodeBlock({ code = '', lang = 'json' }: CodeBlockProps) { const [copied, setCopied] = useState(false); const [messageApi, messageContextHolder] = message.useMessage(); const highlighted = useMemo( () => (lang === 'json' ? highlightJson(code) : escapeHtml(code)), [code, lang], ); async function copyCode() { const ok = await ClipboardManager.copyText(code); if (ok) { setCopied(true); messageApi.success('Copied'); window.setTimeout(() => setCopied(false), 2000); } else { messageApi.error('Copy failed'); } } return (
{messageContextHolder}
{lang.toUpperCase()}
        
      
); }