fix(rich-md-editor): stop the editor flashing during an agent rewrite (#5160)

* fix(rich-md-editor): stop the editor flashing during an agent rewrite

Only reveal streamed chunks that extend what's already shown. A divergent chunk
(an agent rewrite/edit, e.g. removing appended text) would collapse the document
to the partial result and flash on every chunk; now the current content is held
in place and the final result is applied once on settle. Fresh writes still
reveal live.

* fix(rich-markdown-editor): seed shown-body on settled mount and track local edits

Seed lastSyncedBodyRef from a settled (non-streaming) mount and update it on
local edits via onUpdate, so the streaming hold engages on the very first agent
rewrite chunk (no collapse/flash) and the settle still applies the rewrite that
removes a local edit.
This commit is contained in:
Waleed
2026-06-20 18:28:26 -07:00
committed by GitHub
parent 39cfae9016
commit cb17207447
@@ -158,6 +158,14 @@ export function LoadedRichMarkdownEditor({
const [initialContent] = useState<JSONContent | string>(() =>
streamingAtMountRef.current ? '' : parseMarkdownToDoc(splitFrontmatter(content).body)
)
/**
* The body currently shown in the editor: seeded from a settled mount, updated on local edits (via
* onUpdate) and on each streamed sync. The streaming tick reveals a chunk only when it extends this,
* so an agent rewrite holds the current content instead of collapsing to a partial result.
*/
const lastSyncedBodyRef = useRef<string | null>(
streamingAtMountRef.current ? null : splitFrontmatter(content).body
)
const onChangeRef = useRef(onChange)
onChangeRef.current = onChange
const onSaveShortcutRef = useRef(onSaveShortcut)
@@ -263,13 +271,12 @@ export function LoadedRichMarkdownEditor({
},
onUpdate: ({ editor }) => {
const md = postProcessSerializedMarkdown(editor.getMarkdown())
lastSyncedBodyRef.current = md
onChangeRef.current(applyFrontmatter(settledRef.current?.frontmatter ?? '', md))
},
})
editorInstanceRef.current = editor
const lastSyncedBodyRef = useRef<string | null>(null)
const wasStreamingRef = useRef(streamingAtMountRef.current)
const pendingStreamBodyRef = useRef<string | null>(null)
@@ -291,6 +298,12 @@ export function LoadedRichMarkdownEditor({
streamRafRef.current = null
return
}
const shownBody = lastSyncedBodyRef.current
const extendsShown = shownBody === null || pending.startsWith(shownBody)
if (!extendsShown) {
streamRafRef.current = null
return
}
if (
pending.length > STREAM_REPARSE_THROTTLE_THRESHOLD &&
performance.now() - lastStreamParseAtRef.current < STREAM_REPARSE_THROTTLE_MS
@@ -303,7 +316,7 @@ export function LoadedRichMarkdownEditor({
lastStreamParseAtRef.current = performance.now()
const el = containerRef.current
const pinnedToBottom = el ? el.scrollHeight - el.scrollTop - el.clientHeight < 80 : false
editor.setEditable(false)
if (editor.isEditable) editor.setEditable(false)
editor.commands.setContent(parseMarkdownToDoc(pending), {
contentType: 'json',
emitUpdate: false,