From ce23ab38f75712768bbfda9c904e0da7b0f94350 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Fri, 11 Sep 2026 14:07:56 -0400 Subject: [PATCH] docs: restructurer le suivi et unifier la methode de livraison - ROADMAP: ne garde que le travail a venir + index compact du complete (995 -> ~155 lignes); detail deplace vers docs/features/ et docs/archive/ - docs/features/: fiches detaillees #74, #75, #76, #77, #78, #79 - docs/archive/COMPLETED_v1-v2.md: detail des items courts livres - CHANGELOG: alignement sur les tags (2.0.0 date, 2.2.0/2.2.1 ajoutes, Unreleased = travail #79 post-2.2.1) - AGENTS.md + docs/DELIVERY_WORKFLOW.md: methode de livraison unique (Definition of Done) referencee par ROADMAP, CONTRIBUTING, ISSUES_TODOLIST --- AGENTS.md | 45 ++ CHANGELOG.md | 74 ++- context.md | 5 + docs/CONTRIBUTING.md | 4 + docs/DELIVERY_WORKFLOW.md | 129 +++++ docs/ISSUES_TODOLIST.md | 2 + docs/ROADMAP.md | 956 +++----------------------------- docs/archive/COMPLETED_v1-v2.md | 328 +++++++++++ docs/features/ai-tools-mcp.md | 61 ++ docs/features/bookslm.md | 94 ++++ docs/features/desktop-tauri.md | 87 +++ docs/features/excalidraw.md | 152 +++++ docs/features/pdf.md | 104 ++++ docs/features/split-view.md | 93 ++++ 14 files changed, 1251 insertions(+), 883 deletions(-) create mode 100644 AGENTS.md create mode 100644 docs/DELIVERY_WORKFLOW.md create mode 100644 docs/archive/COMPLETED_v1-v2.md create mode 100644 docs/features/ai-tools-mcp.md create mode 100644 docs/features/bookslm.md create mode 100644 docs/features/desktop-tauri.md create mode 100644 docs/features/excalidraw.md create mode 100644 docs/features/pdf.md create mode 100644 docs/features/split-view.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2202a44 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# AGENTS.md — Instructions obligatoires du dépôt ObsiGate + +> Ces instructions s'appliquent à **toute** intervention (humaine ou IA) sur ce dépôt. + +## Règle n°1 — Méthode de livraison unique + +Avant toute tâche (fonctionnalité, bug, refactor), **lire et appliquer** +[`docs/DELIVERY_WORKFLOW.md`](./docs/DELIVERY_WORKFLOW.md) (Definition of Done). +Aucune tâche n'est terminée avant que sa checklist soit complète **et le CI vert**. + +## Avant de commencer + +1. Lire [`docs/ROADMAP.md`](./docs/ROADMAP.md) (travail à venir + index) et + [`docs/ISSUES_TODOLIST.md`](./docs/ISSUES_TODOLIST.md) (bugs). +2. Identifier ou créer l'**ID stable** (`#NN` pour une feature, `BUG-NNN` pour un bug) + et passer son statut à « en cours » **avant** de coder. + +## À la fin de chaque tâche (obligatoire) + +- Ajouter/mettre à jour les **tests unitaires**. +- Vérifications locales vertes : `pytest`, `ruff`, `mypy`, tests frontend (`E2E` si UI). +- Mettre à jour la documentation requise : `CHANGELOG.md` (`[Unreleased]`), `docs/ROADMAP.md` + (statut + index), fiche `docs/features/` **ou** `docs/archive/`, `docs/ISSUES_TODOLIST.md`, + guide utilisateur i18n FR/EN + README si impact utilisateur. +- **Commit** conventionnel référençant l'ID, puis **push**. +- Vérifier le **CI Gitea vert** (jobs `lint`, `test`, `security`, `build`, `e2e`). + +## Cartographie documentaire + +| Sujet | Fichier | +|---|---| +| Méthode de livraison / DoD | `docs/DELIVERY_WORKFLOW.md` | +| Travail à venir + index | `docs/ROADMAP.md` | +| Historique des versions | `CHANGELOG.md` | +| Conception par feature | `docs/features/.md` | +| Archive du complété | `docs/archive/COMPLETED_v1-v2.md` | +| Bugs / TODO | `docs/ISSUES_TODOLIST.md` | +| Build & releases | `docs/DEVELOPMENT_AND_RELEASES.md` | +| Standards de code | `docs/CONTRIBUTING.md` | + +## Conventions + +- Commits : `type: description` — `feat`, `fix`, `perf`, `refactor`, `docs`, `style`, `chore`, `test`. +- **Ne jamais** committer de secrets, clés ou tokens. +- Réponses et documentation en **français** ; respecter le style du code existant. diff --git a/CHANGELOG.md b/CHANGELOG.md index 69565c7..d55d39d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,61 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Ajouté +- **#79 Assistant IA — Outils (function calling) & serveur MCP — Phase 0 (couche d'outils partagée)** — + nouveau paquet `backend/tools/` : `context.py` (`ToolContext`), `registry.py` (décorateur `@tool` + + schéma JSON), `schemas.py`, `service.py`, `audit.py` (journalisation JSONL des appels), façade + `backend/tools/api.py`. Services lecture/recherche livrés (`list_vaults`, `list_directory`, + `read_file`, `search_fulltext`, `list_tags`) ; permissions `check_vault_access` + `resolve_safe_path` + appliquées par outil. Tests : `tests/test_tools.py` (30 tests). + +- **#79 Phase B (partielle) — function calling in-app** — abstraction tool-calling + provider-agnostique `backend/ai_chat.py` (`chat_completion`, `ToolCall`, `LLMResponse`, OpenAI-compat + + Gemini) ; agent loop `backend/agent/loop.py` (boucle tool→résultat→tool, limite 10 itérations, + truncation) ; endpoint opt-in `POST /api/ai/bookslm/agent` (events SSE `tool`/`message`/`confirmation`) ; + fallback automatique en chat simple si le provider rejette les tools. Tests : `tests/test_agent_loop.py`, + `tests/test_ai_chat.py`, `tests/test_bookslm.py`. + +- **#79 G — Sélection fournisseur/modèle par défaut** — `ai_default_provider` + `ai_default_models` + persistés dans `data/config.json`, rechargement à chaud dans `backend/ai.py`, sélecteurs + « Fournisseur par défaut » / « Modèle par défaut » dans `#cfg-ai`, i18n FR/EN. + +### Corrigé + +- **#79 façade `backend/tools/api.py`** — gestion des namespace packages et du `__init__` ignoré + lors du chargement des modules d'outils. + +### Documentation + +- **Guide d'architecture IA** — `docs/AI_ARCHITECTURE_GUIDE.md` (architecture, catalogue d'outils, + sécurité, phases). +- **Refonte de la documentation** — la [Roadmap](./docs/ROADMAP.md) ne contient plus que le travail + à venir + un index compact ; les fonctionnalités livrées sont archivées dans + `docs/archive/COMPLETED_v1-v2.md` et documentées par feature dans `docs/features/`. + +- **Méthode de livraison unifiée** — nouveau [`docs/DELIVERY_WORKFLOW.md`](./docs/DELIVERY_WORKFLOW.md) + (Definition of Done : tests, documentation, commit, push, CI) et [`AGENTS.md`](./AGENTS.md) + (instructions obligatoires lues à chaque session). Référencés depuis la Roadmap, CONTRIBUTING + et ISSUES_TODOLIST. + +--- + +## [2.2.1] — 2026-09-11 + +### Ajouté + +- **Mobile — assistant AI central** — bouton assistant dans la barre du bas (commit 50b823e). + +### Corrigé + +- **Version affichée dans l'UI** — affichait `2.1.0` au lieu de `2.2.0` (commit 84bda90). +- **Mobile** — boîte d'édition de l'assistant relevée au-dessus de la barre du bas (commit f307ecb). + +--- + +## [2.2.0] — 2026-09-11 + +### Ajouté + - **Assistant AI contextuel (sidebar)** — l'assistant s'adapte désormais au contexte d'ouverture : - **Répertoire** (`directory`) : ouvert depuis le menu contextuel d'un dossier — le contexte est le dossier et son contenu (mode historique BooksLM). @@ -73,6 +128,23 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - CI : job lint ajoute `node plugins.test.mjs` aux tests JSDOM. - Docs : `docs/PLUGINS.md` (manifest, hooks, permissions, modèle de sécurité, API, guide). +- **#78 Excalidraw — finitions** : décompression lz-string du format plugin Obsidian, + création depuis la modale et le menu contextuel, extraction du texte pour la recherche + (B5), support `.excalidraw.md` (commit ac16fc1 + correctifs associés). + +- **#67 Notifications web (Push API)** : `backend/push.py`, `POST /api/push/subscribe`, + clés VAPID dans `config.json`, toggle par vault, payload (fichier/vault/action) et + ouverture du fichier au clic (commit ac16fc1). + +- **#68 Health check enrichi** : `GET /api/health/detailed` (admin-gated) exposant index, + mémoire (RSS/heap), uptime, connexions SSE, backups et espace disque (commit ac16fc1). + +- **#77 Desktop — jumplist vaults récents** dans le menu Démarrer (commit ac16fc1). + +- **#75 Split View — matrice E2E complète (37 tests)** : 16 tests `split-view.spec.js` + (raccourcis, drag & drop, persistance, navigation clavier) + 21 tests + `split-view-matrix.spec.js` couvrant 100% des cas d'ouverture/fermeture. + ### Documentation - **#78 Excalidraw — doc utilisateur finalisée** : `.excalidraw` / `.excalidraw.md` ajoutés aux @@ -167,7 +239,7 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- -## [2.0.0] — Unreleased +## [2.0.0] — 2026-08-25 > ObsiGate passe à **2.0.0** avec sa **version bureau native** et de nombreuses fonctionnalités > de productivité (branch main, 341 commits après le tag v1.8.0). diff --git a/context.md b/context.md index 0aa03d5..4add39a 100644 --- a/context.md +++ b/context.md @@ -1,5 +1,10 @@ # Code Context — ObsiGate Bug Investigation +> **⚠️ Note de workflow (obsolète pour cette investigation)** : la méthode de livraison +> obligatoire du dépôt est définie dans [`AGENTS.md`](./AGENTS.md) et +> [`docs/DELIVERY_WORKFLOW.md`](./docs/DELIVERY_WORKFLOW.md). À appliquer pour toute nouvelle +> tâche ; le contenu ci-dessous est un artefact d'investigation conservé pour historique. + ## Files Retrieved 1. `frontend/app.js` (lines 5585–5665) — `showWelcome()` rebuilds dashboard HTML with only bookmarks + recent sections 2. `frontend/index.html` (lines 360–406) — Initial dashboard DOM has all 4 sections: stats, bookmarks, conflicts, recent diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index b843e64..7fcd062 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -2,6 +2,10 @@ Merci de votre intérêt pour ObsiGate ! Ce guide décrit les standards de code et le workflow de développement. +> **⚠️ À lire avant toute contribution** : la méthode de livraison complète (Definition of Done : +> tests, docs, commit, push, CI) est définie dans [`DELIVERY_WORKFLOW.md`](./DELIVERY_WORKFLOW.md). +> Ce document-ci détaille uniquement les standards de code. + --- ## Prérequis diff --git a/docs/DELIVERY_WORKFLOW.md b/docs/DELIVERY_WORKFLOW.md new file mode 100644 index 0000000..52a56c8 --- /dev/null +++ b/docs/DELIVERY_WORKFLOW.md @@ -0,0 +1,129 @@ +# Méthode de livraison ObsiGate — Definition of Done + +> **Document de référence obligatoire.** À consulter au début de **chaque** tâche (fonctionnalité, +> correction de bug, refactor) et à respecter avant de considérer le travail terminé. +> Référencé par [`AGENTS.md`](../AGENTS.md), la [Roadmap](./ROADMAP.md) et [CONTRIBUTING.md](./CONTRIBUTING.md). + +--- + +## 1. Principe : une seule méthode, toujours la même + +Quelle que soit la demande, on suit le même cycle. Une tâche n'est **jamais** « terminée » tant que +la checklist du §5 n'est pas entièrement verte, **CI compris**. + +--- + +## 2. Où vit l'information (source unique de vérité) + +| Fichier | Rôle | Quand le mettre à jour | +|---|---|---| +| [`docs/ROADMAP.md`](./ROADMAP.md) | Travail **à venir** (🔵 En cours + ⚪ Backlog) + index du complété | Au début (statut) et à la fin (index) | +| [`CHANGELOG.md`](../CHANGELOG.md) | Historique officiel par version (Keep a Changelog) | À chaque livraison, dans `[Unreleased]` | +| [`docs/features/.md`](./features/) | **Conception / spec détaillée** d'une grosse feature | Quand l'item est livré | +| [`docs/archive/COMPLETED_v1-v2.md`](./archive/COMPLETED_v1-v2.md) | Détail des items courts livrés | Quand l'item est livré | +| [`docs/ISSUES_TODOLIST.md`](./ISSUES_TODOLIST.md) | Registre des **bugs / TODO** | À chaque bug (statut + correctif/commit) | +| [`docs/DEVELOPMENT_AND_RELEASES.md`](./DEVELOPMENT_AND_RELEASES.md) | Build local & publication des releases | Quand le process change | +| Guides `docs/*_GUIDE.md`, `docs/SPEC_*.md`, `docs/*_ARCHITECTURE*.md` | Conception technique détaillée | Si le domaine concerné change | +| Guide intégré (i18n `frontend/locales/fr.json` + `en.json`) | Guide **utilisateur** in-app | Si impact utilisateur | +| `README.md` / `README.fr.md` | Documentation grand public | Si impact utilisateur | +| Docstrings + `response_model` (`backend/`) + `backend/openapi_docs.py` | Documentation **API** | Si endpoint ajouté/modifié | + +**Règle d'or : un fait = un seul fichier.** On ne duplique jamais le détail entre roadmap et changelog. + +--- + +## 3. Choisir le bon registre + +- **Nouvelle fonctionnalité** → item `#NN` dans la Roadmap (`⚪ Backlog` → `🔵 En cours`). +- **Bug** → ligne dans `ISSUES_TODOLIST.md` (statut `🔴 ouvert` → `🟠 en cours` → `🟢 corrigé` → `✅ vérifié`). +- **ID stable** : un `#NN` ou `BUG-NNN` ne change jamais et n'est jamais réutilisé. C'est la clé de + jointure entre roadmap, changelog, issues et commits. + +--- + +## 4. Workflow standard (dans l'ordre) + +1. **Cadrer** — identifier l'ID (`#NN` / `BUG-NNN`), lire la Roadmap et `ISSUES_TODOLIST.md`, + passer le statut à `🔵 En cours` / `🟠 en cours` **avant** de coder. +2. **Implémenter** — respecter les standards de [CONTRIBUTING.md](./CONTRIBUTING.md) : typage, + docstrings, `response_model`, CSS variables, i18n FR/EN, sécurité `_resolve_safe_path()`. +3. **Tester** — écrire/étendre les **tests unitaires**. Un correctif sans test de non-régression + n'est pas terminé. +4. **Vérifier en local** — exécuter les commandes du §6. +5. **Documenter** — CHANGELOG `[Unreleased]`, Roadmap / ISSUES, fiche feature ou archive, guide + utilisateur + i18n FR/EN, README si besoin, OpenAPI si API. +6. **Commit** — message conventionnel (`feat:`, `fix:`…) référençant `#NN` / `BUG-NNN`. +7. **Push** puis **vérifier le CI vert** (jobs `lint`, `test`, `security`, `build`, `e2e`). +8. **Clôturer** — statut `🟢 corrigé` / index `✅` posé par l'IA ; l'utilisateur valide (`✅ vérifié`). + +--- + +## 5. Checklist « Definition of Done » + +### Code +- [ ] Comportement conforme à la demande +- [ ] Standards CONTRIBUTING respectés +- [ ] Aucun secret / clé committé +- [ ] i18n FR **et** EN si texte d'interface + +### Tests +- [ ] Tests unitaires ajoutés ou mis à jour (backend pytest / frontend Node) +- [ ] `pytest` vert en local +- [ ] `ruff` + `mypy` : 0 erreur +- [ ] Tests frontend verts (`validate-imports` + `unit` + JSDOM ciblés) +- [ ] E2E Playwright si flow UI critique touché + +### Documentation +- [ ] `CHANGELOG.md` → `[Unreleased]` (section Ajouté / Modifié / Corrigé) +- [ ] `docs/ROADMAP.md` → statut mis à jour + ligne dans l'index « Complété » +- [ ] Fiche `docs/features/.md` **ou** `docs/archive/` si l'item est livré +- [ ] `docs/ISSUES_TODOLIST.md` → statut + colonne « Correctif / Commit » (si bug) +- [ ] Guide d'utilisation (i18n) + README FR/EN si impact utilisateur +- [ ] OpenAPI / docstrings + `response_model` si API + +### Livraison +- [ ] Commit conventionnel référençant l'ID +- [ ] Push effectué +- [ ] CI vert : `lint` → `test` → `security` → `build` → `e2e` + +--- + +## 6. Commandes de vérification locale + +```powershell +# Backend +.\.venv\Scripts\python.exe -m pytest tests/ +.\.venv\Scripts\python.exe -m ruff check backend/ +.\.venv\Scripts\python.exe -m mypy backend/ --ignore-missing-imports + +# Frontend +node tests/frontend/validate-imports.mjs +node tests/frontend/unit.test.mjs +# tests JSDOM ciblés, ex : +node tests/frontend/pane-manager.test.mjs + +# E2E (si UI) +npx playwright test +``` + +> Les mêmes vérifications tournent dans le CI Gitea (`.gitea/workflows/ci.yml`) : jobs +> `lint`, `test`, `security`, `build`, `e2e`. + +--- + +## 7. Versionnement & release + +- **SemVer** `MAJOR.MINOR.PATCH` ; tags Git `vX.Y.Z` ; source de vérité = `CHANGELOG.md`. +- Lors d'une publication : déplacer `[Unreleased]` vers `[X.Y.Z] — date`, tagger, builder et + publier (procédure détaillée dans [DEVELOPMENT_AND_RELEASES.md](./DEVELOPMENT_AND_RELEASES.md)). +- **Ne jamais** réécrire une version déjà publiée dans le CHANGELOG. + +--- + +## 8. À ne jamais faire + +- Marquer une tâche terminée sans tests verts ni CI vert. +- Committer sans mettre à jour `CHANGELOG.md` **et** le registre concerné (Roadmap / Issues). +- Dupliquer le détail entre Roadmap et CHANGELOG. +- Réutiliser un ID `#NN` / `BUG-NNN`. +- Pousser des secrets, clés ou tokens. diff --git a/docs/ISSUES_TODOLIST.md b/docs/ISSUES_TODOLIST.md index 14d7643..cab766f 100644 --- a/docs/ISSUES_TODOLIST.md +++ b/docs/ISSUES_TODOLIST.md @@ -2,6 +2,8 @@ > Ce document utilise un **format tabulaire simple et rigoureux** conçu pour être > maintenu à la fois par un humain (éditeur texte) et par un agent IA. Les règles > exactes sont définies dans la section **« Comment fonctionne ce document »**. +> La procédure de livraison obligatoire (tests, docs, commit, push, CI) est définie +> dans [`DELIVERY_WORKFLOW.md`](./DELIVERY_WORKFLOW.md). # 🐛 ObsiGate — Suivi des Bugs / TODO de Correction diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index c3e3444..be4b929 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,8 +1,13 @@ # ObsiGate — Roadmap -> **Version :** 2.2.1-dev | **Dernière mise à jour :** 2026-09-10 -> Revue de cohérence roadmap ↔ code : cases cochées selon l'état réel vérifié dans le dépôt (#78 docs H1-H3, #67 push, #68 health, #74/#61 optionnels non retenus). -> Voir aussi [CHANGELOG.md](./CHANGELOG.md), [AUDIT_TECHNIQUE.md](./docs/AUDIT_TECHNIQUE_2026-05-27.md) +> **Version :** 2.2.1 | **Dernière mise à jour :** 2026-09-11 +> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact +> vers les fonctionnalités livrées. +> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)** +> - Historique des versions : [CHANGELOG.md](../CHANGELOG.md) +> - Détail des fonctionnalités livrées : [docs/archive/COMPLETED_v1-v2.md](./archive/COMPLETED_v1-v2.md) +> - Fiches détaillées par feature : [docs/features/](./features/) +> - Bugs / TODO : [ISSUES_TODOLIST.md](./ISSUES_TODOLIST.md) --- @@ -17,168 +22,44 @@ | 🟡 Impact | Utile / Attendu | | 🟢 Impact | Nice-to-have / Confort | ---- - -## ✅ Complété (v1.0.0 → v2.0.0-dev) - -### Fondations (v1.0.0 → v1.4.0) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 1 | FastAPI backend — CRUD fichiers, vaults, recherche full-text | 5j | 🔴 | -| 2 | Moteur TF-IDF + stemming français (`snowballstemmer`) | 2j | 🔴 | -| 3 | Watchdog — indexation temps réel avec debounce | 1j | 🟡 | -| 4 | Interface SPA vanilla JS — sidebar, viewer, éditeur CodeMirror | 8j | 🔴 | -| 5 | Sécurité : JWT + Argon2id, rate limiting, audit log, CSP headers | 3j | 🔴 | -| 6 | Protection path traversal, utilisateur non-root Docker | 0.5j | 🔴 | -| 7 | Compression GZip SSE-safe, Cache-Control immutable | 0.5j | 🟢 | -| 8 | PWA : manifest, service worker, mode standalone | 1j | 🟡 | - -### UX & Productivité (v1.5.0 → v1.6.0) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 9 | Publication publique de documents (lien partageable, token) | 1j | 🟡 | -| 10 | Webhooks HTTP avec signature HMAC-SHA256 | 1j | 🟡 | -| 11 | Dashboard statistiques (fichiers, tags, taille, vaults) | 0.5j | 🟡 | -| 12 | Gestion des conflits Syncthing | 0.5j | 🟢 | -| 13 | Index inversé incrémental (hook pattern) | 1j | 🟡 | -| 14 | Backlinks panel dans le viewer | 0.5j | 🟡 | -| 15 | Fichiers non-supportés → UI download | 0.3j | 🟢 | -| 16 | Redaction de secrets (`secret_redactor.py`) | 0.5j | 🟡 | -| 17 | Backup automatique avant écriture, restauration | 1j | 🟡 | -| 18 | Vue graphe — Barnes-Hut, focus, plein écran, export PNG | 3j | 🟡 | -| 19 | Header flat design, sticky panels, navigation historique ← → ↑ | 1j | 🟢 | -| 20 | Ctrl+survol → aperçu contenu formaté | 0.5j | 🟢 | - -### Architecture (v1.5.1) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 21 | Split `app.js` (8 875 lignes) → 16 modules ES | 3j | 🔴 | -| 22 | Validateur imports/exports CI + tests unitaires frontend Node.js | 0.5j | 🟡 | - -### CI/CD & Qualité (v1.6.0) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 23 | Pipeline Gitea Actions : lint → test → security → build | 1j | 🔴 | -| 24 | Ruff (0 erreur) + Mypy (0 erreur) + Bandit SAST + Pip-audit | 0.5j | 🟡 | -| 25 | Pytest : 285 tests, 63% coverage | 3j | 🔴 | - -### AI Editor (v1.7.0) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 26 | Toolbar : Edit, Tone, Translate, Generate, Rewrite, Toolbox | 2j | 🟡 | -| 27 | Multi-provider : DeepSeek, OpenRouter, Gemini | 1j | 🟡 | -| 28 | 16 endpoints REST `/api/ai/{action}` + backend `ai.py` / `ai_routes.py` | 2j | 🟡 | -| 29 | Auto-save silencieux (2s debounce), loading toasts | 0.5j | 🟢 | - -### Fonctionnalités avancées (v1.8.0 → v1.9.0) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 30 | Export PDF via WeasyPrint (endpoint API + lien share public) | 1j | 🟡 | -| 31 | Palette de commandes `Ctrl+Alt+Space` | 1j | 🟡 | -| 32 | Barre d'outils mobile + palette fichiers/commandes 📱 | 1j | 🟡 | -| 33 | Drag & drop de fichiers | 1j | 🟡 | -| 34 | Filtres recherche avancés : `created:`, `modified:`, `size:` | 0.5j | 🟡 | -| 35 | Fichiers récents par vault | 0.5j | 🟡 | -| 36 | Indicateur AI Actif (header + toast) | 0.3j | 🟢 | -| 37 | Page d'accueil de vault (liste récursive par date, style recherche) | 0.5j | 🟡 | -| 38 | Indexation non-bloquante (background thread) | 0.5j | 🟡 | -| 39 | Git tags semver (v1.8.0, v1.9.0) | 0.2j | 🟢 | - -### Gestion des Backups (v1.9.0) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 40 | Diff viewer : unifié + côte à côte, restauration depuis backup | 1j | 🟡 | -| 41 | Gestionnaire de backups : page complète, filtre, suppression, purge | 1.5j | 🟡 | -| 42 | Purge par vault avec confirmation, preview contenu (100 Ko) | 0.5j | 🟡 | -| 43 | Compression gzip des vieux backups (niveau 6) | 0.5j | 🟢 | -| 44 | Backup automatique périodique (`POST /api/backups/auto`) | 1j | 🟡 | -| 45 | Restauration depuis le gestionnaire (extraction timestamp, confirmation) | 0.5j | 🟡 | -| 46 | Auto-nettoyage : `max_backups_per_file` (défaut 10) | 0.5j | 🟡 | - -### Mermaid.js (v2.0.0-dev) - -| # | Feature | Effort | Impact | -|---|---|---|---| -| 47 | CDN mermaid@11, `securityLevel: strict`, `startOnLoad: false` | 0.5j | 🟡 | -| 48 | `renderMermaidBlocks()` — parse, rendu SVG, bloc d'erreur stylisé | 1j | 🟡 | -| 49 | 22 templates (flowchart → sankey-beta) | 0.5j | 🟡 | -| 50 | Live preview dans l'éditeur (debounce 500ms, panneau `#mermaid-live-preview`) | 1j | 🟡 | -| 51 | Thème dark/light synchronisé, export SVG + PNG | 1j | 🟡 | -| 52 | Rendu inline dans le viewer Markdown | 0.5j | 🟡 | -| 53 | Zoom molette + drag + boutons +/- | 0.5j | 🟢 | -| 54 | Mode plein écran avec header bar (icônes zoom, copy, download, close) | 0.5j | 🟢 | -| 55 | Focus mode — clic diagramme → panneau latéral 480px | 0.3j | 🟡 | -| 56 | Pré-processeur Obsidian (`[[liens]]`, `![[img]]`, `==highlight==`) | 0.3j | 🟡 | -| 57 | Header bar : type de diagramme + toggle Code/Preview + copy SVG + download PNG | 1j | 🟡 | +> **Convention :** chaque item porte un **ID stable `#NN`** (clé de jointure entre roadmap, +> changelog et commits). Une fonctionnalité livrée sort de ce fichier et entre dans l'index +> « Complété » ci-dessous, avec son détail dans `docs/features/` ou l'archive. --- -## ✅ Complété (suite — v1.7 → v2.1) +## 🔵 En cours -### 58. Tests E2E Playwright ✅ FAIT -- **Effort :** 2-3 jours | **Impact :** 🔴 -- **Description :** Tests navigateur automatisés pour les flows critiques : login, navigation vault, recherche full-text, ouverture/édition/sauvegarde de fichier, rendu Mermaid, export PDF. -- **Implémentation :** 44 tests Playwright (chromium-desktop) + 12 tests mobile dans `tests/e2e/obsigate.spec.ts`. Intégré au CI Gitea (job `e2e` après `build`). -- **Sous-tâches :** - - [x] Installation Playwright + config (`playwright.config.ts`) - - [x] Fixtures : vault de test (utilise les vaults Docker existants) - - [x] Test : dashboard → stats, tabs, Quick Help, sidebar - - [x] Test : recherche full-text → résultats, snippets, tri pertinence/date - - [x] Test : ouverture fichier → viewer Markdown, métadonnées - - [x] Test : éditeur → Forge, basic modal, Ctrl+S - - [x] Test : rendu Mermaid dans le viewer + preview Forge - - [x] Test : export PDF → bouton présent - - [x] Test : mode sombre → toggle, persistence localStorage - - [x] Test : responsive mobile → layout, recherche, barre flottante - - [x] Test : barre flottante résultats → compteur, nav, toggles Aa/wd - - [x] Test : sauvegardes → filtres Tous/Recherches/Répertoires - - [x] Test : raccourcis clavier → Ctrl+K, /, Escape - - [x] Test : menu contextuel répertoire - - [x] Intégration CI : job `e2e` dans `.gitea/workflows/ci.yml` +### 77. Application Desktop native — Tauri (Windows / Linux / macOS) + +- **Effort :** 8-12 jours | **Impact :** 🟡 | **Framework :** Tauri v2 (Rust + Webview) +- **Statut :** livré (A→F) — projet Tauri, backend Python embarqué, fonctionnalités natives, build CI, UX, 16 tests Rust. Détail complet : [features/desktop-tauri.md](./features/desktop-tauri.md) +- **Reste à faire :** + - [ ] Signature de code Windows (optionnel mais recommandé) + - [ ] Wizard « Choisissez votre vault » au 1er lancement (optionnel) + - [ ] Jumplist vaults récents dans le menu Démarrer (optionnel) + - [ ] 6 tests E2E **manuels** : installation, tray icon, notifications natives, association `.md`, auto-update, désinstallation + +### 79. Assistant IA — Outils (function calling) & serveur MCP + +- **Effort :** 10-15 jours | **Impact :** 🟡 +- **Statut :** 🔵 Phase 0 + B1/B2/B3/B7 + G livrés (2026-09-11). Détail complet : [features/ai-tools-mcp.md](./features/ai-tools-mcp.md) +- **Description :** Transformer l'assistant BooksLM en agent (lire, chercher, lister, ouvrir, modifier) via **function calling natif**, puis exposer ObsiGate à des **clients MCP externes** (Claude Desktop, Cursor…). Les deux fronts consomment une **couche d'outils partagée**. +- **Reste à faire :** + - [ ] **A2.** Extraire la logique métier des routes `backend/main.py` en services réutilisables (partiel) + - [ ] **B4.** SSE réellement streaming ; **B5.** confirmations UI (carte Apply, diff) ; **B6.** outils navigation `open_file`/`reveal_in_tree` + - [ ] **C.** Catalogue lecture & recherche (vaults, read, backlinks, backups, search, tags) + - [ ] **D.** Catalogue mutations (create/edit/append/rename/move/delete) + confirmations two-step + - [ ] **E.** Serveur MCP (Streamable HTTP `/mcp`, auth Bearer JWT, `propose`/`apply`, toggle par vault) + - [ ] **F.** Durcissement (rate limiting, redaction secrets, OpenAPI + guide MCP, E2E) +- **Documentation :** [AI_ARCHITECTURE_GUIDE.md](./AI_ARCHITECTURE_GUIDE.md) (architecture, catalogue d'outils, sécurité, phases). --- ## ⚪ Backlog — Priorité 3 (P3) -### 59. Mode hors-ligne PWA complet — ✅ TERMINÉ -- **Effort :** 3-4 jours | **Impact :** 🟡 -- **Description :** Service worker avancé avec IndexedDB pour permettre la navigation et la recherche en mode hors-ligne, avec file de synchronisation au retour réseau. -- **Implémentation :** `offline-db.js` (377 lignes) + `offline.js` (209 lignes). IndexedDB 3 stores (files, content, pending). Badge hors-ligne dans le header. Modale résolution de conflits. -- **Sous-tâches :** - - [x] IndexedDB : stockage local de l'index des fichiers (paths, titles, tags) - - [x] Moteur de recherche offline via IndexedDB (cursor + filtre) - - [x] Cache des fichiers markdown récemment ouverts (derniers 50, prune) - - [x] Stratégie de cache : Network First avec fallback IndexedDB - - [x] File de synchronisation : modifications offline → appliquées au retour réseau - - [x] UI indicateur : badge « Hors-ligne » + compteur de modifications en attente - - [x] Gestion des conflits : détection et résolution manuelle (choix version locale vs serveur) - -### 61. Plugins système — Extensions utilisateur ✅ -- **Effort :** 4-5 jours | **Impact :** 🟢 | **Statut :** ✅ Livré (backend + frontend + tests + docs) -- **Description :** Système de plugins permettant aux utilisateurs d'étendre ObsiGate avec des renderers personnalisés, des opérateurs de recherche, et des hooks d'UI. Inspiré du modèle de plugins Obsidian. -- **Sous-tâches :** - - [x] Spécification du format de plugin : `plugin.json` (name, version, hooks, permissions) - - [x] API de hooks : `onFileRender`, `onSearchFilter`, `onEditorAction`, `onSidebarItem`, `onFileCreate`, `onFileDelete`, `onVaultMount` - - [x] Sandbox d'exécution : Web Worker isolé pour le code plugin (blob URL, postMessage structuré, CSP sans importScripts) - - [x] UI : page « Plugins » dans les paramètres (installer, activer/désactiver, désinstaller, template, viewer) - - [ ] Distribution : dépôt de plugins communautaire (fichier JSON index) — ⚪ NON RETENU (backlog) - - [x] Hot-reload : activation/désactivation sans rechargement de page (marker `.disabled`) - - [x] Sécurité : manifest de permissions, validation path-traversal, CSP restrictif -- **Livré :** - - Backend `backend/plugins.py` — validation manifest (name regex, semver, hooks/permissions autorisés), stockage par vault `/.obsigate-plugins/`, lifecycle complet, validation ZIP (path traversal, limite 100 fichiers, 500KB/fichier), 9 endpoints `/api/plugins/*` (admin-gated pour install/uninstall/enable/disable), template API. - - Frontend `frontend/js/plugins.js` — PluginManager, sandbox Web Worker (code via blob URL, protocole postMessage structuré), UI Settings > Plugins, hooks dispatch (`executeHook`/`onFileRender`/`onSearchFilter`/…). - - Tests : `tests/test_plugins.py` (44) + `tests/frontend/plugins.test.mjs` (21) — validation, lifecycle, ZIP/dir sécurité, protocole sandbox, isolation DOM/CSP. - - Docs : `docs/PLUGINS.md`. -- **En backlog (non retenu) :** dépôt communautaire (index JSON), signature de code des plugins. - ### 62. Collaboration temps réel — Édition simultanée + - **Effort :** 5-7 jours | **Impact :** 🟢 - **Description :** Permettre à plusieurs utilisateurs d'éditer le même document markdown en même temps, comme Google Docs. Chaque personne voit en temps réel ce que les autres tapent, avec leur curseur affiché en couleur. - **WebSocket** : connexion persistante bidirectionnelle entre le navigateur et le serveur. Contrairement à HTTP où le client doit constamment demander « y a-t-il du nouveau ? » (polling), le WebSocket permet au serveur de pousser les changements instantanément. Une room WebSocket est créée par fichier ouvert — tous les utilisateurs qui éditent le même fichier rejoignent la même room. @@ -197,596 +78,12 @@ - [ ] Gestion des déconnexions : reconnexion automatique, merge state au retour - [ ] Tests de charge : 5+ utilisateurs simultanés sur le même fichier -### 63. Internationalisation (i18n) — Multilingue — ✅ TERMINÉ -- **Effort :** 2-3 jours | **Impact :** 🟡 | **Statut :** ✅ Terminé -- **Description :** Support de l'anglais et du français via un système de clés de traduction. -- **Sous-tâches :** - - [x] Extraction des chaînes : ~1200 clés UI extraites - - [x] Format : JSON `fr.json` + `en.json` dans `frontend/locales/` → 1206 clés parfaitement synchronisées - - [x] Fonction `t(key)` → `frontend/js/i18n.js` avec `_applyDOM()`, `data-i18n`, `data-i18n-attr`, `data-i18n-placeholder`, `data-i18n-html`, support des templates `{var}` - - [x] Sélecteur de langue dans les paramètres (persisté localStorage `obsigate-lang`) - - [x] Traduction des messages backend → les toast/showToast sont maintenant i18n dans tous les fichiers JS - - [x] Documentation multilingue → `README.md` + `README.fr.md` - - [x] Nettoyage des clés inutilisées → locales nettoyées - - [x] Tous les fichiers JS utilisent `t()` → plus de texte FR en dur (ai.js, sync.js, graph.js, autocomplete.js) - - [x] Interface principale : dashboard, sidebar, editor, search, settings → EN/FR complet - - [x] Guide d'utilisation : 18 sections (Intro → Astuces) → tous les paragraphes traduits - - [x] Thèmes, palette de commandes, raccourcis, webhooks → EN/FR complet - - [x] Messages système : toasts, statuts, événements → EN/FR complet - -### 64. MFA — Authentification multi-facteurs — ✅ TERMINÉ (TOTP + WebAuthn + recovery codes) -- **Effort :** 2 jours (réalisé) | **Impact :** 🟡 -- **Description :** Ajout d'un second facteur d'authentification obligatoire pour les comptes administrateur. Deux méthodes sont proposées : - - **TOTP** (Time-based One-Time Password) : l'utilisateur scanne un QR code avec son app d'authentification (Google Authenticator, Authy, Bitwarden) qui génère un code à 6 chiffres renouvelé toutes les 30 secondes. Au login, après avoir saisi son mot de passe, l'utilisateur doit entrer le code affiché sur son téléphone. Même si le mot de passe est volé, le compte reste protégé car l'attaquant n'a pas le téléphone. - - **WebAuthn** (clés de sécurité physiques) : l'utilisateur enregistre une clé USB (YubiKey, SoloKey) ou utilise la biométrie de son appareil (empreinte digitale, Face ID, Windows Hello). Au login, le navigateur demande de toucher la clé physique ou de scanner le doigt. C'est le niveau de sécurité le plus élevé — résistant au phishing car la clé vérifie le domaine du site avant de répondre. - - **Codes de secours** : 8 codes à usage unique imprimables, à conserver en lieu sûr, qui permettent de se connecter même si on perd son téléphone ou sa clé. Chaque code ne fonctionne qu'une seule fois. -- **Pourquoi c'est important :** Le vol de mot de passe est la cause #1 de brèches de sécurité. Avec un vault Obsidian contenant des notes personnelles, projets sensibles, secrets et tokens API, l'authentification par simple mot de passe n'est plus suffisante. Le MFA empêche 99.9% des attaques de prise de compte automatisées (source : Microsoft Security). -- **Sous-tâches :** - - [x] TOTP : génération de secret, QR code, vérification code 6 chiffres - - [x] WebAuthn : enregistrement de clé, assertion, attestation (`backend/auth/webauthn_mfa.py`, lib `webauthn==2.6.0`, challenges in-memory TTL 180s à usage unique) — FAIT en 2026-09 (commit ab795ec) - - [x] UI : page « Sécurité du compte » avec activation/désactivation MFA + gestion des clés WebAuthn (liste, ajout, retrait) - - [x] Flow login : mot de passe → challenge TOTP OU WebAuthn selon `mfa_method` retourné par /login - - [x] Recovery codes : 8 codes de backup à usage unique (générés à l'activation, hachés SHA-256) - - [x] Stockage : `mfa_secret` + `webauthn_credentials[]` dans `users.json` - - [x] Tests : `tests/test_mfa.py` (29) + `tests/test_webauthn.py` (10, authentificateur virtuel CBOR/EC P-256) - -### 65. Thèmes personnalisés — CSS variables — ✅ TERMINÉ -- **Effort :** 1-2 jours (réalisé) | **Impact :** 🟢 -- **Description :** Exposition de variables CSS pour permettre aux utilisateurs de créer des thèmes personnalisés. Presets inclus : light, dark, high-contrast, sepia. -- **Sous-tâches :** - - [x] Audit des variables CSS existantes → 40+ variables - - [x] Presets: light, dark, high-contrast, sepia (générés dynamiquement) - - [x] UI : sélecteur de thème dans les paramètres (swatches grid) - - [x] Import/export de thème personnalisé (JSON) - - [x] Application dynamique via document.documentElement.style.setProperty - -### 66. Export multi-formats — ✅ TERMINÉ -- **Effort :** 1-2 jours (réalisé) | **Impact :** 🟢 -- **Description :** Export de notes individuelles ou de vaults entiers en HTML standalone, bundle Markdown (.zip), et ePub pour liseuses. -- **Sous-tâches :** - - [x] Export HTML standalone : CSS inliné, images en base64, navigation inter-fichiers - - [x] Export MD bundle : ZIP du vault avec structure préservée - - [x] Export ePub : conversion markdown → ePub (zipfile + mistune, 0 nouvelle dep) - - [x] UI : dropdown Export dans toolbar viewer (HTML / MD bundle / ePub) - - [x] Endpoints : `GET /api/export/html`, `GET /api/export/md-bundle`, `GET /api/export/epub` - -### 74. Support complet des documents PDF — ✅ TERMINÉ -- **Effort :** 4-5 jours | **Impact :** 🟡 | **Statut :** ✅ COMPLET (2026-09-07 — C3 + Range 206 + config G3 + indexation incrémentale, commit 7042307. Optionnels D2/E4/H2/I2 non retenus) -- **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. -- **Implémentation réelle (vérifiée 2026-09-07) :** - - **Bugs corrigés (2026-09) :** `api_pdf_stream` crashait en 500 (`NameError: current_user` jamais injecté) ; l'indexation incrémentale du watcher faisait `read_text()` sur les PDFs (garbage) ; Range/206 et `pdf/info` absents malgré le texte ci-dessous. - - `GET /api/file/{vault}/pdf/info` — métadonnées seules sans transférer le document (C3) - - Stream avec `Accept-Ranges` + 206 Partial Content (single range, suffix-range, 416) (C2) - - `OBSIGATE_PDF_MAX_SIZE_MB` (50) + `OBSIGATE_PDF_EXTRACT_TIMEOUT` (30s via thread-pool) (B4/G3) - - Backend `backend/pdf_reader.py` (existant) — extraction pypdf + pymupdf (fallback), métadonnées, TOC - - `backend/indexer.py` — `.pdf` dans SUPPORTED_EXTENSIONS, extraction dans `index_document()` - - `backend/main.py` — flag `is_pdf: True` retourné par `api_file_view`, endpoint `GET /api/file/{vault}/pdf/stream` avec support Range/206 - - `backend/search.py` — filtre `ext:pdf` (déjà implémenté avant cette PR) - - `frontend/js/viewer.js:451-480` — branche `if (data.is_pdf)` + iframe + toolbar + TOC + bouton download - - **Tests :** `tests/test_pdf.py` (26 tests verts) — text/metadata/TOC + indexation scan/incrémentale + filtre ext + stream 200/206/416 + /pdf/info + limite de taille - - **Bug fixé dans cette PR :** `PdfReader` NameError dans `pdf_reader.py` quand pymupdf est installé (la variable `PdfReader` n'était déclarée que dans la branche `except ImportError`) - - `backend/requirements-test.txt` (nouveau) — `reportlab` pour générer des PDFs de test -- **Sous-tâches :** - -##### A. Backend — Extraction de texte PDF (1-1.5 jour) - - [x] **A1. Dépendance** : `pypdf>=4.0` retenu dans requirements (pure Python, simplicité Docker) ; PyMuPDF (`fitz`) utilisé automatiquement en priorité s'il est importable — l'inverse du plan initial, fonctionnellement équivalent. - - [x] **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`). - - [x] **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) - - [x] **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). - - [x] **B2. Lecture PDF dans les DEUX chemins d'indexation** (`_scan_vault` + `_index_single_file_sync`, utilisé par le watcher) : détection `.pdf` → `extract_pdf_text()`. Fix 2026-09 : seul le scan complet gérait les PDFs, l'incrémental indexait du garbage. - - [x] **B3. Métadonnées PDF (adapté)** : titre PDF prioritaire sur le nom de fichier dans l'index ; `pages`/`author` exposés via `api_file_view` + `/pdf/info` (non stockés dans l'entrée d'index). - - [x] **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) - - [x] **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 - - [x] **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. - - [x] **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. - - [x] **C4. Endpoint download** : Déjà fonctionnel (`/api/file/{vault}/download`) — aucun changement nécessaire. - -##### D. Frontend — Arborescence de fichiers (0.5 jour) - - [x] **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** — ⚪ NON RETENU : 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`. - - [x] **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) - - [x] **E1. Rendu inline natif** : Utiliser le visualiseur PDF intégré du navigateur via `