Uh oh!
There was an error while loading. Please reload this page.
fix(files): stop a cached collab snapshot resurrecting blank lines - #6293
Conversation
A collaborative markdown file's cold-start seed can come from a cached Yjs snapshot (workspace_file_collab_state.doc_state) rather than a fresh markdown re-parse. The snapshot is a raw CRDT binary, so it preserves top-level empty paragraphs that parseMarkdownToDoc/stripEmptyParagraphs strips from every parse target. The static placeholder always re-parses (clean); a warm seed replays the snapshot verbatim, so a stray blank line appears once the doc settles — and only intermittently, since a stale/cold cache falls through to the clean re-parse. Enforce the same no-top-level-empty-paragraph invariant on the Yjs side: - normalize.ts: stripEmptyTopLevelParagraphs(doc) shared helper. - seed.ts: repair the cached snapshot on read (self-heals legacy snapshots, preserving CRDT client ids; no data migration). - persist.ts: normalize before caching so new snapshots are clean by construction.
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview Adds Reviewed by Cursor Bugbot for commit 72c1a4a. Configure here. |
Greptile SummaryThe PR normalizes cached collaborative Yjs documents when they are seeded and persisted, preventing legacy empty top-level paragraphs from resurfacing.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/collab-doc/normalize.ts | Adds the shared Yjs fragment constant and an idempotent helper that removes empty top-level paragraphs. |
| apps/sim/lib/collab-doc/seed.ts | Repairs legacy cached snapshots before using them to seed collaborative rooms. |
| apps/sim/lib/collab-doc/persist.ts | Normalizes the Yjs document before deriving markdown and caching its snapshot. |
| apps/sim/lib/collab-doc/normalize.test.ts | Covers production-shaped normalization, idempotency, nested content, clean no-ops, and snapshot metadata preservation. |
| apps/sim/lib/collab-doc/converter.ts | Reuses the canonical collaborative document field constant. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Cache[Cached Yjs snapshot] --> Seed[Seed-time normalization]
Live[Live Yjs document] --> Persist[Persist-time normalization]
Seed --> Room[Collaborative room]
Persist --> Markdown[Durable markdown]
Persist --> CleanCache[Normalized cached snapshot]
Reviews (2): Last reviewed commit: "refactor(collab-doc): make COLLAB_DOC_FI..." | Re-trigger Greptile
Uh oh!
There was an error while loading. Please reload this page.
converter.ts had a duplicate 'default' fragment-name constant; import the now-exported one from normalize.ts so the value TipTap's Collaboration binding depends on lives in exactly one place. Fold the back-to-front loop note into the helper's TSDoc.
waleedlatif1
commented
Aug 5, 2026
waleedlatif1
commented
Aug 5, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 72c1a4a. Configure here.
Summary
workspace_file_collab_state.doc_state) instead of a fresh markdown re-parse. That snapshot is a raw CRDT binary, so it preserves top-level empty paragraphs thatparseMarkdownToDoc→stripEmptyParagraphsstrips from every parse target.len=0paragraphs sitting exactly between the description paragraph and the bullet list (plus one more before the trailing link) — the reported "two spaces."Changes
lib/collab-doc/normalize.ts—stripEmptyTopLevelParagraphs(doc)shared pure-Yjs helper enforcing the same no-top-level-empty-paragraph invariant on the CRDT side.lib/collab-doc/seed.ts— repair the cached snapshot on read (self-heals legacy snapshots, preserving CRDT client ids; returns original bytes untouched when already clean).lib/collab-doc/persist.ts— normalize before caching so new snapshots are clean by construction.Type of Change
Testing
normalize.test.ts: 5 tests (production repro, idempotency, no-op-when-clean, nested-untouched, encode/decode preserves config + CRDT ids). Verified they go red when the helper is neutralized.lib/collab-docsuite (24 tests) green;type-checkclean.Checklist