You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 6cb2a7b
Browse filesBrowse the repository at this point in the historyBrowse files
fix(files): stop the collaborative editor rewriting and reflowing a document on open
Opening a file rewrote it. Binding an editor to a seeded document emits a Yjs
update of its own — ProseMirror appends an empty paragraph to any doc that does
not end in one — which the relay saw as a real edit and persisted. Every open
therefore uploaded the file under a FRESH storage key and deleted the old one,
404ing the page's own in-flight content read, bumping "Last Updated" just from
viewing, and churning a blob per open. Worse, a trailing blank line cannot
serialize, so the file never recorded that paragraph and nothing reconciled the
two: each client that seeded without seeing another's contribution stacked one
more. A real document reached 18 against the placeholder's 1 — measured as the
pane growing several hundred pixels the instant the live editor took over.
- Seed and merge through the editor's own normal form (`editorNormalForm`), so
binding is a no-op and `canonicalizeYDoc` collapses an accumulated run back to
one. Placed at the collab boundary, not in `parseMarkdownToDoc`: only the CRDT
has to agree with the editor — every other consumer of the parse renders
through a real editor that normalizes itself.
- Skip a persist whose projection already matches the durable bytes. Byte length
is the free reject, so the compare read only happens when a no-op write is
actually on the table.
- Revoke collaborative readiness on a fatal join. The sticky `syncedOnce` latch
outlived the document: after a readiness timeout the provider drops `synced`
so the gate closes, but the latch re-opened it on the offline fallback's seed
flag — handing back an EDITABLE editor on a document the provider had
abandoned, with client autosave gated off because collaboration is nominally
on. Keystrokes went nowhere and vanished on reload, with no error shown.
- Recover from a superseded storage key instead of stranding the reader: a 404
re-resolves the file record, so the read re-keys onto the current object. And
do not focus-refetch durable bytes while the relay owns durability.
- Prefetch the workspace file list in the layout, where the sidebar already
reads it. `HydrationBoundary` defers an already-seen query to an effect that
SSR never runs, so a page-level prefetch of that key could not reach the
server render — the file route rendered a spinner and disagreed with the
client about the header's markup (a hydration mismatch).
- Load the document font with `display: block`. A swap repaints prose in
metric-adjusted Arial first, so paragraphs re-wrap when the real face lands.
Also: a detail-route `loading.tsx` (the segment was inheriting the list chrome),
`normalize.ts` renamed to `field.ts` now that it holds only the field constant,
and the duplicate `COLLAB_DOC_FIELD` in the streaming path folded into it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/collaboration/apply-streamed-markdown.ts
+7-5Lines changed: 7 additions & 5 deletions
Original file line number
Diff line number
Diff line change
@@ -2,10 +2,8 @@ import type { Editor } from '@tiptap/core'
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/collaboration/collab-streaming-integration.test.ts
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/collaboration/readiness.test.ts
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/collaboration/readiness.ts
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-fidelity.ts
+5-4Lines changed: 5 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -173,10 +173,11 @@ function stripEmptyListItemLines(markdown: string): string {
173
173
* round-trip ({@link stripEmptyListItemLines}), restores callout markers the serializer
174
174
* backslash-escapes (`> \[!NOTE\]` → `> [!NOTE]`), and collapses trailing blank lines to a single
175
175
* newline. Interior blank runs are NOT collapsed here — blank lines inside a fenced code block (or a
176
-
* verbatim raw-markdown-snippet) are significant, and a global collapse would corrupt them. Spurious
177
-
* interior blank runs between top-level blocks are removed upstream instead, by
178
-
* {@link parseMarkdownToDoc} stripping empty paragraphs, so a doc that has been through the editor
179
-
* never serializes with an interior blank run outside code in the first place. The table serializer's
176
+
* verbatim raw-markdown-snippet) are significant, and a global collapse would corrupt them. An interior
177
+
* run between top-level blocks is significant too: it is how an empty paragraph is written, and
178
+
* {@link parseMarkdownToDoc} reads exactly the count back out, so collapsing it here would delete the
179
+
* document's spacing. Only the TRAILING run is collapsed — it can carry no paragraph (see
180
+
* `clampEmptyParagraphs`) and would otherwise churn the file on every save. The table serializer's
180
181
* spurious surrounding blank lines are trimmed at the source (PipeSafeTable), so no global
181
182
* leading-newline strip is needed here — avoiding clobbering content that legitimately begins with
0 commit comments