···212 pub edit_root: Option<StrongRef<'static>>,
213 /// StrongRef to the most recent sh.weaver.edit.diff record.
214 pub last_diff: Option<StrongRef<'static>>,
215- /// Whether the current doc state is synced with PDS.
216- /// False if local has changes not yet pushed to PDS.
217- pub is_synced: bool,
0218}
219220impl PartialEq for LoadedDocState {
···993 };
994 let loro_cursor = content.get_cursor(cursor_offset, Side::default());
995996- // Track sync state - if synced, record current version
997- let last_synced_version = if state.is_synced {
998- Some(doc.oplog_vv())
999- } else {
1000- None
1001- };
10021003 Self {
1004 doc,
···212 pub edit_root: Option<StrongRef<'static>>,
213 /// StrongRef to the most recent sh.weaver.edit.diff record.
214 pub last_diff: Option<StrongRef<'static>>,
215+ /// Version vector of the last known PDS state.
216+ /// Used to determine what changes need to be synced.
217+ /// None if never synced to PDS.
218+ pub synced_version: Option<VersionVector>,
219}
220221impl PartialEq for LoadedDocState {
···994 };
995 let loro_cursor = content.get_cursor(cursor_offset, Side::default());
996997+ // Use the synced version from state (tracks the PDS version vector)
998+ let last_synced_version = state.synced_version;
00009991000 Self {
1001 doc,
+21-3
crates/weaver-app/src/components/editor/sync.rs
···671 entry_ref: local.entry_ref, // Restored from localStorage
672 edit_root: None,
673 last_diff: None,
674- is_synced: false, // Local-only, not synced to PDS
675 }))
676 }
677···692 }
693 }
694000695 Ok(Some(LoadedDocState {
696 doc,
697 entry_ref: None, // Entry ref comes from the entry itself, not edit state
698 edit_root: Some(pds.root_ref),
699 last_diff: pds.last_diff_ref,
700- is_synced: true, // Just loaded from PDS, fully synced
701 }))
702 }
703···705 // Both exist - merge using CRDT
706 tracing::debug!("Merging document from localStorage and PDS");
7070000000000000708 let doc = LoroDoc::new();
709710 // Import local snapshot first
···724 }
725 }
72600727 Ok(Some(LoadedDocState {
728 doc,
729 entry_ref: local.entry_ref, // Restored from localStorage
730 edit_root: Some(pds.root_ref),
731 last_diff: pds.last_diff_ref,
732- is_synced: false, // Local had state, may have unsynced changes
733 }))
734 }
735 }
···671 entry_ref: local.entry_ref, // Restored from localStorage
672 edit_root: None,
673 last_diff: None,
674+ synced_version: None, // Local-only, never synced to PDS
675 }))
676 }
677···692 }
693 }
694695+ // Capture the version after loading all PDS state - this is our sync baseline
696+ let synced_version = Some(doc.oplog_vv());
697+698 Ok(Some(LoadedDocState {
699 doc,
700 entry_ref: None, // Entry ref comes from the entry itself, not edit state
701 edit_root: Some(pds.root_ref),
702 last_diff: pds.last_diff_ref,
703+ synced_version, // Just loaded from PDS, fully synced
704 }))
705 }
706···708 // Both exist - merge using CRDT
709 tracing::debug!("Merging document from localStorage and PDS");
710711+ // First, reconstruct the PDS state to get its version vector
712+ let pds_doc = LoroDoc::new();
713+ if let Err(e) = pds_doc.import(&pds.root_snapshot) {
714+ tracing::warn!("Failed to import PDS root snapshot for VV: {:?}", e);
715+ }
716+ for updates in &pds.diff_updates {
717+ if let Err(e) = pds_doc.import(updates) {
718+ tracing::warn!("Failed to apply PDS diff for VV: {:?}", e);
719+ }
720+ }
721+ let pds_version = pds_doc.oplog_vv();
722+723+ // Now create the merged doc
724 let doc = LoroDoc::new();
725726 // Import local snapshot first
···740 }
741 }
742743+ // Use the PDS version as our sync baseline - any local changes
744+ // beyond this will be detected as unsynced
745 Ok(Some(LoadedDocState {
746 doc,
747 entry_ref: local.entry_ref, // Restored from localStorage
748 edit_root: Some(pds.root_ref),
749 last_diff: pds.last_diff_ref,
750+ synced_version: Some(pds_version),
751 }))
752 }
753 }