5b7b0ff1e4
- 添加 CSS 变量定义浅色和深色主题色板 - 扩展 Tailwind 配置支持语义化颜色 (surface-*, fg-*, line-*, code) - 创建 useTheme hook 管理主题状态和持久化 - 创建 ThemeToggle 组件支持三种模式 (light/dark/system) - 迁移所有组件从硬编码 gray-* 到语义化颜色 - 支持系统主题偏好检测 (prefers-color-scheme) - 添加主题初始化脚本防止闪烁 (FOUC)
171 lines
5.1 KiB
TypeScript
171 lines
5.1 KiB
TypeScript
/**
|
|
* Markdown Component
|
|
*
|
|
* Markdown 渲染组件,支持 GFM 语法和代码高亮
|
|
*/
|
|
|
|
import ReactMarkdown from 'react-markdown';
|
|
import remarkGfm from 'remark-gfm';
|
|
import { CodeBlock, InlineCode } from './CodeBlock';
|
|
import { cn } from '../utils/cn';
|
|
|
|
interface MarkdownProps {
|
|
content: string;
|
|
className?: string;
|
|
}
|
|
|
|
export function Markdown({ content, className }: MarkdownProps) {
|
|
return (
|
|
<div className={cn('markdown-content', className)}>
|
|
<ReactMarkdown
|
|
remarkPlugins={[remarkGfm]}
|
|
components={{
|
|
// 代码块和内联代码
|
|
code({ className, children, node, ...props }) {
|
|
const match = /language-(\w+)/.exec(className || '');
|
|
const language = match ? match[1] : '';
|
|
const codeContent = String(children).replace(/\n$/, '');
|
|
|
|
// 检查是否为内联代码:如果父节点不是 pre,则为内联代码
|
|
const isInline = node?.position?.start.line === node?.position?.end.line &&
|
|
!codeContent.includes('\n');
|
|
|
|
if (isInline && !language) {
|
|
return <InlineCode {...props}>{children}</InlineCode>;
|
|
}
|
|
|
|
return (
|
|
<CodeBlock
|
|
code={codeContent}
|
|
language={language || 'text'}
|
|
/>
|
|
);
|
|
},
|
|
|
|
// 段落
|
|
p({ children }) {
|
|
return <p className="mb-4 last:mb-0">{children}</p>;
|
|
},
|
|
|
|
// 标题
|
|
h1({ children }) {
|
|
return <h1 className="text-2xl font-bold mb-4 mt-6 first:mt-0">{children}</h1>;
|
|
},
|
|
h2({ children }) {
|
|
return <h2 className="text-xl font-bold mb-3 mt-5 first:mt-0">{children}</h2>;
|
|
},
|
|
h3({ children }) {
|
|
return <h3 className="text-lg font-bold mb-2 mt-4 first:mt-0">{children}</h3>;
|
|
},
|
|
h4({ children }) {
|
|
return <h4 className="text-base font-bold mb-2 mt-3 first:mt-0">{children}</h4>;
|
|
},
|
|
|
|
// 列表
|
|
ul({ children }) {
|
|
return <ul className="list-disc list-inside mb-4 space-y-1">{children}</ul>;
|
|
},
|
|
ol({ children }) {
|
|
return <ol className="list-decimal list-inside mb-4 space-y-1">{children}</ol>;
|
|
},
|
|
li({ children }) {
|
|
return <li className="text-fg-secondary">{children}</li>;
|
|
},
|
|
|
|
// 引用
|
|
blockquote({ children }) {
|
|
return (
|
|
<blockquote className="border-l-4 border-line-muted pl-4 my-4 text-fg-muted italic">
|
|
{children}
|
|
</blockquote>
|
|
);
|
|
},
|
|
|
|
// 链接
|
|
a({ href, children }) {
|
|
return (
|
|
<a
|
|
href={href}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-primary-400 hover:text-primary-300 underline underline-offset-2"
|
|
>
|
|
{children}
|
|
</a>
|
|
);
|
|
},
|
|
|
|
// 强调
|
|
strong({ children }) {
|
|
return <strong className="font-bold text-fg">{children}</strong>;
|
|
},
|
|
em({ children }) {
|
|
return <em className="italic">{children}</em>;
|
|
},
|
|
|
|
// 分割线
|
|
hr() {
|
|
return <hr className="my-6 border-line" />;
|
|
},
|
|
|
|
// 表格
|
|
table({ children }) {
|
|
return (
|
|
<div className="overflow-x-auto my-4">
|
|
<table className="min-w-full border-collapse border border-line">
|
|
{children}
|
|
</table>
|
|
</div>
|
|
);
|
|
},
|
|
thead({ children }) {
|
|
return <thead className="bg-surface-subtle">{children}</thead>;
|
|
},
|
|
tbody({ children }) {
|
|
return <tbody>{children}</tbody>;
|
|
},
|
|
tr({ children }) {
|
|
return <tr className="border-b border-line">{children}</tr>;
|
|
},
|
|
th({ children }) {
|
|
return (
|
|
<th className="px-4 py-2 text-left text-sm font-semibold text-fg-secondary border-r border-line last:border-r-0">
|
|
{children}
|
|
</th>
|
|
);
|
|
},
|
|
td({ children }) {
|
|
return (
|
|
<td className="px-4 py-2 text-sm text-fg-secondary border-r border-line last:border-r-0">
|
|
{children}
|
|
</td>
|
|
);
|
|
},
|
|
|
|
// 图片
|
|
img({ src, alt }) {
|
|
return (
|
|
<img
|
|
src={src}
|
|
alt={alt || ''}
|
|
className="max-w-full h-auto rounded-lg my-4"
|
|
/>
|
|
);
|
|
},
|
|
|
|
// 预格式化文本(非代码块的 pre)
|
|
pre({ children }) {
|
|
return (
|
|
<pre className="bg-surface-base p-4 rounded-lg overflow-x-auto my-4 text-sm">
|
|
{children}
|
|
</pre>
|
|
);
|
|
},
|
|
}}
|
|
>
|
|
{content}
|
|
</ReactMarkdown>
|
|
</div>
|
|
);
|
|
}
|