Skip to content

fix(collaboration): sync numbering, styles, and headers in real-time (SD-2062, SD-2040)#2314

Open
tupizz wants to merge 1 commit intomainfrom
tadeu/sd-2062-sd-2040-collab-metadata-sync
Open

fix(collaboration): sync numbering, styles, and headers in real-time (SD-2062, SD-2040)#2314
tupizz wants to merge 1 commit intomainfrom
tadeu/sd-2062-sd-2040-collab-metadata-sync

Conversation

@tupizz
Copy link
Contributor

@tupizz tupizz commented Mar 6, 2026

Demo

CleanShot.2026-03-05.at.19.24.40.mp4

Problem

In collaboration mode, three types of document metadata only sync via the 30-second debounced DOCX export. This means other clients see stale or missing data for up to 30 seconds:

  1. Numbering definitions (SD-2062) — list indent/spacing comes from numbering level definitions stored in editor.converter.numbering. Without them, the receiving client renders lists with wrong marker spacing (34.6px vs 10.6px).

  2. Style definitions (SD-2040) — document default styles live in editor.converter.translatedLinkedStyles. The styles.apply API was blocked entirely during collaboration because there was no sync mechanism.

  3. Header/footer content (SD-2040) — header content pushes to the headerFooterJson Y.js map on edit, but headerIds (the mapping that tells the layout pipeline which header to render for each section type) was never synced. Joining clients had header content but no way to resolve it.

How collaboration sync works now

SuperDoc uses two real-time sync channels:

Data Y.js shared type When pushed
Document body XmlFragment('supereditor') via y-prosemirror Every PM transaction (automatic)
Media files Map('media') On image upload
Header/footer content Map('headerFooterJson') Every header editor keystroke
Numbering + Styles + headerFooterIds Map('converterMeta') On list-definitions-change, stylesDefaultsChanged events

Everything else (full DOCX XML) syncs via the debounced 30s export to Map('meta').docx.

What changed

1. Unified converter metadata sync (converterMeta Y.js map)

One map, one observer, one anti-ping-pong flag for all converter metadata. Adding a new type is: add a key to CONVERTER_META_KEYS, add push/apply logic, add a trigger.

Files: collaboration-helpers.js, collaboration.js

2. Header/footer atomicity fix

headerIds/footerIds are now bundled into every headerFooterJson entry alongside the content. Before, they lived in a separate Y.js map (converterMeta) and could arrive in a different network message than the content. The receiver would have header content but headerIds.default === undefined, so the layout pipeline couldn't resolve which header to render.

Files: collaboration-helpers.js (pushHeaderFooterToYjs, applyRemoteHeaderFooterChanges)

3. Initial state sync on join

Y.js map observers only fire for future changes. Data that arrived via Y.js sync before the observer was attached is missed. Fixed by reading existing map entries after attaching each observer.

Also push all initial metadata during initializeMetaMap (first client) so joining clients get headers, numbering, and styles immediately.

Files: collaboration.js

4. numberingPlugin collaboration hardening

The numbering plugin's appendTransaction clears listRendering when definitions are missing. In collaboration, definitions may arrive slightly after the document change (different Y.js types). The plugin now preserves synced listRendering when ydoc is present and the value already exists.

Files: numberingPlugin.js

5. Removed styles.apply collaboration gate

styles.apply was blocked during collaboration with "Stylesheet mutations cannot be synced via Yjs." Now that styles sync via the converterMeta map, the gate is removed.

Files: styles-adapter.ts, capabilities-adapter.ts

Test plan

  • Unit tests: 753 files pass, 8146 tests pass (32 new tests added)
  • Smoke test: editor loads and edits correctly
  • Manual: open two tabs with ?collab=1&room=<name>, create a numbered list on tab A, verify correct spacing on tab B
  • Manual: type in header on tab A, verify header appears on tab B
  • Manual: apply style change on tab A, verify tab B re-renders with new styles

…(SD-2062, SD-2040)

Add real-time sync for converter metadata that previously only reached
other clients via the 30-second debounced DOCX export.

- Numbering definitions (list indent/spacing)
- Style definitions (document defaults)
- Header/footer content and section-type mappings (headerIds/footerIds)
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e3bf7cd2c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +118 to +121
const editorEventHandlers = {
'list-definitions-change': () => pushConverterMetadata(this.editor, 'numbering'),
stylesDefaultsChanged: () => pushConverterMetadata(this.editor, 'styles'),
};

Choose a reason for hiding this comment

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

P1 Badge Add a push trigger for headerFooterIds updates

The new converter metadata wiring never republishes headerFooterIds after initialization: only 'list-definitions-change' and 'stylesDefaultsChanged' call pushConverterMetadata, so converterMeta['headerFooterIds'] can go stale while the editor keeps mutating header/footer IDs (for example updateGlobalTitlePageFlag in sections-adapter.ts updates converter.headerIds.titlePg/footerIds.titlePg). This stale key is then replayed for joiners in the same module, which can override fresher header/footer mapping and produce incorrect first/default header rendering in collaboration sessions.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants