tfrere's picture
tfrere HF Staff
feat(frontend): editor refresh (embed studio, comment popover, shiki, top bar, hooks, styles)
76fc93a
Raw
History Blame Contribute Delete
1.7 kB
import { Extension } from "@tiptap/core";
import { Plugin, PluginKey } from "@tiptap/pm/state";
import { ySyncPluginKey } from "@tiptap/y-tiptap";
/**
* Prevents ProseMirror's scrollIntoView from firing after remote Yjs sync
* transactions. The y-tiptap binding calls tr.scrollIntoView() whenever
* _isLocalCursorInView() is true, but that check uses window dimensions
* instead of the actual scroll container. This fights the user's manual
* scroll, causing a "stuck then resume" feeling.
*
* Strategy: a filterTransaction hook sets a module-level flag when it
* sees a remote-origin sync transaction, and handleScrollToSelection
* suppresses the scroll while the flag is set. The flag is cleared
* asynchronously after the synchronous dispatch cycle completes.
*/
let _blockScroll = false;
export const ScrollGuard = Extension.create({
name: "scrollGuard",
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey("scroll-guard"),
filterTransaction(tr) {
const meta = tr.getMeta(ySyncPluginKey);
if (meta?.isChangeOrigin) {
_blockScroll = true;
queueMicrotask(() => { _blockScroll = false; });
}
return true;
},
}),
];
},
addOptions() {
return {};
},
onBeforeCreate() {
const existing = this.editor.options.editorProps?.handleScrollToSelection;
this.editor.setOptions({
editorProps: {
...this.editor.options.editorProps,
handleScrollToSelection(view) {
if (_blockScroll) return true;
if (existing) return existing.call(this, view);
return false;
},
},
});
},
});