mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-05-06 20:42:52 +08:00
feat(MarkdownViewer):增强 Markdown 解析和渲染能力
- 添加对 GFM (GitHub Flavored Markdown) 的支持 - 增加数学公式渲染功能 - 实现加粗编号标题的特殊处理 - 优化代码块样式 - 添加图片缩放功能
This commit is contained in:
@@ -30,19 +30,23 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"github-markdown-css": "^5.8.1",
|
||||
"katex": "^0.16.21",
|
||||
"katex": "^0.16.22",
|
||||
"lottie-react": "^2.4.1",
|
||||
"lucide-react": "^0.487.0",
|
||||
"markdown-navbar": "^1.4.3",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-medium-image-zoom": "^5.2.14",
|
||||
"react-resizable-panels": "^2.1.8",
|
||||
"react-router-dom": "^7.5.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"remark-gfm": "1.0.0",
|
||||
"rehype-katex": "^6.0.2",
|
||||
"remark-gfm": "3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.1.0",
|
||||
"tailwindcss": "^4.1.3",
|
||||
|
||||
@@ -2,16 +2,27 @@ import { useState } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { Button } from '@/components/ui/button.tsx'
|
||||
import { Copy, Download, FileText, ArrowRight } from 'lucide-react'
|
||||
import { toast } from 'sonner' // 你可以换成自己的通知组件
|
||||
import { toast } from 'react-hot-toast' // 你可以换成自己的通知组件
|
||||
import Error from '@/components/Lottie/error.tsx'
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
||||
import { solarizedlight as codeStyle } from 'react-syntax-highlighter/dist/cjs/styles/prism'
|
||||
import { atomDark as codeStyle } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
||||
import Zoom from 'react-medium-image-zoom'
|
||||
import 'react-medium-image-zoom/dist/styles.css'
|
||||
|
||||
import 'github-markdown-css/github-markdown-light.css'
|
||||
import { FC } from 'react'
|
||||
import Loading from '@/components/Lottie/Loading.tsx'
|
||||
import Idle from '@/components/Lottie/Idle.tsx'
|
||||
import { useTaskStore } from '@/store/taskStore'
|
||||
import StepBar from '@/pages/HomePage/components/StepBar.tsx'
|
||||
import gfm from 'remark-gfm'
|
||||
import remarkMath from 'remark-math'
|
||||
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
interface MarkdownViewerProps {
|
||||
content: string
|
||||
status: 'idle' | 'loading' | 'success' | 'failed'
|
||||
@@ -31,6 +42,8 @@ const MarkdownViewer: FC<MarkdownViewerProps> = ({ content, status }) => {
|
||||
const currentTask = useTaskStore(state => state.getCurrentTask())
|
||||
const taskStatus = currentTask?.status || 'PENDING'
|
||||
const retryTask = useTaskStore.getState().retryTask
|
||||
let firstHeadingRendered = false
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(content)
|
||||
@@ -124,7 +137,58 @@ const MarkdownViewer: FC<MarkdownViewerProps> = ({ content, status }) => {
|
||||
<div className="markdown-body flex-1 bg-white">
|
||||
{' '}
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[gfm,remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
img: ({ node, ...props }) => (
|
||||
<Zoom>
|
||||
<img
|
||||
{...props}
|
||||
className="rounded-lg shadow-md max-w-full cursor-pointer mx-auto my-4 max-h-[300px] "
|
||||
alt={props.alt || ''}
|
||||
/>
|
||||
</Zoom>
|
||||
),
|
||||
strong({ node, children, ...props }){
|
||||
return <strong className="text-lg font-bold my-4 text-blue-600" {...props}>{children}</strong>
|
||||
},
|
||||
li({ node, children, ...props }) {
|
||||
const rawText = String(children)
|
||||
|
||||
// 检测是否是“加粗的编号开头项”,比如 "**2. 算法摄影的兴起**"
|
||||
const isFakeHeading = /^(\*\*.+\*\*)$/.test(rawText.trim())
|
||||
|
||||
if (isFakeHeading) {
|
||||
return (
|
||||
<p className="text-lg font-bold my-4 text-gray-800 text-left">
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
return <li {...props}>{children}</li>
|
||||
},
|
||||
h1({ node, children, ...props }) {
|
||||
return (
|
||||
<h1
|
||||
className="text-3xl text-center font-bold my-6 text-blue-600"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
)
|
||||
},
|
||||
h2({ node, children, ...props }) {
|
||||
return (
|
||||
<h2
|
||||
className="text-2xl font-bold my-4 text-blue-600"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
)
|
||||
},
|
||||
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
const codeContent = String(children).replace(/\n$/, '')
|
||||
@@ -133,15 +197,17 @@ const MarkdownViewer: FC<MarkdownViewerProps> = ({ content, status }) => {
|
||||
return (
|
||||
<div className="group relative">
|
||||
<SyntaxHighlighter
|
||||
style={codeStyle}
|
||||
language={match[1]}
|
||||
PreTag="div"
|
||||
{...props}
|
||||
style={codeStyle}
|
||||
language={match[1]}
|
||||
PreTag="div"
|
||||
{...props}
|
||||
>
|
||||
{codeContent}
|
||||
</SyntaxHighlighter>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log('点击负责')
|
||||
navigator.clipboard.writeText(codeContent)
|
||||
toast.success('代码已复制')
|
||||
}}
|
||||
|
||||
@@ -16,6 +16,14 @@ BASE_PROMPT = '''
|
||||
输出说明:
|
||||
- 仅返回最终的 **Markdown 内容**。
|
||||
- **不要**将输出包裹在代码块中(例如:```` ```markdown ````,```` ``` ````)。
|
||||
请注意,在生成 Markdown 时,避免将编号标题(如“1. **内容**”)写成有序列表的格式,以免解析错误。
|
||||
|
||||
- 如果要加粗并保留编号,应使用 `1\. **内容**`(加反斜杠),防止被误解析为有序列表。
|
||||
- 或者使用 `## 1. 内容` 的形式作为标题。
|
||||
|
||||
请确保以下格式 **不会出现误渲染**:
|
||||
❌ `1. **xxx**`
|
||||
✅ `1\. **xxx**` 或 `## 1. xxx`
|
||||
|
||||
视频分段(格式:开始时间 - 内容):
|
||||
|
||||
@@ -27,10 +35,14 @@ BASE_PROMPT = '''
|
||||
根据上面的分段转录内容,生成结构化的笔记,遵循以下原则:
|
||||
|
||||
1. **完整信息**:记录尽可能多的相关细节,确保内容全面。
|
||||
2. **清晰结构**:用合适的标题级别(`##`,`###`)整理内容,概述每个部分的要点。(如果额外重要的任务有格式需求可以不遵守)
|
||||
2. **清晰结构**:用合适的标题级别(`##`,`###`)整理内容,概述每个部分的要点。主标题用`#`来标识(如果额外重要的任务有格式需求可以不遵守)
|
||||
3. **去除无关内容**:省略广告、填充词、问候语和不相关的言论。
|
||||
4. **保留关键细节**:保留重要事实、示例、结论和建议。(如果额外重要的任务有格式需求可以不遵守)
|
||||
5. **可读布局**:必要时使用项目符号,并保持段落简短,增强可读性。(如果额外重要的任务有格式需求可以不遵守)
|
||||
6. 视频中提及的数学公式必须保留,并以 LaTeX 语法形式呈现,适合 Markdown 渲染。
|
||||
|
||||
|
||||
请始终遵循此规则。
|
||||
|
||||
额外重要的任务如下(每一个都必须严格完成):
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class VideoRequest(BaseModel):
|
||||
task_id: Optional[str] = None
|
||||
format: Optional[list] = []
|
||||
style: str = None
|
||||
extras: Optional[str]
|
||||
extras: Optional[str]=None
|
||||
video_understanding: Optional[bool] = False
|
||||
video_interval: Optional[int] = 0
|
||||
grid_size: Optional[list] = []
|
||||
|
||||
Reference in New Issue
Block a user