Skip to content

improvement(rich-md-editor): streaming, performance, minor bugfixes#5148

Open
icecrasher321 wants to merge 8 commits into
stagingfrom
fix/file-editor-md
Open

improvement(rich-md-editor): streaming, performance, minor bugfixes#5148
icecrasher321 wants to merge 8 commits into
stagingfrom
fix/file-editor-md

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

Smooth streaming support, perf improvements, minor bugfixes

Type of Change

  • Bug fix

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 20, 2026 4:14pm

Request Review

@cursor

cursor Bot commented Jun 20, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches file content reconciliation and autosave/stream locking; incorrect baseline or lock timing could show stale content or allow edits during agent writes, but changes are covered by new state tests.

Overview
Improves mothership file preview while the copilot writes: editors stay read-only via isAgentEditing (stream + a short grace between edit sections, released when the turn stops), with bubble menus hidden in the embedded panel. isAgentResponding is wired from chat sending state into that lock.

useEditableFileContent paces streamed text for display only (useSmoothText with snapOnNonAppend), keeps reconciliation/autosave on the true reducer content, and avoids remounting the loading frame when streaming ends before the initial fetch finishes.

text-editor-state adds hasBaseline so if a stream starts before fetch on an existing file, the first fetched pre-edit body is adopted as the baseline instead of being mistaken for the agent’s write (no stale flash or premature finalize).

Rich markdown throttles full-document re-parses during large streams (~40KB+), and frontmatter is applied from the settled snapshot on change.

useSmoothText instantly shows large content when streaming starts on an already-open document (no word-by-word replay of the whole file).

Reviewed by Cursor Bugbot for commit 6773fe9. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6773fe9. Configure here.

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR improves the rich-markdown streaming experience with smooth word-by-word pacing via useSmoothText, fixes a race where a stream completing before the initial file fetch would unmount a settled editor, and adds an isAgentEditing grace lock that keeps the editor read-only across brief gaps between sequential agent edit sections. It also throttles large-document reparsing during streaming to reduce main-thread saturation, removes a redundant frontmatterRef, and adds showBubbleMenu / isAgentEditing pass-through props.

  • useSmoothText snap-on-transition: when isStreamPhaseLocked flips false→true on an already-open document, the hook detects the edge and immediately reveals the full current content instead of replaying it word-by-word from the start — gated on content.length > RESUME_SKIP_THRESHOLD so brand-new files still animate normally.
  • hasBaseline flag in text-editor-state.ts: distinguishes a real saved-content baseline from the initial '' placeholder, fixing a race in the streaming-active finalize path where the file's own pre-edit content was mistaken for the agent's write and used to finalize prematurely.
  • isContentLoading fix: adds && !isInitialized so a stream finishing before the backing fetch resolves no longer unmounts the already-settled editor while isLoading is still true.

Confidence Score: 5/5

Safe to merge. The changes are well-scoped, carefully commented, and backed by new unit tests. All core data paths (reconciliation, saves, dirty tracking) operate on the true reducer content, not the display-paced value.

The streaming, pacing, and editor-lock changes are cleanly layered — useSmoothText is display-only so it cannot corrupt reconciliation or saves; the hasBaseline fix is guarded by a new flag and tested end-to-end; the isContentLoading guard is a narrow additive condition that only relaxes an over-eager unmount. No regressions to existing paths are apparent.

text-editor-state.ts: the parallel reconciling path (lines 150-151) still evaluates hasFetchedAdvanced without consulting state.hasBaseline, which was flagged in a prior review. The fix in this PR covers the streaming-active path but not the post-stream path.

Important Files Changed

Filename Overview
apps/sim/hooks/use-smooth-text.ts Adds a not-streaming→streaming transition detector that snaps revealed to full length when the existing document is large (>60 chars), preventing a full-file replay animation on in-place edits.
apps/sim/hooks/use-smooth-text.test.tsx New jsdom/React-root test harness covering snap-on-transition, mount-time skip, and new-file pacing; all four cases are well-specified.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor-state.ts Adds hasBaseline flag to distinguish a real saved-content baseline from the initial placeholder; fixes the streaming-active finalize-to-stale race. The parallel reconciling path (stream already ended, first fetch arrives) still lacks the hasBaseline guard.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts Integrates useSmoothText for display-only paced reveal, widens isStreamInteractionLocked with an isAgentEditing grace lock, and fixes isContentLoading to not unmount a settled editor while the backing fetch is still in-flight.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx Adds useAgentFileEditLock hook with a 1.5s grace window that keeps the editor read-only between sequential agent edit sections, releasing immediately once isAgentResponding drops.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.tsx Adds per-frame large-doc throttle for the streaming RAF tick, removes the redundant frontmatterRef in favour of reading directly from settledRef, and gates EditorBubbleMenu on the new showBubbleMenu prop.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor-state.test.ts New describe block covers the stream-before-fetch lifecycle, including baseline adoption mid-stream, stale-content hold in reconciling, and no-regression path for established-baseline finalization.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx Threads isAgentResponding down to ResourceContent so the edit-lock hook has the signal it needs.
apps/sim/app/workspace/[workspaceId]/home/home.tsx Passes isSending as isAgentResponding to MothershipView — minimal, correct one-liner.

Reviews (2): Last reviewed commit: "remove comments" | Re-trigger Greptile

Comment on lines +108 to +118
if (
isStreaming &&
!prevIsStreamingRef.current &&
content.length > RESUME_SKIP_THRESHOLD &&
revealed < content.length
) {
effectiveRevealed = content.length
revealedRef.current = content.length
setRevealed(content.length)
}
prevIsStreamingRef.current = isStreaming

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 setRevealed called during the render body

The new streaming-start snap (mirroring the existing snapOnNonAppend block below) calls setRevealed synchronously during the render phase, not inside a useEffect. React supports this derived-state pattern — it throws away the in-progress render and re-renders with the new state — but in React 18 concurrent mode, an interrupted render mutates prevIsStreamingRef.current before the committed render runs. If React discards the first render attempt and retries (e.g., a higher-priority update arrived), the ref is already set to isStreaming, so the committed render sees !prevIsStreamingRef.current === false and skips the snap even though revealed state is still 0. The net result is that the pre-existing document is paced from the beginning instead of snapping to full. This is an inherent limitation of the ref+setState-during-render pattern already used by snapOnNonAppend, so no new risk is introduced, but it is worth documenting why the correctness relies on there being no interleaved concurrent interruption between the two render attempts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants