diff --git a/js/DocumentService.js b/js/DocumentService.js index e9973fb..1560840 100644 --- a/js/DocumentService.js +++ b/js/DocumentService.js @@ -15,6 +15,10 @@ export class DocumentService { this.indexService = indexService; /** @type {Promise} */ this.markedPromise = this.initializeMarked(); + /** @type {Map} */ + this.documentCache = new Map(); + /** @type {number} */ + this.cacheMaxSize = 20; } /** @@ -50,8 +54,15 @@ export class DocumentService { */ setupRenderer(renderer) { const originalLink = renderer.link.bind(renderer); + const originalImage = renderer.image.bind(renderer); renderer.code = this.renderCode; + + renderer.image = (href, title, text) => { + const titleAttr = title ? ` title="${title}"` : ''; + return `${text}`; + }; + renderer.link = (href, title, text) => { const isExternal = href.startsWith('http'); const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : ''; @@ -138,6 +149,10 @@ export class DocumentService { * @throws {Error} If the document fails to load. */ async loadDocument(path) { + if (this.documentCache.has(path)) { + return this.documentCache.get(path); + } + try { const [response, marked] = await Promise.all([ fetch(path), @@ -163,12 +178,20 @@ export class DocumentService { const titleContent = metadata.title || indexDoc?.title || path.split('/').pop().replace('.md', ''); processedContent = this.ensureTitle(processedContent, titleContent); - return { + const result = { content: processedContent, metadata, marked, title: titleContent }; + + if (this.documentCache.size >= this.cacheMaxSize) { + const firstKey = this.documentCache.keys().next().value; + this.documentCache.delete(firstKey); + } + this.documentCache.set(path, result); + + return result; } catch (error) { throw new Error('Failed to load document'); } @@ -239,15 +262,30 @@ export class DocumentService { const mediaPath = `./docs/images/${filename}`; if (filename.toLowerCase().endsWith('.mp4')) { - return `\n