diff --git a/scripts/previewRenderRevision.test.ts b/scripts/previewRenderRevision.test.ts new file mode 100644 index 0000000..64a3910 --- /dev/null +++ b/scripts/previewRenderRevision.test.ts @@ -0,0 +1,17 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import test from 'node:test'; + +const viewer = readFileSync('src/lib/MarkdownViewer.svelte', 'utf8'); + +test('preview rendering uses a revision and cleans up its debounce timer', () => { + assert.match(viewer, /let previewRenderRevision = 0;/); + assert.match(viewer, /const renderRevision = \+\+previewRenderRevision;/); + assert.match(viewer, /return \(\) => clearTimeout\(timer\);/); +}); + +test('a completed preview render verifies its tab, content, and revision', () => { + assert.match(viewer, /previewRenderRevision !== renderRevision/); + assert.match(viewer, /tabManager\.activeTabId !== tabId/); + assert.match(viewer, /currentTab\?\.rawContent !== rawContent/); +}); diff --git a/src/lib/MarkdownViewer.svelte b/src/lib/MarkdownViewer.svelte index f2a0cd5..0509f11 100644 --- a/src/lib/MarkdownViewer.svelte +++ b/src/lib/MarkdownViewer.svelte @@ -2353,17 +2353,33 @@ import { t } from './utils/i18n.js'; } } - let debounceTimer: number; + let previewRenderRevision = 0; $effect(() => { const tab = tabManager.activeTab; + const renderRevision = ++previewRenderRevision; if (tab && (tab.isSplit || (isEditing && settings.showToc)) && tab.rawContent !== undefined) { - if ((tab as any)._lastRenderedRawContent === tab.rawContent) return; - - clearTimeout(debounceTimer); - debounceTimer = setTimeout(() => { - renderTabPreviewFromRaw(tab).catch(console.error); + const tabId = tab.id; + const rawContent = tab.rawContent; + if ((tab as any)._lastRenderedRawContent === rawContent) return; + + const timer = setTimeout(() => { + renderMarkdownPreview(rawContent, tab.path) + .then((processed) => { + const currentTab = tabManager.activeTab; + if ( + previewRenderRevision !== renderRevision || + tabManager.activeTabId !== tabId || + currentTab?.rawContent !== rawContent + ) return; + tabManager.updateTabContent(tabId, processed); + (currentTab as any)._lastRenderedRawContent = rawContent; + tick().then(renderRichContent); + }) + .catch(console.error); }, 16); + + return () => clearTimeout(timer); } });