代码修改
This commit is contained in:
1 parent
8f5af46ec2
commit
8f75e12871
168 files changed
+76575
No files matched your search
@@ -0,0 +1,127 @@
|
||||
import React, { useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
|
||||
interface MarkdownRendererProps {
|
||||
content: string;
|
||||
className?: string;
|
||||
onCitationClick?: (index: number) => void;
|
||||
}
|
||||
|
||||
const extractTextFromNode = (node: React.ReactNode): string => {
|
||||
if (typeof node === 'string' || typeof node === 'number') return String(node);
|
||||
if (Array.isArray(node)) return node.map(extractTextFromNode).join('');
|
||||
if (React.isValidElement<{ children?: React.ReactNode }>(node)) return extractTextFromNode(node.props.children);
|
||||
return '';
|
||||
};
|
||||
|
||||
const CodeBlock: React.FC<{ children: React.ReactNode; className?: string }> = ({ children, className = '' }) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const codeText = extractTextFromNode(children).replace(/\n$/, '');
|
||||
const language = className?.match(/language-(\w+)/)?.[1];
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(codeText);
|
||||
setCopied(true);
|
||||
window.setTimeout(() => setCopied(false), 1500);
|
||||
} catch (error) {
|
||||
console.error('复制代码失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="group relative my-4 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm dark:border-slate-700 dark:bg-slate-900">
|
||||
<div className="flex items-center justify-between border-b border-gray-200 bg-gray-100 px-3 py-2 text-xs text-gray-500 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400">
|
||||
<span>{language || 'code'}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
className="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-50 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200 dark:hover:bg-slate-600"
|
||||
>
|
||||
{copied ? '已复制' : '复制'}
|
||||
</button>
|
||||
</div>
|
||||
<pre className="m-0 overflow-x-auto p-4 text-sm leading-relaxed text-gray-800 dark:text-slate-100">
|
||||
<code className={className}>{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content, className = '', onCitationClick }) => {
|
||||
const contentWithCitationBadge = content.replace(
|
||||
/\[\[(\d+)\]\]/g,
|
||||
'<button type="button" data-citation-index="$1" class="inline-flex items-center justify-center min-w-[1.5rem] h-6 px-2 mx-0.5 rounded-md border text-xs font-semibold align-middle bg-red-50 text-red-600 border-red-200 dark:bg-slate-700 dark:text-slate-100 dark:border-slate-500 cursor-pointer transition-all hover:opacity-85">[$1]</button>',
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`markdown-content ${className}`}
|
||||
onClick={(event) => {
|
||||
if (!onCitationClick) return;
|
||||
const target = event.target as HTMLElement;
|
||||
const button = target.closest('[data-citation-index]') as HTMLElement | null;
|
||||
if (!button) return;
|
||||
const index = Number(button.dataset.citationIndex || '0');
|
||||
if (Number.isNaN(index) || index <= 0) return;
|
||||
onCitationClick(index);
|
||||
}}
|
||||
>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
h1: ({ node, ...props }) => <h1 className="text-2xl font-bold mb-4" {...props} />,
|
||||
h2: ({ node, ...props }) => <h2 className="text-xl font-bold mb-3" {...props} />,
|
||||
h3: ({ node, ...props }) => <h3 className="text-lg font-bold mb-2" {...props} />,
|
||||
h4: ({ node, ...props }) => <h4 className="text-base font-bold mb-2" {...props} />,
|
||||
h5: ({ node, ...props }) => <h5 className="text-sm font-bold mb-2" {...props} />,
|
||||
h6: ({ node, ...props }) => <h6 className="text-xs font-bold mb-2" {...props} />,
|
||||
p: ({ node, ...props }) => <p className="mb-4 leading-relaxed" {...props} />,
|
||||
ul: ({ node, ...props }) => <ul className="list-disc list-inside mb-4 ml-4" {...props} />,
|
||||
ol: ({ node, ...props }) => <ol className="list-decimal list-inside mb-4 ml-4" {...props} />,
|
||||
li: ({ node, ...props }) => <li className="mb-1" {...props} />,
|
||||
// code: ({ node, inline, className, children, ...props }: any) => {
|
||||
// if (inline) {
|
||||
// return (
|
||||
// <code
|
||||
// className={`rounded bg-gray-100 px-1 py-0.5 text-sm text-gray-800 dark:bg-slate-800 dark:text-slate-100 ${className || ''}`}
|
||||
// {...props}
|
||||
// >
|
||||
// {children}
|
||||
// </code>
|
||||
// );
|
||||
// }
|
||||
// return <CodeBlock className={className}>{children}</CodeBlock>;
|
||||
// },
|
||||
// pre: ({ node, children }) => <>{children}</>,
|
||||
blockquote: ({ node, ...props }) => (
|
||||
<blockquote className="border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic my-4" {...props} />
|
||||
),
|
||||
a: ({ node, ...props }) => (
|
||||
<a className="text-blue-600 dark:text-blue-400 hover:underline" {...props} />
|
||||
),
|
||||
table: ({ node, ...props }) => (
|
||||
<div className="overflow-x-auto mb-4">
|
||||
<table className="min-w-full border-collapse" {...props} />
|
||||
</div>
|
||||
),
|
||||
thead: ({ node, ...props }) => <thead className="bg-gray-100 dark:bg-gray-800" {...props} />,
|
||||
tbody: ({ node, ...props }) => <tbody className="divide-y divide-gray-200 dark:divide-gray-700" {...props} />,
|
||||
tr: ({ node, ...props }) => <tr className="hover:bg-gray-50 dark:hover:bg-gray-700" {...props} />,
|
||||
th: ({ node, ...props }) => <th className="px-4 py-2 text-left font-semibold" {...props} />,
|
||||
td: ({ node, ...props }) => <td className="px-4 py-2" {...props} />,
|
||||
hr: ({ node, ...props }) => <hr className="my-4 border-gray-300 dark:border-gray-600" {...props} />,
|
||||
strong: ({ node, ...props }) => <strong className="font-bold" {...props} />,
|
||||
em: ({ node, ...props }) => <em className="italic" {...props} />,
|
||||
}}
|
||||
>
|
||||
{contentWithCitationBadge}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownRenderer;
|
||||
Reference in new issue
Block a user