fix: 预览时本地图片不显示
问题: 重构后丢失了原始版本的图片路径转换逻辑,
相对路径图片(如 )无法显示。
修复:
- 新增 rehypeFixImages 自定义 rehype 插件
- 在 sanitize 之后、highlight 之前执行
- 将相对路径 src 转换为 file:// 绝对路径
- 基于当前文件路径计算目录,拼接图片路径
- renderMarkdown 新增 filePath 可选参数
- Preview 组件传入 activeTab.filePath
示例:
Markdown: 
文件路径: /project/docs/README.md
转换后: file:///project/docs/images/logo.png
This commit is contained in:
@@ -19,7 +19,7 @@ export function Preview() {
|
||||
}
|
||||
|
||||
const requestId = ++requestIdRef.current
|
||||
renderMarkdown(activeTab.content).then(result => {
|
||||
renderMarkdown(activeTab.content, activeTab.filePath).then(result => {
|
||||
// M-07: 只有当 requestId 仍为最新时才更新
|
||||
if (requestId === requestIdRef.current) {
|
||||
setHtml(result)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { unified } from 'unified'
|
||||
import { unified, type Plugin } from 'unified'
|
||||
import remarkParse from 'remark-parse'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkRehype from 'remark-rehype'
|
||||
@@ -6,7 +6,42 @@ import rehypeRaw from 'rehype-raw'
|
||||
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
|
||||
import rehypeStringify from 'rehype-stringify'
|
||||
import rehypeHighlight from 'rehype-highlight'
|
||||
import type { Element, Root } from 'hast'
|
||||
|
||||
// 自定义 rehype 插件:将相对路径图片转为 file:// 绝对路径
|
||||
function rehypeFixImages(filePath: string | null): Plugin<[], Root> {
|
||||
return () => (tree: Root) => {
|
||||
if (!filePath) return
|
||||
|
||||
// 获取文件所在目录
|
||||
const dir = filePath.replace(/[/\\][^/\\]+$/, '')
|
||||
const sep = dir.includes('\\') ? '\\' : '/'
|
||||
|
||||
function visit(node: Element | Root): void {
|
||||
if (!node.children) return
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'element' && child.tagName === 'img') {
|
||||
const src = child.properties?.src as string | undefined
|
||||
if (src && !src.startsWith('http://') && !src.startsWith('https://') && !src.startsWith('data:') && !src.startsWith('file://')) {
|
||||
// 相对路径转绝对路径
|
||||
child.properties = {
|
||||
...child.properties,
|
||||
src: 'file://' + (dir + sep + src).replace(/\\/g, '/')
|
||||
}
|
||||
}
|
||||
}
|
||||
if (child.type === 'element') {
|
||||
visit(child as Element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visit(tree)
|
||||
}
|
||||
}
|
||||
|
||||
export async function renderMarkdown(content: string, filePath?: string | null): Promise<string> {
|
||||
try {
|
||||
const processor = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkGfm)
|
||||
@@ -19,11 +54,10 @@ const processor = unified()
|
||||
img: [...(defaultSchema.attributes?.img ?? []), ['src']]
|
||||
}
|
||||
})
|
||||
.use(rehypeFixImages(filePath ?? null))
|
||||
.use(rehypeHighlight)
|
||||
.use(rehypeStringify)
|
||||
|
||||
export async function renderMarkdown(content: string): Promise<string> {
|
||||
try {
|
||||
const result = await processor.process(content)
|
||||
return String(result)
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user