markdown2
All checks were successful
Deploy to Server / deploy (push) Successful in 38s

This commit is contained in:
jeremygan2021
2026-02-24 16:32:52 +08:00
parent 009a2a59f3
commit c07f7028fc
3 changed files with 163 additions and 6 deletions

View File

@@ -0,0 +1,48 @@
import React, { useState } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { CopyOutlined, CheckOutlined } from '@ant-design/icons';
import { Button, Tooltip } from 'antd';
const CodeBlock = ({ language, children, ...props }) => {
const [copied, setCopied] = useState(false);
const handleCopy = () => {
navigator.clipboard.writeText(String(children).replace(/\n$/, ''));
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div style={{ position: 'relative', margin: '1em 0' }}>
<Tooltip title={copied ? "已复制" : "复制代码"}>
<Button
type="text"
icon={copied ? <CheckOutlined style={{ color: '#52c41a' }} /> : <CopyOutlined style={{ color: '#fff' }} />}
size="small"
onClick={handleCopy}
style={{
position: 'absolute',
top: 8,
right: 8,
zIndex: 1,
color: '#fff',
background: 'rgba(255, 255, 255, 0.1)',
border: 'none',
}}
/>
</Tooltip>
<SyntaxHighlighter
style={vscDarkPlus}
language={language}
PreTag="div"
customStyle={{ margin: 0, borderRadius: 8, padding: '1.5em 1em 1em 1em' }}
{...props}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
</div>
);
};
export default CodeBlock;

View File

@@ -14,6 +14,8 @@ import rehypeRaw from 'rehype-raw';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import 'katex/dist/katex.min.css';
import styles from './ForumDetail.module.less';
import CodeBlock from '../components/CodeBlock';
const { Title, Text } = Typography;
const { TextArea } = Input;
@@ -133,14 +135,12 @@ const ForumDetail = () => {
code({node, inline, className, children, ...props}) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match ? (
<SyntaxHighlighter
style={vscDarkPlus}
<CodeBlock
language={match[1]}
PreTag="div"
{...props}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
</CodeBlock>
) : (
<code className={className} {...props}>
{children}
@@ -226,7 +226,7 @@ const ForumDetail = () => {
fontSize: 16,
lineHeight: 1.8,
minHeight: 200,
}} className="markdown-body">
}} className={styles['markdown-body']}>
<ReactMarkdown
remarkPlugins={[remarkMath, remarkGfm]}
rehypePlugins={[rehypeKatex, rehypeRaw]}
@@ -275,7 +275,7 @@ const ForumDetail = () => {
</Space>
<Text style={{ color: '#444', fontSize: 12 }}>#{index + 1}</Text>
</div>
<div style={{ color: '#eee', fontSize: isMobile ? 14 : 16 }}>
<div style={{ color: '#eee', fontSize: isMobile ? 14 : 16 }} className={styles['markdown-body']}>
<ReactMarkdown
remarkPlugins={[remarkMath, remarkGfm]}
rehypePlugins={[rehypeKatex, rehypeRaw]}

View File

@@ -0,0 +1,109 @@
.markdown-body {
color: #ddd;
font-size: 16px;
line-height: 1.8;
h1, h2, h3, h4, h5, h6 {
color: #fff;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
h1 { font-size: 2em; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 0.3em; }
h2 { font-size: 1.5em; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 0.3em; }
h3 { font-size: 1.25em; }
h4 { font-size: 1em; }
h5 { font-size: 0.875em; }
h6 { font-size: 0.85em; color: #888; }
p {
margin-top: 0;
margin-bottom: 16px;
}
a {
color: #1890ff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
ul, ol {
margin-top: 0;
margin-bottom: 16px;
padding-left: 2em;
}
li {
word-wrap: break-all;
}
blockquote {
margin: 0 0 16px;
padding: 0 1em;
color: #8b949e;
border-left: 0.25em solid #30363d;
}
/* Table Styles */
table {
display: block;
width: 100%;
width: max-content;
max-width: 100%;
overflow: auto;
border-spacing: 0;
border-collapse: collapse;
margin-top: 0;
margin-bottom: 16px;
thead {
background-color: rgba(255, 255, 255, 0.1);
}
tr {
background-color: transparent;
border-top: 1px solid rgba(255, 255, 255, 0.1);
&:nth-child(2n) {
background-color: rgba(255, 255, 255, 0.05);
}
}
th, td {
padding: 6px 13px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
th {
font-weight: 600;
text-align: left;
/* Ensure text color is readable */
color: #fff;
}
td {
color: #ddd;
}
}
/* Inline Code */
code:not([class*="language-"]) {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: rgba(110, 118, 129, 0.4);
border-radius: 6px;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
}
/* Images */
img {
max-width: 100%;
box-sizing: content-box;
background-color: transparent;
}
}