Repository navigation
Implement Vault File Watching and Auto-Sync for Desktop and Mobile #151
Copy link
Copy link
Closed
Labels
Description
Context
Liminal Notes is a local-first application where the file system is the source of truth. Users may modify their vault files externally using sync tools (Dropbox, Syncthing), git, or other text editors. Currently, the app does not react to these changes, leading to stale indices, "ghost" files in the tree, and potential data overwrites if a file is modified while open.
Goal
Implement a robust file watching system for both Desktop and Mobile platforms that detects external changes, updates the application state (indices, UI), and safely handles editing conflicts.
Requirements
1. File Detection Strategy
- Desktop (Tauri):
- Use the
notify(andnotify-debouncer-mini) crate to implement real-time, recursive file watching on the vault root. - Debounce events (e.g., 500ms) to prevent event flooding.
- Filter out hidden files/directories (e.g.,
.git,.liminal). - Emit events (
vault:file-changed,vault:file-deleted) to the frontend.
- Use the
- Mobile (React Native/Expo):
- Implement a Polling on Focus strategy (native file watching is unstable/complex in RN).
- Create a
FileWatcherservice that snapshots file modification times (mtime). - Trigger scans when the app enters the foreground (
AppState) and periodically (e.g., every 30s) while active. - Compare snapshots to detect creations, modifications, and deletions.
2. Application State Updates
- Indexing:
- Update
SearchIndexandLinkIndexincrementally when a file changes. - Add
updateFile(path)andremoveFile(path)methods to the index contexts to avoid expensive full vault rebuilds.
- Update
- File Tree:
- Ensure the file explorer sidebar automatically refreshes when files are added or removed externally.
3. Editor Conflict Resolution
Handle the scenario where the currently open note is modified externally:
- Clean State (No unsaved changes):
- Automatically reload the new content from disk.
- Constraint: Ensure the cursor position/scroll state is preserved if possible, or at least prevent a "cursor reset loop" if the content hasn't effectively changed.
- Dirty State (Unsaved changes):
- Do NOT overwrite the user's unsaved work.
- Display a non-intrusive Conflict Banner at the top of the editor.
- Banner Actions:
- Reload: Discard local changes and load from disk.
- Keep Mine: Dismiss banner (next save overwrites disk).
- Safety: Block save operations (including auto-save) while a conflict is active to prevent accidental data loss.
4. Technical Constraints & Implementation Details
- Mobile "Self-Trigger" Prevention: The mobile watcher must not flag the app's own save operations as "external" changes. Implement a mechanism (e.g.,
notifyInternalWrite) to update the watcher's snapshot immediately after a successful write. - Theming: Add semantic theme variables (
--ln-warning-bg,--ln-warning-text, etc.) tothemes.tsto ensure the conflict banner looks correct in all themes (Light, Dark, Dracula, etc.). - Documentation: Update
docs/to reflect the file watching architecture.
Acceptance Criteria
- Desktop: modifying a file via terminal/VS Code updates the Liminal UI immediately.
- Mobile: modifying a file externally and switching back to Liminal updates the UI.
- Search results reflect external content changes.
- Deleted files disappear from the file tree automatically.
- Editing a file that changes externally shows the "Conflict Banner".
- Auto-save does not overwrite external changes while the banner is visible.
Reactions are currently unavailable
Activity