| 624 | |
| 625 | // 读取本地插件 README |
| 626 | private async getLocalPluginReadme( |
| 627 | pluginPath: string |
| 628 | ): Promise<{ success: boolean; content?: string; error?: string }> { |
| 629 | try { |
| 630 | // 尝试不同的 README 文件名(大小写不敏感) |
| 631 | const possibleReadmeFiles = ['README.md', 'readme.md', 'Readme.md', 'README.MD'] |
| 632 | |
| 633 | for (const filename of possibleReadmeFiles) { |
| 634 | const readmePath = path.join(pluginPath, filename) |
| 635 | try { |
| 636 | let content = await fs.readFile(readmePath, 'utf-8') |
| 637 | |
| 638 | // 将插件路径转换为 file:// URL(跨平台兼容) |
| 639 | const pluginPathUrl = pathToFileURL(pluginPath).href |
| 640 | |
| 641 | // 替换 Markdown 图片语法: |
| 642 | content = content.replace( |
| 643 | /!\[([^\]]*)\]\((?!http|file:)([^)]+)\)/g, |
| 644 | (_match, alt, imgPath) => { |
| 645 | const cleanPath = imgPath.replace(/^\.\//, '') |
| 646 | return `` |
| 647 | } |
| 648 | ) |
| 649 | |
| 650 | // 替换 HTML img 标签的 src 属性 |
| 651 | content = content.replace( |
| 652 | /<img([^>]*?)src=["'](?!http|file:)([^"']+)["']([^>]*?)>/gi, |
| 653 | (_match, before, src, after) => { |
| 654 | const cleanSrc = src.replace(/^\.\//, '') |
| 655 | return `<img${before}src="${pluginPathUrl}/${cleanSrc}"${after}>` |
| 656 | } |
| 657 | ) |
| 658 | |
| 659 | // 替换 Markdown 链接语法(排除锚点链接 #) |
| 660 | content = content.replace( |
| 661 | /\[([^\]]+)\]\((?!http|file:|#)([^)]+)\)/g, |
| 662 | (_match, text, linkPath) => { |
| 663 | const cleanPath = linkPath.replace(/^\.\//, '') |
| 664 | return `[${text}](${pluginPathUrl}/${cleanPath})` |
| 665 | } |
| 666 | ) |
| 667 | |
| 668 | // 替换 HTML a 标签的 href 属性(排除锚点链接和外部链接) |
| 669 | content = content.replace( |
| 670 | /<a([^>]*?)href=["'](?!http|file:|#)([^"']+)["']([^>]*?)>/gi, |
| 671 | (_match, before, href, after) => { |
| 672 | const cleanHref = href.replace(/^\.\//, '') |
| 673 | return `<a${before}href="${pluginPathUrl}/${cleanHref}"${after}>` |
| 674 | } |
| 675 | ) |
| 676 | |
| 677 | return { success: true, content } |
| 678 | } catch { |
| 679 | // 继续尝试下一个文件名 |
| 680 | continue |
| 681 | } |
| 682 | } |
| 683 | |