From 02564cadfa53670d2b9089688228c6881c4768e8 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Fri, 5 Jun 2026 20:30:26 -0400 Subject: [PATCH] Add roadmap items for PDF support and split view editor --- .coverage | Bin 53248 -> 53248 bytes ROADMAP.md | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 225 insertions(+), 2 deletions(-) diff --git a/.coverage b/.coverage index 50b83adc60093ddb8a28d1106af22148a0d54b24..eed886e79a3320d745f26e43261458fc77a629bf 100644 GIT binary patch delta 727 zcmYLHZ%7ki7=P~G+iri(-NeF^KDf$+VCqVu$hBJ}l`sv3^h?(IXMd2O0yDdI?^J5) z$YN5d;6_GJP)JsvqDDd{Vi$ghpkOP61qm$=(&gNB^R%81&+~hq=l6Sm4=*o{ARIv> zI$UlV2R6k{v(MQ8dyZ{lYuKG^8B1DbEK`=p7CW<F>@xx^w~5%nrrPf zg(`d27&hcngmJI&Rp?kxBVCcFBaH1Wp|?E;j}jCQ5PxxI(Br)HN3uaQ=6qB~Cc7=I zka~RptkiVC4GgGZz-~-r0lZPN8sS@G=g0H5Tm$8GaPjn^aQRT}_2JB9)qGi!cy}c% zKoh?kSACHbYQj8*DCUVvIK#_H!U%~yN9$*2%RNbJC z!A+roiCbhG-K;8_`oTZJth>0j=;6u3L?QOv>+5d-0k(Dp`zF?^8y9t-xPSX4>C#+Q zvJDIxN@hJX|7X5`Z~-Fy0s{lpIRJNm^CCbR!FM0yD1@gi;+#^bGM&oyRel=0s8ndWm{ss{e9_I{+bZ44xZ(Y&l6sJM0;k&UH}~gm)WKN&|=I}9b)^LIp!tvgb6Zd8UIX$jb&m|q12hf0bnJ3tKys#^1E&DG5fP+!FluGQu^r)&EUebYZ){DtBQI}#x}+py6@YOPCwd6 zm9zo_xlhJmNi7O(TwE(=6A(vMO$)4}GI9jNIcuw9`<=wBU38 zHNdw0NT!gsQ1Gt3P`T`iMIRm$^vpVe`=@IIQj!{Pnm)MQdj?hWXn!7IB=*| zuTrytigV)>lqn2g^U<4t Mf10Le)N!-#4+RC)B>(^b diff --git a/ROADMAP.md b/ROADMAP.md index 0a83883..36ca263 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -233,6 +233,229 @@ - [ ] UI : bouton « Exporter » dans le viewer (fichier unique) + dans le menu vault (export complet) - [ ] Endpoints : `GET /api/export/html`, `GET /api/export/md-bundle`, `GET /api/export/epub` +### 74. Support complet des documents PDF +- **Effort :** 4-5 jours | **Impact :** 🟡 +- **Description :** Prise en charge native des fichiers PDF dans ObsiGate avec parité fonctionnelle complète avec les documents Markdown : apparition dans l'arborescence, indexation full-text, visualisation inline dans le navigateur, recherche TF-IDF, et téléchargement. Actuellement, les PDF sont traités comme des fichiers binaires non supportés (message « Ce fichier est binaire et ne peut pas être affiché » + bouton download). +- **Architecture actuelle :** + - `SUPPORTED_EXTENSIONS` (`backend/indexer.py:56`) : ne contient pas `.pdf` → les PDF sont ignorés par l'indexeur, le file watcher, et l'arborescence + - `api_file_view()` (`backend/main.py:2303`) : UnicodeDecodeError sur lecture → retourne `unsupported: true` + - `frontend/js/viewer.js:377` : si `data.unsupported` → affiche le message binaire + bouton download + - `pdf_export.py` : exporte du MD → PDF (WeasyPrint) — aucun rapport avec la lecture de PDF existants + - Icone PDF déjà présente dans `EXT_ICONS` frontend (`.pdf` → `file-text`) — inutilisée +- **Sous-tâches :** + +##### A. Backend — Extraction de texte PDF (1-1.5 jour) + - [ ] **A1. Dépendance** : Ajouter `pymupdf` (PyMuPDF/fitz) à `requirements.txt` — bibliothèque C performante avec extraction texte + métadonnées, déjà compatible avec l'image Docker (libs système GTK/Pango déjà présentes pour WeasyPrint). Alternative légère : `pypdf` (pure Python, pas de deps système) si pymupdf pose problème. + - [ ] **A2. Module `backend/pdf_reader.py`** : Créer un module dédié avec les fonctions : + - `extract_pdf_text(file_path: Path) -> str` : extrait tout le texte du PDF, page par page, avec séparateur `\f` entre pages. Gère les PDF encodés, protégés par mot de passe (retourne erreur explicite), et corrompus. + - `extract_pdf_metadata(file_path: Path) -> dict` : extrait titre, auteur, sujet, nombre de pages, taille. + - `extract_pdf_preview(file_path: Path, max_chars: int = 100000) -> str` : extrait les N premiers caractères pour l'indexation (limité par `SEARCH_CONTENT_LIMIT`). + - [ ] **A3. Fallback pypdf** : Si pymupdf non disponible (exception d'import), fallback automatique sur `pypdf` avec un log warning. Code structuré avec une interface abstraite (`PdfReader` protocol) pour swap transparent. + +##### B. Backend — Indexation des PDF (1 jour) + - [ ] **B1. Ajout à `SUPPORTED_EXTENSIONS`** : Ajouter `.pdf` au set dans `backend/indexer.py:56`. Déclencher un rebuild complet de l'index (incrémental via le file watcher pour les nouveaux PDFs). + - [ ] **B2. Modification de `index_document()`** (`backend/indexer.py:524`) : Dans la fonction d'indexation, détecter l'extension `.pdf` et appeler `extract_pdf_text()` au lieu de `read_text()`. Le texte extrait alimente le pipeline TF-IDF existant — aucun changement nécessaire dans `search.py`. + - [ ] **B3. Métadonnées PDF dans le document info** : Enrichir la structure de retour de `index_document()` avec les champs spécifiques PDF : `page_count`, `pdf_title` (titre extrait des métadonnées, prioritaire sur le nom de fichier), `pdf_author`. + - [ ] **B4. Gestion d'erreur robuste** : PDF corrompu → log warning + skip (ne pas bloquer l'indexation). PDF volumineux (>50 Mo) → log info + extraction tronquée à `SEARCH_CONTENT_LIMIT`. Timeout d'extraction configurable (30s par défaut). + +##### C. Backend — API endpoints PDF (0.5 jour) + - [ ] **C1. Modification de `api_file_view()`** (`backend/main.py:2270`) : Avant la tentative de `read_text()`, détecter `.pdf` par extension. Pour les PDF : + - Extraire le texte avec `extract_pdf_text()` + - Extraire les métadonnées (pages, auteur) + - Retourner une réponse structurée : `is_pdf: true`, `page_count`, `pdf_metadata`, `html` (aperçu texte formaté), `raw_length` + - Le champ `html` contient un rendu texte simple (pas de markdown) : texte paginé ou première page formatée + - [ ] **C2. Nouvel endpoint `GET /api/file/{vault}/pdf/stream`** : Sert le fichier PDF brut avec `Content-Type: application/pdf` et `Content-Disposition: inline` pour visualisation dans le navigateur. Supporte le `Range` header (HTTP 206 Partial Content) pour le streaming progressif des gros PDFs — essentiel pour la performance sur des documents volumineux. + - [ ] **C3. Nouvel endpoint `GET /api/file/{vault}/pdf/info`** : Retourne les métadonnées seules (pages, titre, auteur) sans le contenu — permet à l'UI d'afficher les infos avant de charger le PDF lourd. + - [ ] **C4. Endpoint download** : Déjà fonctionnel (`/api/file/{vault}/download`) — aucun changement nécessaire. + +##### D. Frontend — Arborescence de fichiers (0.5 jour) + - [ ] **D1. Icône et filtre** : L'icône PDF (`file-text` de Lucide) est déjà mappée dans `EXT_ICONS` (`frontend/js/utils.js:129`). Une fois `.pdf` dans `SUPPORTED_EXTENSIONS`, les PDFs apparaissent automatiquement dans l'arborescence via l'API `list_directory`. Aucun changement UI nécessaire. + - [ ] **D2. Distinction visuelle** (optionnel) : Sous-titre léger sous le nom du fichier dans l'arborescence indiquant le nombre de pages (ex: « 12 pages ») pour différencier rapidement les PDF des MD. Donnée disponible via l'API `pdf/info`. + - [ ] **D3. Drag & drop et upload** : Le mécanisme d'upload existant (`POST /api/file/{vault}/upload`) fonctionne déjà pour tout type de fichier. Vérifier que le MIME type `application/pdf` est correctement détecté et que le watcher réindexe automatiquement. + +##### E. Frontend — Viewer PDF (1 jour) + - [ ] **E1. Rendu inline natif** : Utiliser le visualiseur PDF intégré du navigateur via `