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

@@ -1,3 +1,6 @@
import 'dart:io';
import '../editor/persistence/sidecar_repository.dart';
import '../models/note.dart';
import '../models/pen_tool.dart';
import 'database_service.dart';
@@ -6,7 +9,12 @@ import 'stroke_rasterizer.dart';
/// Runs OCR locally: typed text from strokes + handwriting via platform OCR.
class OcrService {
/// Extract searchable text from [note] and merge into the local FTS index.
/// Extract searchable text from [note] and persist it for search. The text is
/// written to the note's `*.badnote.json` sidecar `ocrText` field — the
/// vault-scan source of truth the Phase 6 search index reads — and also
/// appended to the (rebuildable) SQLite FTS cache so legacy callers keep
/// working. [note.id] is the synthetic note path, which is exactly the
/// `sourceFilePath` the editor opened its [SidecarRepository] with.
Future<void> processNote(Note note) async {
final parts = <String>[];
@@ -40,6 +48,22 @@ class OcrService {
final combined = parts.join(' ').trim();
if (combined.isEmpty) return;
// Persist into the note's sidecar so the vault-scan search index finds it.
// Prefer the editor's already-open repo (same in-memory sidecar — no race);
// if the note is closed, open/flush/dispose a transient handle.
final open = SidecarRepositoryRegistry.forPath(note.id);
if (open != null) {
open.scheduleOcrTextSave(combined);
await open.flush();
} else if (await File('${note.id}$kSidecarSuffix').exists()) {
final repo = await SidecarRepository.open(note.id, docType: 'notebook');
repo.scheduleOcrTextSave(combined);
await repo.flush();
repo.dispose();
}
// Also keep the legacy SQLite FTS cache warm (rebuildable; not the source
// of truth). Harmless if the row is never read.
final db = await DatabaseService.getInstance();
await db.appendOcrToFts(note.id, combined);
}