feat(search): index PDF text, OCR scanned PDFs on import
All checks were successful
CI / Windows build (push) Successful in 15m50s
All checks were successful
CI / Windows build (push) Successful in 15m50s
Search now covers handwriting, the PDF text layer, AND scanned (rasterized) PDFs. - PdfTextIndexer runs at import: sums the embedded text layer across pages; if present it stores that as the document body, otherwise the PDF is rasterized and its rendered pages are OCR'd in the background. The result lands in the sidecar `pageText` field (distinct from `ocrText`, the handwriting OCR). Idempotent (skips a sidecar that already has pageText); degrades gracefully with no OCR engine. - pdfrx_page_text_source abstracts text/render so it's testable. - VaultSearchIndex now harvests title + typed text + handwriting OCR + PDF pageText, so search finds notes, typed PDFs and scanned PDFs. analyze clean, 409 tests green.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@@ -8,6 +10,7 @@ import '../models/note.dart';
|
||||
import '../providers/document_provider.dart';
|
||||
import '../providers/note_provider.dart';
|
||||
import '../providers/ocr_provider.dart';
|
||||
import '../providers/search_provider.dart';
|
||||
import '../editor/canvas/pen_editor_screen.dart';
|
||||
import '../services/pptx_service.dart';
|
||||
import '../services/vault_service.dart';
|
||||
@@ -250,6 +253,10 @@ class HomeScreen extends ConsumerWidget {
|
||||
final vaultPath = await vault.createNotebook(pickedPath);
|
||||
// Refresh the documents list so the new notebook shows on return.
|
||||
await ref.read(documentListProvider.notifier).loadDocuments();
|
||||
// For a PDF, index its document body (embedded text layer, or background
|
||||
// OCR of a rasterized/scanned PDF) into the sidecar so search covers it.
|
||||
// Fire-and-forget: import returns and opens the editor immediately.
|
||||
_indexPdfInBackground(ref, vaultPath);
|
||||
if (!context.mounted) return;
|
||||
await _openVaultFile(context, ref, vaultPath);
|
||||
} catch (e) {
|
||||
@@ -257,6 +264,25 @@ class HomeScreen extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Kick off background document-body indexing for an in-vault PDF (no-op for
|
||||
/// other types). Runs detached from the import await chain so the editor opens
|
||||
/// immediately; on completion it bumps the search-index epoch so the newly
|
||||
/// indexed text is searchable. Idempotency and graceful OCR degradation live in
|
||||
/// [PdfTextIndexer]; failures here are swallowed (search just misses the body).
|
||||
void _indexPdfInBackground(WidgetRef ref, String vaultPath) {
|
||||
final ext = p.extension(vaultPath).replaceFirst('.', '').toLowerCase();
|
||||
if (ext != 'pdf') return;
|
||||
final indexer = ref.read(pdfTextIndexerProvider);
|
||||
unawaited(() async {
|
||||
final indexed = await indexer.indexPdf(vaultPath);
|
||||
if (indexed != null && indexed.isNotEmpty) {
|
||||
// Force the next search to re-scan the vault (picks up the new pageText).
|
||||
final epoch = ref.read(searchIndexEpochProvider.notifier);
|
||||
epoch.state = epoch.state + 1;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/// Route an in-vault [filePath] to the correct editor by extension:
|
||||
/// pdf → [PenEditorScreen]; pptx/ppt → [PenSlideScreen]; docx → convert to
|
||||
/// PDF (best-effort, LibreOffice) then open as PDF. Unsupported / failed
|
||||
|
||||
Reference in New Issue
Block a user