diff --git a/src/scroll.ts b/src/scroll.ts index 1a56bb7..62fa8eb 100644 --- a/src/scroll.ts +++ b/src/scroll.ts @@ -8,7 +8,11 @@ export function startObserving(sourcePane: HTMLElement, targetPane: HTMLElement) } if ('onscrollend' in window) { - sourcePane.addEventListener('scrollend', () => syncScrollProgress(sourcePane, targetPane)); + sourcePane.addEventListener('scrollend', () => { + if (!states.syncSuppressed) { + syncScrollProgress(sourcePane, targetPane); + } + }); } else { sourcePane.addEventListener('scroll', () => { if (states.scrollUpdater !== undefined) { @@ -16,12 +20,22 @@ export function startObserving(sourcePane: HTMLElement, targetPane: HTMLElement) } states.scrollUpdater = setTimeout(() => { - syncScrollProgress(sourcePane, targetPane); + if (!states.syncSuppressed) { + syncScrollProgress(sourcePane, targetPane); + } }, 100); }); } } +export function suppressScrollSync() { + states.syncSuppressed = true; +} + +export function resumeScrollSync() { + states.syncSuppressed = false; +} + export function syncScrollProgress(sourcePane: HTMLElement, targetPane: HTMLElement, animated = true) { const { line, progress } = getScrollProgress(sourcePane); scrollToProgress(targetPane, line, progress, animated); @@ -136,6 +150,8 @@ function clampProgressValue(value: number) { const states: { scrollUpdater: ReturnType | undefined; + syncSuppressed: boolean; } = { scrollUpdater: undefined, + syncSuppressed: false, }; diff --git a/src/view.ts b/src/view.ts index cf5cb16..8dfe5e6 100644 --- a/src/view.ts +++ b/src/view.ts @@ -5,7 +5,7 @@ import { renderMarkdown, renderMermaid, renderKatex, handlePostRender, applyStyl import { replaceImageURLs } from './features/image'; import { hidePreviewButtons, previewModes } from './support/settings'; import { localized } from './shared/strings'; -import { syncScrollProgress } from './scroll'; +import { syncScrollProgress, suppressScrollSync, resumeScrollSync } from './scroll'; import { resolveTaskToggle } from './features/task'; import { ClassNames, CacheKeys } from './shared/const'; @@ -377,10 +377,15 @@ function handleTaskItemToggle(event: MouseEvent) { // Let the native toggle stand for instant feedback; just sync the source const from = lineRange.from + toggle.offset; + + // Suppress scroll-sync for this dispatch: the editor's scrollDOM can emit a + // scrollend event that would jump the preview to the editor's cursor line. + suppressScrollSync(); MarkEdit.editorView.dispatch({ changes: { from, to: from + 1, insert: toggle.replacement }, annotations: silentChange.of(true), }); + requestAnimationFrame(resumeScrollSync); } const states: {