Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ interface FileViewerProps {
saveRef?: React.MutableRefObject<(() => Promise<void>) | null>
streamingContent?: string
isAgentEditing?: boolean
streamIsIncremental?: boolean
disableStreamingAutoScroll?: boolean
previewContextKey?: string
}
Expand All @@ -108,6 +109,7 @@ export function FileViewer({
saveRef,
streamingContent,
isAgentEditing,
streamIsIncremental,
disableStreamingAutoScroll = false,
previewContextKey,
}: FileViewerProps) {
Expand Down Expand Up @@ -150,6 +152,7 @@ export function FileViewer({
saveRef={saveRef}
streamingContent={streamingContent}
isAgentEditing={isAgentEditing}
streamIsIncremental={streamIsIncremental}
disableStreamingAutoScroll={disableStreamingAutoScroll}
previewContextKey={previewContextKey}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ interface RichMarkdownEditorProps {
saveRef?: React.MutableRefObject<(() => Promise<void>) | null>
streamingContent?: string
isAgentEditing?: boolean
/**
* True when the stream delivers complete full-file snapshots (an `append`/`patch` edit built on the
* existing file) rather than a from-scratch rebuild (`create`/`update`). Incremental snapshots are
* applied live; a rebuild is only revealed while it extends what's shown (see the streaming tick).
*/
streamIsIncremental?: boolean
disableStreamingAutoScroll?: boolean
previewContextKey?: string
}
Expand All @@ -60,6 +66,7 @@ export const RichMarkdownEditor = memo(function RichMarkdownEditor({
saveRef,
streamingContent,
isAgentEditing,
streamIsIncremental,
disableStreamingAutoScroll = false,
previewContextKey,
}: RichMarkdownEditorProps) {
Expand Down Expand Up @@ -101,6 +108,7 @@ export const RichMarkdownEditor = memo(function RichMarkdownEditor({
isStreaming={isStreamInteractionLocked}
canEdit={canEdit}
autoFocus={autoFocus}
streamIsIncremental={streamIsIncremental}
disableStreamingAutoScroll={disableStreamingAutoScroll}
onChange={setDraftContent}
onSaveShortcut={saveImmediately}
Expand All @@ -117,6 +125,8 @@ interface LoadedRichMarkdownEditorProps {
isStreaming: boolean
canEdit: boolean
autoFocus?: boolean
/** See {@link RichMarkdownEditorProps.streamIsIncremental}. */
streamIsIncremental?: boolean
disableStreamingAutoScroll?: boolean
onChange: (markdown: string) => void
onSaveShortcut: () => Promise<void>
Expand All @@ -140,6 +150,7 @@ export function LoadedRichMarkdownEditor({
isStreaming,
canEdit,
autoFocus,
streamIsIncremental,
disableStreamingAutoScroll,
onChange,
onSaveShortcut,
Expand All @@ -160,8 +171,9 @@ export function LoadedRichMarkdownEditor({
)
/**
* 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.
* onUpdate) and on each streamed sync. Incremental edits (append/patch) stream complete snapshots and
* always apply; a from-scratch rebuild (create/update) only applies while it still extends this, so a
* rewrite holds the current content instead of collapsing to a partial result.
*/
const lastSyncedBodyRef = useRef<string | null>(
streamingAtMountRef.current ? null : splitFrontmatter(content).body
Expand All @@ -170,6 +182,10 @@ export function LoadedRichMarkdownEditor({
onChangeRef.current = onChange
const onSaveShortcutRef = useRef(onSaveShortcut)
onSaveShortcutRef.current = onSaveShortcut
// Read in the RAF tick so an already-scheduled tick still sees the latest edit kind (it can change
// between sessions within one turn, e.g. an append followed by a rewrite).
const streamIsIncrementalRef = useRef(streamIsIncremental)
streamIsIncrementalRef.current = streamIsIncremental
const router = useRouter()
const routerRef = useRef(router)
routerRef.current = router
Expand Down Expand Up @@ -300,7 +316,11 @@ export function LoadedRichMarkdownEditor({
}
const shownBody = lastSyncedBodyRef.current
const extendsShown = shownBody === null || pending.startsWith(shownBody)
if (!extendsShown) {
// Incremental edits (append/patch) arrive as complete full-file snapshots, so each is applied
// live — ProseMirror diffs the localized change in place (mid-doc rewrite, insertion, delete).
// A rebuild (create/update streamed from scratch) only extends while revealing from empty; once
// a chunk would collapse the established document it is held until settle, avoiding the flicker.
if (!streamIsIncrementalRef.current && !extendsShown) {
streamRafRef.current = null
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ export const ResourceContent = memo(function ResourceContent({
}, [workspaceId, streamFileName])

const disableStreamingAutoScroll = previewSession?.operation === 'patch'
// `append`/`patch` stream complete full-file snapshots (built on the existing file), so the editor
// applies each live. `create`/`update` are streamed from scratch and would collapse an open doc, so
// the editor holds until settle. See the rich-markdown streaming tick.
const streamIsIncremental =
previewSession?.operation === 'append' || previewSession?.operation === 'patch'
const isTextPreview =
!!previewSession && resolveFileCategory(null, previewSession.fileName) === 'text-editable'
// Feed streamed content only while actively streaming. On completion the session keeps
Expand Down Expand Up @@ -195,6 +200,7 @@ export const ResourceContent = memo(function ResourceContent({
previewMode={previewMode ?? 'preview'}
streamingContent={textStreamingContent}
isAgentEditing={isAgentEditing}
streamIsIncremental={streamIsIncremental}
disableStreamingAutoScroll={disableStreamingAutoScroll}
previewContextKey={previewContextKey}
/>
Expand All @@ -218,6 +224,7 @@ export const ResourceContent = memo(function ResourceContent({
previewSession?.fileId === resource.id ? textStreamingContent : undefined
}
isAgentEditing={isAgentEditing}
streamIsIncremental={streamIsIncremental}
disableStreamingAutoScroll={disableStreamingAutoScroll}
previewContextKey={previewContextKey}
/>
Expand Down Expand Up @@ -604,6 +611,7 @@ interface EmbeddedFileProps {
previewMode?: PreviewMode
streamingContent?: string
isAgentEditing?: boolean
streamIsIncremental?: boolean
disableStreamingAutoScroll?: boolean
previewContextKey?: string
}
Expand All @@ -615,6 +623,7 @@ function EmbeddedFile({
previewMode,
streamingContent,
isAgentEditing,
streamIsIncremental,
disableStreamingAutoScroll = false,
previewContextKey,
}: EmbeddedFileProps) {
Expand Down Expand Up @@ -657,6 +666,7 @@ function EmbeddedFile({
previewMode={previewMode}
streamingContent={streamingContent}
isAgentEditing={isAgentEditing}
streamIsIncremental={streamIsIncremental}
disableStreamingAutoScroll={disableStreamingAutoScroll}
previewContextKey={previewContextKey}
/>
Expand Down
Loading