Skip to content

Commit 72c1a4a

Browse files
committed
refactor(collab-doc): make COLLAB_DOC_FIELD a single canonical constant
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.
1 parent 64c3191 commit 72c1a4a

2 files changed

Lines changed: 6 additions & 10 deletions

File tree

apps/sim/lib/collab-doc/converter.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
parseMarkdownToDoc,
1818
serializeDocToMarkdown,
1919
} from '@/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-parse'
20+
import { COLLAB_DOC_FIELD } from './normalize'
2021

2122
/**
2223
* Server-side conversion between a file's markdown and its collaborative Yjs document.
@@ -36,13 +37,6 @@ import {
3637
* 'server-only'` marker because this repo does not use that package.
3738
*/
3839

39-
/**
40-
* The Yjs `XmlFragment` name TipTap's Collaboration extension binds to. The client configures
41-
* `Collaboration.configure({ document })` with no explicit `field`, so it uses TipTap's default,
42-
* `'default'`. The server MUST target the same fragment or the client would sync an empty document.
43-
*/
44-
const COLLAB_DOC_FIELD = 'default'
45-
4640
let cachedSchema: Schema | null = null
4741

4842
/** The shared ProseMirror schema, built headlessly from the exact client extension set. */

apps/sim/lib/collab-doc/normalize.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import * as Y from 'yjs'
33
/**
44
* The Yjs `XmlFragment` name TipTap's Collaboration extension binds to (its default `field`). The
55
* client configures `Collaboration.configure({ document })` with no explicit `field`, so it uses
6-
* TipTap's default, `'default'`.
6+
* TipTap's default, `'default'`. Server-side conversion, seeding, and persistence MUST target the same
7+
* fragment or the client would sync an empty document — so this is the single canonical source consumed
8+
* by both bundles (it imports only `yjs`, making it safe from client and server alike).
79
*/
810
export const COLLAB_DOC_FIELD = 'default'
911

@@ -22,13 +24,13 @@ export const COLLAB_DOC_FIELD = 'default'
2224
*
2325
* Idempotent, and only TOP-LEVEL paragraphs are touched — blank lines that carry meaning inside a
2426
* construct (e.g. a loose list) live below the fragment root and are left alone. Runs its own Yjs
25-
* transaction so the deletions commit atomically.
27+
* transaction so the deletions commit atomically, iterating the fragment back-to-front so a deletion
28+
* never shifts a not-yet-checked index.
2629
*/
2730
export function stripEmptyTopLevelParagraphs(doc: Y.Doc): boolean {
2831
const fragment = doc.getXmlFragment(COLLAB_DOC_FIELD)
2932
let removed = false
3033
doc.transact(() => {
31-
// Back-to-front so a deletion never shifts a not-yet-checked index.
3234
for (let i = fragment.length - 1; i >= 0; i--) {
3335
const node = fragment.get(i)
3436
if (node instanceof Y.XmlElement && node.nodeName === 'paragraph' && node.length === 0) {

0 commit comments

Comments
 (0)