feat(storage): app-pause flush + vault search index
Some checks failed
CI / Windows build (push) Has been cancelled

Phase 6 (final storage phase).

- SidecarRepositoryRegistry tracks every open repo; SidecarFlushObserver
  (a WidgetsBindingObserver in main) flushes them all on
  inactive/hidden/paused/detached, awaiting each flush — the last
  strokes can't be lost on app close, not just on the 800ms timer.
- VaultSearchIndex rebuilds by scanning vault sidecars (the source of
  truth) — note titles, OCR text and document names — and search_provider
  queries it, so search spans notes + PDFs. Rebuilt on launch / after
  import.

The vault file-based storage migration (Phases 0-6) is complete:
annotations travel with the file, picked vault folder, atomic autosave,
one Import-file entry, SQLite migrated to sidecars. analyze clean,
tests green.
This commit is contained in:
2026-06-24 23:19:21 +08:00
parent 4886f1b2df
commit 24d13642fd
9 changed files with 749 additions and 68 deletions

View File

@@ -222,6 +222,7 @@ class BadnoteSidecar {
List<Bookmark>? bookmarks,
List<SidecarScratchLink>? scratchLinks,
Map<int, String>? legacyAnnotations,
this.ocrText,
this.legacyId,
}) : strokes = strokes ?? <int, List<EditorStroke>>{},
highlights = highlights ?? <int, List<SidecarHighlight>>{},
@@ -263,6 +264,14 @@ class BadnoteSidecar {
/// sidecars.
final Map<int, String> legacyAnnotations;
/// Searchable text recovered from this notebook's handwriting via local OCR
/// (Phase 6 search index). Persisted in the sidecar — the source of truth —
/// so the vault-scan search index can find handwritten notes WITHOUT the
/// (rebuildable, per-device) SQLite cache. Typed text already lives in the
/// strokes' `textContent`, so this holds ONLY the OCR'd handwriting. Null when
/// the notebook has no handwriting or OCR hasn't run.
final String? ocrText;
/// The legacy SQLite row id this sidecar was migrated from (a `documents.id`
/// or `notes.id`). Set ONLY by the one-time migration; it makes the migration
/// idempotent (a re-run recognizes an already-migrated item by this id even if
@@ -295,6 +304,7 @@ class BadnoteSidecar {
for (final entry in legacyAnnotations.entries)
entry.key.toString(): entry.value,
},
if (ocrText != null && ocrText!.isNotEmpty) 'ocrText': ocrText,
if (legacyId != null) 'legacyId': legacyId,
};
@@ -349,6 +359,7 @@ class BadnoteSidecar {
}
return out;
}(),
ocrText: json['ocrText'] as String?,
legacyId: json['legacyId'] as String?,
);
}