/** * 文件:content-processor.ts * 作用:内容处理器,负责处理markdown内容的各种转换 */ import { TFile, App } from 'obsidian'; import { ErrorHandler } from './error-handler'; export interface ProcessorOptions { enableImageToBase64?: boolean; enableLinkProcessing?: boolean; enableCodeHighlight?: boolean; enableMathProcessing?: boolean; platform?: string; } export class ContentProcessor { private app: App; constructor(app: App) { this.app = app; } /** * 处理图片链接,转换为base64或平台URL */ async processImages( content: string, file: TFile, options: ProcessorOptions = {} ): Promise { return await ErrorHandler.withErrorHandling(async () => { const { enableImageToBase64 = true } = options; if (!enableImageToBase64) { return content; } // WikiLink 图片处理: ![[image.png]] content = await this.processWikiLinkImages(content, file); // Markdown 图片处理: ![alt](image.png) content = await this.processMarkdownImages(content, file); return content; }, 'ContentProcessor.processImages', content) || content; } /** * 处理链接 */ processLinks(content: string, linkStyle: 'inline' | 'footnote' = 'inline'): string { return ErrorHandler.withErrorHandlingSync(() => { if (linkStyle === 'footnote') { return this.convertLinksToFootnotes(content); } return this.processInlineLinks(content); }, 'ContentProcessor.processLinks', content) || content; } /** * 处理代码块高亮 */ processCodeBlocks(content: string, highlightTheme: string = 'default'): string { return ErrorHandler.withErrorHandlingSync(() => { // 为代码块添加语法高亮类 return content.replace( /```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => { const language = lang || 'text'; return `
${this.escapeHtml(code.trim())}
`; } ); }, 'ContentProcessor.processCodeBlocks', content) || content; } /** * 处理数学公式 */ processMath(content: string, mathEngine: 'latex' | 'asciimath' = 'latex'): string { return ErrorHandler.withErrorHandlingSync(() => { // 行内公式: $...$ content = content.replace(/\$([^$]+)\$/g, (match, formula) => { return `${formula}`; }); // 块级公式: $$...$$ content = content.replace(/\$\$([^$]+)\$\$/g, (match, formula) => { return `
${formula}
`; }); return content; }, 'ContentProcessor.processMath', content) || content; } /** * 处理Gallery短代码 */ async processGalleryShortcode(content: string, galleryPath: string, numPics: number = 2): Promise { return await ErrorHandler.withErrorHandling(async () => { const galleryRegex = /{{}}\s*{{}}/g; return content.replace(galleryRegex, (match, dir, figcaption, q1, q2, unquoted) => { const pickAll = q1 === '1' || q2 === '1' || unquoted === '1'; const maxPics = pickAll ? 999 : numPics; // 这里应该调用实际的图片列表获取逻辑 // 为了简化,返回占位符 return ``; }); }, 'ContentProcessor.processGalleryShortcode', content) || content; } /** * 清理HTML标签 */ sanitizeHtml(content: string, allowedTags: string[] = []): string { return ErrorHandler.withErrorHandlingSync(() => { const allowedTagsSet = new Set(allowedTags); return content.replace(/<[^>]*>/g, (tag) => { const tagName = tag.match(/<\/?(\w+)/)?.[1]?.toLowerCase(); if (tagName && allowedTagsSet.has(tagName)) { return tag; } return ''; }); }, 'ContentProcessor.sanitizeHtml', content) || content; } /** * 处理自定义语法扩展 */ processCustomSyntax(content: string): string { return ErrorHandler.withErrorHandlingSync(() => { // 斜体标注: [fig 一段说明 /] content = content.replace(/\[fig\s+([^/]+)\s+\/\]/g, '$1'); // 彩色提示块 content = content.replace(/^\|\|([rgby]?)\s+(.+)$/gm, (match, color, text) => { const colorMap: Record = { 'r': 'background:#8B4513;color:white', 'g': 'background:#9ACD32;color:black', 'b': 'background:#D3D3D3;color:black', 'y': 'background:#FFFF99;color:black', '': 'background:#F5F5F5;color:black' }; const style = colorMap[color] || colorMap['']; return `
${text}
`; }); return content; }, 'ContentProcessor.processCustomSyntax', content) || content; } // 私有辅助方法 private async processWikiLinkImages(content: string, file: TFile): Promise { const wikiImageRegex = /!\[\[([^\]]+)\]\]/g; return content.replace(wikiImageRegex, (match, imagePath) => { // 这里应该实现实际的图片处理逻辑 return `${imagePath}`; }); } private async processMarkdownImages(content: string, file: TFile): Promise { const markdownImageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g; return content.replace(markdownImageRegex, (match, alt, src) => { // 这里应该实现实际的图片处理逻辑 return `${alt}`; }); } private convertLinksToFootnotes(content: string): string { const links: string[] = []; // 提取所有链接 content = content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => { links.push(url); return `${text}[${links.length}]`; }); // 添加脚注 if (links.length > 0) { content += '\n\n---\n\n'; links.forEach((url, index) => { content += `[${index + 1}]: ${url}\n`; }); } return content; } private processInlineLinks(content: string): string { return content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); } private escapeHtml(text: string): string { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } }