diff --git a/packages/core/src/editor.ts b/packages/core/src/editor.ts index 4d356ea2..b9bd3762 100644 --- a/packages/core/src/editor.ts +++ b/packages/core/src/editor.ts @@ -436,6 +436,9 @@ export function createEditor(config: EditorConfig): EditorAPI { if (tableEditFlushed) flushScheduledChangeNow(); const beforeSelection = view.state.selection.main; const silent = opts?.silent === true; + // Host-driven silent loads must not leave a debounced user edit pending — the + // timer would emit stale Markdown against the newly loaded document. + if (silent) flushScheduledChangeNow(); const selection = resolveDocumentSelection( { anchor: beforeSelection.anchor, head: beforeSelection.head }, next.length, @@ -879,6 +882,7 @@ export function createEditor(config: EditorConfig): EditorAPI { replaceRange(from, to, insert, selection, opts) { if (destroyed) return; const silent = opts?.silent === true; + if (silent) flushScheduledChangeNow(); view.dispatch({ changes: { from, to, insert }, selection: selection diff --git a/packages/core/test/editor.test.ts b/packages/core/test/editor.test.ts index 85505eba..cffcc0a6 100644 --- a/packages/core/test/editor.test.ts +++ b/packages/core/test/editor.test.ts @@ -275,6 +275,60 @@ describe("createEditor", () => { editor.destroy(); }); + it("flushes a pending user change before a silent setDocument", () => { + vi.useFakeTimers(); + + const container = document.createElement("div"); + const docs: string[] = []; + const editor = createEditor({ + container, + initialValue: "", + parseDelayMs: 20, + onChange(doc) { + docs.push(doc); + } + }); + + editor.setDocument("before-load"); + expect(docs).toEqual([]); + + editor.setDocument("after-load", { silent: true }); + expect(docs).toEqual(["before-load"]); + expect(editor.getDocument()).toBe("after-load"); + + vi.advanceTimersByTime(20); + expect(docs).toEqual(["before-load"]); + + editor.destroy(); + }); + + it("flushes a pending user change before a silent replaceRange", () => { + vi.useFakeTimers(); + + const container = document.createElement("div"); + const docs: string[] = []; + const editor = createEditor({ + container, + initialValue: "abc", + parseDelayMs: 20, + onChange(doc) { + docs.push(doc); + } + }); + + editor.setDocument("abcdef"); + expect(docs).toEqual([]); + + editor.replaceRange(0, 6, "synced", undefined, { silent: true }); + expect(docs).toEqual(["abcdef"]); + expect(editor.getDocument()).toBe("synced"); + + vi.advanceTimersByTime(20); + expect(docs).toEqual(["abcdef"]); + + editor.destroy(); + }); + it("defers change emission until IME composition ends", async () => { const container = document.createElement("div"); const docs: string[] = [];