Consolidate roadmap files into docs/ROADMAP.md
This commit is contained in:
-559
@@ -1,559 +0,0 @@
|
||||
# ObsiGate — Roadmap
|
||||
|
||||
> **Version :** 2.0.0-dev | **Dernière mise à jour :** 2026-06-05
|
||||
> Voir aussi [CHANGELOG.md](./CHANGELOG.md), [AUDIT_TECHNIQUE.md](./docs/AUDIT_TECHNIQUE_2026-05-27.md)
|
||||
|
||||
---
|
||||
|
||||
## Légende
|
||||
|
||||
| Icône | Signification |
|
||||
|---|---|
|
||||
| ✅ | Terminé |
|
||||
| 🔵 | En cours |
|
||||
| ⚪ | Prévu / Backlog |
|
||||
| 🔴 Impact | Critique / Bloquant |
|
||||
| 🟡 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 | 🟡 |
|
||||
|
||||
---
|
||||
|
||||
## 🔵 En cours (P1)
|
||||
|
||||
### 58. Tests E2E Playwright
|
||||
- **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.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Installation Playwright + config (`playwright.config.ts`)
|
||||
- [ ] Fixtures : vault de test avec 5-10 fichiers .md variés
|
||||
- [ ] Test : login admin → dashboard → liste vaults
|
||||
- [ ] Test : navigation sidebar → ouverture fichier → viewer Markdown
|
||||
- [ ] Test : recherche full-text → résultats triés par pertinence
|
||||
- [ ] Test : éditeur CodeMirror → modification → Ctrl+S → backup créé
|
||||
- [ ] Test : rendu Mermaid (flowchart + sequence) dans le viewer
|
||||
- [ ] Test : export PDF → téléchargement vérifié
|
||||
- [ ] Test : mode sombre → toggle → persistence localStorage
|
||||
- [ ] Test : responsive mobile → toolbar + sidebar repliée
|
||||
- [ ] Intégration CI : job `e2e` dans `.gitea/workflows/ci.yml`
|
||||
|
||||
---
|
||||
|
||||
## ⚪ Backlog — Priorité 3 (P3)
|
||||
|
||||
### 59. Mode hors-ligne PWA complet
|
||||
- **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.
|
||||
- **Sous-tâches :**
|
||||
- [ ] IndexedDB : stockage local de l'index des fichiers (paths, titles, tags)
|
||||
- [ ] Moteur de recherche offline via IndexedDB (cursor + filtre)
|
||||
- [ ] Cache des fichiers markdown récemment ouverts (derniers 50)
|
||||
- [ ] Stratégie de cache : Network First avec fallback IndexedDB
|
||||
- [ ] File de synchronisation : modifications offline → appliquées au retour réseau
|
||||
- [ ] UI indicateur : badge « Hors-ligne » + compteur de modifications en attente
|
||||
- [ ] Gestion des conflits : détection et résolution manuelle (choix version locale vs serveur)
|
||||
|
||||
### 60. OAuth2 / OIDC — Authentification SSO
|
||||
- **Effort :** 2-3 jours | **Impact :** 🟡
|
||||
- **Description :** Support de fournisseurs OAuth2/OIDC externes pour permettre l'authentification via Google, GitHub, ou tout provider compatible. Complément à l'auth JWT existante (pas de remplacement).
|
||||
- **Sous-tâches :**
|
||||
- [ ] Configuration par provider : `OBSIGATE_OAUTH_GOOGLE_CLIENT_ID`, `OBSIGATE_OAUTH_GITHUB_CLIENT_ID`, etc.
|
||||
- [ ] Endpoint `GET /api/auth/oauth/login?provider=google` → redirection
|
||||
- [ ] Endpoint `GET /api/auth/oauth/callback` → échange code → token OIDC → création/liaison compte local
|
||||
- [ ] Mapping roles : config `OBSIGATE_OAUTH_DEFAULT_ROLE` (défaut `user`)
|
||||
- [ ] UI : boutons « Se connecter avec Google / GitHub » sur la page login
|
||||
- [ ] Sécurité : state parameter anti-CSRF, PKCE, nonce validation
|
||||
- [ ] Stockage : liaison `oauth_provider` + `oauth_sub` dans `users.json`
|
||||
|
||||
### 61. Plugins système — Extensions utilisateur
|
||||
- **Effort :** 4-5 jours | **Impact :** 🟢
|
||||
- **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 :**
|
||||
- [ ] Spécification du format de plugin : `plugin.json` (name, version, hooks, permissions)
|
||||
- [ ] API de hooks : `onFileRender`, `onSearchFilter`, `onEditorAction`, `onSidebarItem`
|
||||
- [ ] Sandbox d'exécution : Web Worker isolé pour le code plugin
|
||||
- [ ] UI : page « Plugins » dans les paramètres (installer, activer/désactiver, désinstaller)
|
||||
- [ ] Distribution : dépôt de plugins communautaire (fichier JSON index)
|
||||
- [ ] Hot-reload : activation/désactivation sans rechargement de page
|
||||
- [ ] Sécurité : manifest de permissions, validation de signature, CSP restrictif
|
||||
|
||||
### 62. Collaboration temps réel — Édition simultanée
|
||||
- **Effort :** 5-7 jours | **Impact :** 🟢
|
||||
- **Description :** Édition collaborative de fichiers markdown via WebSocket + CRDT (Yjs). Plusieurs utilisateurs peuvent éditer le même fichier simultanément avec résolution automatique des conflits.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Serveur WebSocket : endpoint `/ws/collab/{vault}/{path}` avec gestion des rooms
|
||||
- [ ] Intégration Yjs : `Y.Doc` partagé, `Y.Text` pour le contenu markdown
|
||||
- [ ] Awareness : curseurs colorés par utilisateur, sélections visibles
|
||||
- [ ] Synchro backend : persistance périodique du document (debounce 2s)
|
||||
- [ ] Gestion des droits : vérification `check_vault_access` par connexion WS
|
||||
- [ ] UI : indicateur de présence (avatars dans la barre d'outils éditeur)
|
||||
- [ ] UI : curseurs distants dans CodeMirror (extension collaborative)
|
||||
- [ ] 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
|
||||
- **Effort :** 2-3 jours | **Impact :** 🟡
|
||||
- **Description :** Support de l'anglais et du français via un système de clés de traduction. L'interface est actuellement en français uniquement. L'anglais est nécessaire pour une adoption plus large.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Extraction des chaînes : inventaire de toutes les chaînes UI (~300)
|
||||
- [ ] Format : JSON `fr.json` + `en.json` dans `frontend/locales/`
|
||||
- [ ] Fonction `t(key, fallback)` → détection `navigator.language`
|
||||
- [ ] Sélecteur de langue dans les paramètres (persisté localStorage)
|
||||
- [ ] Traduction des messages backend (erreurs API, toasts)
|
||||
- [ ] Documentation multilingue (README.fr.md, README.md)
|
||||
|
||||
### 64. MFA — Authentification multi-facteurs
|
||||
- **Effort :** 2 jours | **Impact :** 🟡
|
||||
- **Description :** Ajout de TOTP (Time-based One-Time Password) et WebAuthn (clés de sécurité) comme second facteur d'authentification pour les comptes admin.
|
||||
- **Sous-tâches :**
|
||||
- [ ] TOTP : génération de secret, QR code, vérification code 6 chiffres
|
||||
- [ ] WebAuthn : enregistrement de clé, assertion, attestation
|
||||
- [ ] UI : page « Sécurité du compte » avec activation/désactivation MFA
|
||||
- [ ] Flow login : mot de passe → challenge TOTP si activé
|
||||
- [ ] Recovery codes : 8 codes de backup à usage unique
|
||||
- [ ] Stockage : `totp_secret` + `webauthn_credential_id` dans `users.json`
|
||||
|
||||
### 65. Thèmes personnalisés — CSS variables
|
||||
- **Effort :** 1-2 jours | **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 :**
|
||||
- [ ] Audit des variables CSS existantes → liste des 30+ variables
|
||||
- [ ] Fichier `themes.json` avec presets (light, dark, high-contrast, sepia)
|
||||
- [ ] UI : sélecteur de thème dans les paramètres (aperçu live)
|
||||
- [ ] Import/export de thème personnalisé (JSON)
|
||||
- [ ] Application dynamique sans rechargement (`document.documentElement.style.setProperty`)
|
||||
|
||||
### 66. Export multi-formats
|
||||
- **Effort :** 1-2 jours | **Impact :** 🟢
|
||||
- **Description :** Export de notes individuelles ou de vaults entiers en HTML standalone, bundle Markdown (.zip), et ePub pour liseuses.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Export HTML standalone : CSS inliné, images en base64, navigation inter-fichiers
|
||||
- [ ] Export MD bundle : ZIP du vault avec structure préservée
|
||||
- [ ] Export ePub : conversion markdown → ePub via `markdown` + `ebooklib`
|
||||
- [ ] 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 `<iframe>` pointant sur `/api/file/{vault}/pdf/stream?path=...`. Approche optimale :
|
||||
- Zéro dépendance JS supplémentaire
|
||||
- Rendu identique à Chrome/Firefox/Safari natif
|
||||
- Support natif du zoom, recherche dans le document, navigation par pages, rotation
|
||||
- L'iframe s'adapte en hauteur (`height: 100%` du content-area)
|
||||
- [ ] **E2. Détection dans le viewer** : Dans `frontend/js/viewer.js`, fonction `renderFileContent()` — ajouter une branche après la détection `data.unsupported` :
|
||||
- Si `data.is_pdf === true` → render l'iframe PDF au lieu du viewer markdown
|
||||
- Si le navigateur ne supporte pas le rendu PDF inline → fallback sur l'UI « binaire » avec bouton download + bouton « Ouvrir dans un nouvel onglet »
|
||||
- [ ] **E3. Barre d'outils PDF** : Dans la barre d'outils du viewer (celle qui a déjà les boutons Copier, Source, .md, PDF, Éditer, pop-out), pour les fichiers PDF :
|
||||
- Remplacer « Copier » / « Source » / « Éditer » par des actions spécifiques PDF
|
||||
- Bouton « Télécharger » (.pdf) — déjà existant, fonctionne
|
||||
- Bouton « Plein écran » — ouvre le PDF dans un nouvel onglet en plein écran
|
||||
- Badge « N pages » indiquant le nombre de pages
|
||||
- Bouton « pop-out » — gardé, ouvre le viewer PDF dans une popup séparée
|
||||
- [ ] **E4. Thème** : L'iframe PDF est en dehors du DOM applicatif donc pas affecté par le thème dark/light. Ajouter un message discret « Le PDF s'affiche avec le thème de votre navigateur » si `_currentTheme === 'dark'` (les PDFs en fond blanc dans un thème sombre peuvent surprendre).
|
||||
- [ ] **E5. Responsive** : L'iframe s'adapte à la largeur du content-area. En mode mobile, hauteur ajustée à la viewport. La toolbar mobile existante fonctionne avec les actions PDF.
|
||||
|
||||
##### F. Frontend — Recherche (0.5 jour)
|
||||
- [ ] **F1. Résultats de recherche** : Les PDFs apparaissent dans les résultats via le TF-IDF existant (le texte extrait est indexé). Ajouter un badge visuel « PDF » à côté du titre dans les résultats de recherche pour distinguer les PDFs des MD — utiliser l'icône `file-text`.
|
||||
- [ ] **F2. Snippets de recherche** : Les extraits de contexte montrent le texte extrait du PDF avec surlignage des termes recherchés — fonctionnement identique aux MD via le mécanisme de snippet existant dans `search.py`.
|
||||
- [ ] **F3. Filtres de recherche avancés** : Ajouter `ext:pdf` comme filtre pour limiter la recherche aux PDFs uniquement (complément aux filtres `created:`, `modified:`, `size:` déjà prévus #34).
|
||||
|
||||
##### G. Docker & Dépendances (0.5 jour)
|
||||
- [ ] **G1. requirements.txt** : Ajouter `pymupdf>=1.24.0` (sinon `pypdf>=4.0` en fallback).
|
||||
- [ ] **G2. Dockerfile** : Vérifier que l'image `python:3.11-slim` dispose des libs système nécessaires pour pymupdf. Si besoin, ajouter `libmupdf-dev` ou utiliser `pypdf` (pure Python) pour éviter la complexité. Recommandation : pypdf pour la simplicité Docker, pymupdf en option pour la performance.
|
||||
- [ ] **G3. Configuration** : Ajouter `OBSIGATE_PDF_MAX_SIZE_MB` (défaut 50) pour limiter la taille des PDFs indexés et `OBSIGATE_PDF_EXTRACT_TIMEOUT` (défaut 30s).
|
||||
|
||||
##### H. Tests (1 jour)
|
||||
- [ ] **H1. Tests unitaires backend** :
|
||||
- `test_pdf_reader.py` : extraction texte PDF simple, PDF vide, PDF avec uniquement des images (OCR non requis — retourne chaîne vide), PDF protégé par mot de passe, PDF corrompu, extraction métadonnées
|
||||
- Fixtures : créer un PDF de test minimal (2 pages, texte simple) via `reportlab` dans les fixtures de test
|
||||
- `test_pdf_indexing.py` : vérifier qu'un PDF dans un vault est correctement indexé, que le texte est recherchable, que `index_document()` gère l'extension `.pdf`
|
||||
- `test_pdf_api.py` : endpoint `view` retourne `is_pdf: true`, endpoint `pdf/stream` retourne `application/pdf`, endpoint `pdf/info` retourne les métadonnées
|
||||
- [ ] **H2. Tests frontend** :
|
||||
- Test d'intégration : naviguer vers un fichier PDF → l'iframe est rendue
|
||||
- Test : fichier PDF dans les résultats de recherche
|
||||
- Test : téléchargement de PDF fonctionnel
|
||||
- [ ] **H3. CI** : Ajouter la fixture PDF de test dans les artefacts de CI. Les tests PDF sont sautés si pymupdf/pypdf n'est pas disponible.
|
||||
|
||||
##### I. Documentation utilisateur (inclus dans l'effort)
|
||||
- [ ] **I1.** Mettre à jour README.md : mentionner le support PDF dans les formats supportés
|
||||
- [ ] **I2.** Ajouter une note dans la FAQ : « Comment visualiser un PDF dans ObsiGate ? »
|
||||
- [ ] **I3.** Documenter les limitations : pas d'OCR (PDFs scannés non recherchables), pas d'annotation PDF, pas d'édition de PDF
|
||||
|
||||
##### J. Points d'attention / Risques
|
||||
- **Performance** : Un PDF de 500 pages peut générer beaucoup de texte → `SEARCH_CONTENT_LIMIT` (100 Ko) limite l'indexation au début du document. Pour les PDFs volumineux, envisager une extraction paginée avec `SEARCH_CONTENT_LIMIT` réparti sur les N premières pages.
|
||||
- **Sécurité** : Les PDFs malveillants (injections JS, liens externes) ne sont pas exécutés dans l'iframe par défaut (sandbox du navigateur). Ajouter `sandbox="allow-same-origin"` sur l'iframe pour renforcer.
|
||||
- **Mémoire** : pymupdf charge le PDF entier en mémoire. Pour les très gros PDFs (>200 Mo), utiliser le streaming ou `pypdf` qui supporte la lecture paresseuse.
|
||||
- **Compatibilité navigateurs** : Le rendu PDF natif fonctionne sur Chrome, Firefox, Edge, Safari. Safari iOS a des limitations sur les iframes PDF. Prévoir le fallback « ouvrir dans un nouvel onglet » pour ces cas.
|
||||
- **PDFs dans les vaults Obsidian** : Obsidian Desktop ne gère pas nativement les PDFs (affichage via iframe système). ObsiGate apporte une valeur ajoutée en offrant la visualisation + recherche.
|
||||
|
||||
---
|
||||
|
||||
### 75. Éditeur multi-panneaux (Split View)
|
||||
- **Effort :** 5-7 jours | **Impact :** 🟡
|
||||
- **Description :** Système de panneaux divisés permettant d'afficher plusieurs documents côte à côte ou superposés, à la manière d'un éditeur de code (VS Code, Sublime Text). Drag & drop des onglets entre les panneaux, actions depuis le menu contextuel, et raccourcis clavier pour diviser/naviguer entre les panneaux. Le système remplace le `content-area` unique par un gestionnaire de panneaux (PaneManager) qui orchestre 1 à 4 zones d'affichage indépendantes, chacune avec sa propre barre d'onglets.
|
||||
- **Architecture actuelle :**
|
||||
- **DOM** (`frontend/index.html:885-896`) : Un seul bloc `.content-wrapper` contenant `.tab-bar` + `main.content-area#content-area`. Le `content-area` est un conteneur monolithique dont le contenu est remplacé par `renderFile()` à chaque changement d'onglet.
|
||||
- **TabManager** (`frontend/app.js:7988`) : Gère déjà les onglets (ouvrir, fermer, preview, drag-reorder, menu contextuel). État stocké dans `_tabs[]`, `_tabCache{}`, `_activeTabId`. Mais il est couplé au `content-area` unique — il remplace `area.innerHTML` à chaque activation.
|
||||
- **Renderer** : `renderFile()` (`frontend/js/viewer.js:377`) et `renderFileContent()` (`frontend/app.js:3182`) écrivent directement dans `content-area` via `document.getElementById("content-area")`.
|
||||
- **Déjà présent** : Les onglets sont draggables dans la barre (réorganisation interne). Le drag & drop de fichiers depuis l'arborescence n'existe pas encore (#33).
|
||||
- **Sous-tâches :**
|
||||
|
||||
##### A. PaneManager — Gestionnaire de panneaux (2-3 jours)
|
||||
- [ ] **A1. Classe `PaneManager`** (`frontend/js/pane-manager.js`) : Nouveau module responsable de la grille de panneaux.
|
||||
- **État** : `_panes: []` — tableau ordonné de `{id, element, tabBar, tabList, contentArea, tabs: [], activeTabId, tabCache: {}, dirtyTabs: Set}`
|
||||
- **Pane actif** : `_activePaneId` — le dernier panneau ayant reçu le focus
|
||||
- **Disposition** : `_layout: 'horizontal' | 'vertical' | 'grid'` — déterminé automatiquement selon le nombre et l'ordre de création
|
||||
- **Maximum** : 4 panneaux (grille 2×2) pour éviter la surcharge cognitive et les problèmes de performance
|
||||
- [ ] **A2. Création dynamique du DOM** : Quand un 2e panneau est créé, le `.content-wrapper` est transformé :
|
||||
- Le `.tab-bar` et `#content-area` d'origine deviennent le **pane #1**
|
||||
- Un conteneur `.pane-grid` (flex/grid) englobe tous les panneaux
|
||||
- Chaque panneau a sa propre `.pane-container` > `.tab-bar.pane-tab-bar` + `.content-area.pane-content`
|
||||
- Une **poignée de redimensionnement** (`.pane-resize-handle`) est insérée entre chaque paire de panneaux
|
||||
- [ ] **A3. Redimensionnement (resize)** : Implémentation native sans bibliothèque.
|
||||
- Mouse down sur `.pane-resize-handle` → capture du pointeur → calcul des ratios en pixels → mise à jour `flex-basis` en %
|
||||
- Double-clic sur la poignée → réinitialise à 50/50
|
||||
- Persistance des ratios dans `localStorage` par clé `pane-layout-{N}panes`
|
||||
- Contrainte : largeur/hauteur minimum de 200px par panneau
|
||||
- [ ] **A4. Indépendance des panneaux** : Chaque panneau a son propre :
|
||||
- Jeu d'onglets (un fichier peut être ouvert dans plusieurs panneaux simultanément)
|
||||
- État de scroll, toggle source view, position du curseur
|
||||
- Cache de données (`tabCache`) — les données fetchées sont partagées via un cache global pour éviter les requêtes dupliquées
|
||||
- Barre d'outils viewer (copier, source, éditer, pop-out) rattachée au panneau actif
|
||||
|
||||
##### B. Refactoring du TabManager (1 jour)
|
||||
- [ ] **B1. Découplage du DOM global** : Actuellement `TabManager` référence `document.getElementById("content-area")` et `document.getElementById("tab-bar")` en dur. Refactorer pour que chaque instance de Pane TabManager reçoive ses propres éléments DOM en paramètre.
|
||||
- [ ] **B2. Extraction de l'état** : Le `TabManager` actuel est un objet singleton avec état mutable partagé. Le scoper dans une factory `createTabManager(tabBar, tabList, contentArea)` qui retourne une instance indépendante.
|
||||
- [ ] **B3. Compatibilité ascendante** : Le pane #1 (unique par défaut) réutilise les IDs DOM existants (`#tab-bar`, `#tab-list`, `#content-area`) pour ne rien casser. Les panneaux additionnels créent des IDs suffixés (`#tab-bar-2`, `#content-area-2`, etc.).
|
||||
- [ ] **B4. Synchronisation avec le state global** : `currentVault`, `currentPath`, `syncActiveFileTreeItem()` sont mis à jour depuis le panneau actif. Quand on switch de panneau (clic dans un panneau), le state global reflète le document affiché dans ce panneau.
|
||||
|
||||
##### C. Actions de division (1 jour)
|
||||
- [ ] **C1. Menu contextuel des onglets** : Ajouter 3 actions au menu existant (`_showTabContextMenu`) :
|
||||
- « **Diviser à droite** » (`splitRight`) — crée un nouveau panneau à droite avec cet onglet, le ferme dans le panneau source
|
||||
- « **Diviser en bas** » (`splitDown`) — crée un nouveau panneau en dessous
|
||||
- « **Déplacer vers...** » (sous-menu) — liste les autres panneaux existants pour y déplacer l'onglet
|
||||
- [ ] **C2. Raccourcis clavier** (non conflictuels avec le navigateur) :
|
||||
- `Ctrl+Alt+\` → diviser le panneau actif à droite
|
||||
- `Ctrl+Alt+Shift+\` → diviser le panneau actif en bas
|
||||
- `Ctrl+Alt+←/→/↑/↓` → naviguer entre les panneaux (focus)
|
||||
- `Ctrl+Alt+W` → fermer le panneau actif (tous ses onglets sont fermés)
|
||||
- `Ctrl+Shift+Alt+W` → fermer tous les autres panneaux
|
||||
- [ ] **C3. Boutons dans la barre d'onglets** : À droite de chaque `.tab-bar`, ajouter deux mini-boutons (visibles au survol) : « Split Right » (⊞→) et « Split Down » (⊞↓).
|
||||
- [ ] **C4. Fermeture d'un panneau** : Si un panneau est fermé, ses onglets sont perdus (pas déplacés). Si c'est le dernier panneau → retour au mode panneau unique (le DOM redevient comme avant). Si le panneau actif est fermé → le focus passe au voisin le plus proche.
|
||||
|
||||
##### D. Drag & Drop (1 jour)
|
||||
- [ ] **D1. Drag d'onglet entre panneaux** : Étendre le drag & drop existant des onglets.
|
||||
- `dragstart` : mémoriser le `paneId` source + `tabId`
|
||||
- `dragover` sur une `.tab-bar` d'un autre panneau : accepter le drop, afficher un indicateur
|
||||
- `drop` : retirer l'onglet du panneau source, l'ajouter au panneau destination, l'activer. Si c'était le dernier onglet du panneau source et que le panneau a été créé par split → fermer le panneau source (sinon le laisser vide)
|
||||
- `dragend` : nettoyer les indicateurs
|
||||
- [ ] **D2. Drag de fichier depuis l'arborescence** (complément à #33) : Quand un fichier est dragué depuis le tree, au lieu de simplement l'ouvrir dans le panneau actif, permettre de le dropper :
|
||||
- Sur une `.tab-bar` d'un panneau spécifique → ouvre dans ce panneau
|
||||
- Sur une zone vide entre `.pane-resize-handle` → zone de drop fantôme qui crée un nouveau panneau
|
||||
- Sur l'espace après le dernier onglet → ouvre dans ce panneau (comportement par défaut)
|
||||
- [ ] **D3. Drag pour créer un panneau** : Si l'onglet est dragué vers le bord droit ou inférieur du `content-area` actif, une zone de drop « glow » apparaît (comme VS Code). Dropper l'onglet sur cette zone crée un nouveau panneau avec cet onglet.
|
||||
|
||||
##### E. Rendu et performance (0.5 jour)
|
||||
- [ ] **E1. Indépendance du rendu** : Chaque panneau appelle `renderFile()` dans son propre `content-area`. `renderFile()` est déjà conçu pour écrire dans le `content-area` global → nécessite un paramètre `targetElement` optionnel (défaut `#content-area`).
|
||||
- [ ] **E2. Cache de données partagé** : Les données fetchées (`/api/file/{vault}/view`) sont mises en cache au niveau du `PaneManager` pour éviter de refetch le même fichier s'il est ouvert dans 2 panneaux différents. Invalidation du cache quand le fichier est sauvegardé.
|
||||
- [ ] **E3. Rendu paresseux** : Seul le panneau actif fait le rendu complet. Les panneaux inactifs conservent leur DOM intact mais ne sont pas ré-actualisés tant qu'ils ne reçoivent pas le focus (sauf si le fichier a changé sur disque via watcher).
|
||||
- [ ] **E4. Gestion des éditeurs** : Quand un fichier est en cours d'édition dans le panneau A, il est verrouillé en lecture dans le panneau B (badge « En édition ailleurs »). Pas d'édition simultanée du même fichier dans 2 panneaux (géré par le backend qui verrouille via `ETag`/`If-Match` — à implémenter dans #78).
|
||||
|
||||
##### F. CSS & Design (0.5 jour)
|
||||
- [ ] **F1. Grille de panneaux** : CSS Grid pour la disposition.
|
||||
- 1 panneau : pas de grille (layout actuel)
|
||||
- 2 panneaux : `grid-template-columns: 1fr 4px 1fr` (horizontal) ou `grid-template-rows: 1fr 4px 1fr` (vertical)
|
||||
- 3 panneaux : disposition automatique (2 en haut, 1 en bas, ou l'inverse selon l'ordre de création)
|
||||
- 4 panneaux : grille 2×2
|
||||
- [ ] **F2. Poignées de redimensionnement** : Style cohérent avec `.sidebar-resize-handle` existante.
|
||||
- 4px de large, fond `var(--border)`, hover `var(--accent)` avec transition 150ms
|
||||
- Curseur `col-resize` ou `row-resize` selon l'orientation
|
||||
- Barre fine centrale de 2px en `var(--accent)` au survol
|
||||
- [ ] **F3. Barre d'onglets par panneau** : Style identique à la barre d'onglets actuelle.
|
||||
- Onglet actif avec accent-color de fond + border-bottom
|
||||
- Badge « panneau actif » discret : bordure gauche de 2px en `var(--accent)` sur tout le panneau
|
||||
- [ ] **F4. Thème sombre/clair** : Toutes les nouvelles classes CSS utilisent les variables CSS existantes → compatibilité automatique avec le toggle de thème.
|
||||
- [ ] **F5. Responsive / Mobile** : Le split view est désactivé en dessous de 768px. Sur mobile, comportement actuel inchangé (panneau unique, toolbar bottom). Un message dans les paramètres : « Le mode multi-panneaux est disponible sur les écrans larges (≥ 768px) ».
|
||||
|
||||
##### G. Persistance et restauration (0.5 jour)
|
||||
- [ ] **G1. Sauvegarde de la disposition** : Dans `localStorage` sous la clé `obsigate-panes` :
|
||||
```json
|
||||
{
|
||||
"layout": "horizontal",
|
||||
"panes": [
|
||||
{"id": "pane-1", "tabs": ["vaultA::doc1.md", "vaultA::doc2.md"], "activeTab": "vaultA::doc1.md", "width": "50%"},
|
||||
{"id": "pane-2", "tabs": ["vaultB::readme.md"], "activeTab": "vaultB::readme.md", "width": "50%"}
|
||||
]
|
||||
}
|
||||
```
|
||||
- [ ] **G2. Restauration au chargement** : Au démarrage, si `obsigate-panes` existe → restaurer la disposition. Les onglets sont rouverts (fetchés). Si un fichier n'existe plus → l'onglet est ignoré avec un log warning.
|
||||
- [ ] **G3. Reset** : Option dans les paramètres : « Réinitialiser la disposition des panneaux » → supprime `obsigate-panes` du localStorage, retour au mode panneau unique.
|
||||
|
||||
##### H. Compatibilité avec les fonctionnalités existantes (0.5 jour)
|
||||
- [ ] **H1. Palette de commandes (#31)** : Ajouter les commandes « Split Right », « Split Down », « Focus Next Pane », « Focus Previous Pane », « Close Pane », « Close Other Panes ».
|
||||
- [ ] **H2. Pop-out (#??)** : Le bouton pop-out ouvre le document dans une popup séparée (comportement existant). Pas de split view dans la popup (fenêtre indépendante).
|
||||
- [ ] **H3. Mermaid Live Preview (#50)** : La preview Mermaid est locale au panneau actif. Chaque panneau a sa propre preview (gérée par `mermaid-viewer.js` déjà scoped au content-area).
|
||||
- [ ] **H4. AI Editor (#26-29)** : L'éditeur AI s'ouvre dans une modale plein écran (inchangé). Pas d'édition multi-panneau dans l'éditeur.
|
||||
- [ ] **H5. Backup & sauvegarde** : `Ctrl+S` sauvegarde le document du panneau actif uniquement (pas de sauvegarde globale). Chaque panneau gère son propre dirty state.
|
||||
|
||||
##### I. Tests (1 jour)
|
||||
- [ ] **I1. Tests unitaires PaneManager** : Création de panneaux, split right/down, fermeture, redimensionnement, navigation entre panneaux, drag & drop d'onglets entre panneaux.
|
||||
- [ ] **I2. Tests d'intégration frontend** :
|
||||
- Ouvrir 2 fichiers → split right → vérifier que chaque panneau a le bon contenu
|
||||
- Drag d'un onglet du pane 1 vers le pane 2 → vérifier le transfert
|
||||
- Split down → vérifier la disposition verticale
|
||||
- Fermer un panneau → vérifier que l'autre panneau reprend tout l'espace
|
||||
- Redimensionner → vérifier la persistance du ratio
|
||||
- [ ] **I3. Tests E2E (Playwright, #58)** :
|
||||
- Test : split right → 2 panneaux visibles
|
||||
- Test : drag tab entre panneaux
|
||||
- Test : raccourci clavier `Ctrl+Alt+\`
|
||||
- Test : restauration de la disposition au rechargement
|
||||
- [ ] **I4. Tests de régression** : Vérifier que le comportement en mode panneau unique (par défaut) est strictement identique au comportement actuel — même DOM, mêmes IDs, mêmes événements.
|
||||
|
||||
##### J. Points d'attention / Risques
|
||||
- **Complexité du DOM** : Passer d'un content-area unique à un système multi-panneau est le changement architectural le plus profond depuis le refactoring en modules ES — toucher à `renderFile()`, `TabManager`, et tous les appels à `document.getElementById("content-area")` disséminés dans le code.
|
||||
- **Performance mémoire** : 4 panneaux × 10 onglets × contenu HTML = potentiellement lourd. Implémenter le lazy rendering (seul le panneau actif a son DOM dans le document, les autres sont masqués via `display: none` ou `content-visibility: auto`).
|
||||
- **Mobile** : Le split view n'a pas de sens sur mobile → désactivé. Mais le code doit gérer la transition si l'utilisateur bascule en mode desktop (ex: tablette en paysage).
|
||||
- **État global vs local** : `currentVault` et `currentPath` sont utilisés par de nombreuses fonctions (recherche, sidebar, breadcrumb). Le passage au panneau actif doit mettre à jour ces variables de manière fiable — un bug ici casserait la navigation.
|
||||
- **Backup avant split** : Avant la première activation du mode multi-panneau, sauvegarder la disposition actuelle pour pouvoir revenir en arrière en cas de bug.
|
||||
|
||||
---
|
||||
|
||||
## ⚪ Backlog — Priorité 4 (P4)
|
||||
|
||||
### 67. Notifications web — Push API
|
||||
- **Effort :** 2 jours | **Impact :** 🟢
|
||||
- **Description :** Notifications push navigateur pour les changements de vault (fichier modifié, ajouté, supprimé). Utilise la Push API et le service worker existant.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Souscription Push : endpoint `POST /api/push/subscribe` (stockage `subscription` + `vault`)
|
||||
- [ ] Envoi : webhook interne `on_file_change` → dispatch notification via Web Push
|
||||
- [ ] Configuration VAPID : clés publique/privée dans `config.json`
|
||||
- [ ] UI : permission navigateur + toggle activer/désactiver par vault
|
||||
- [ ] Payload : titre du fichier, vault, action (created/modified/deleted)
|
||||
- [ ] Clic sur notification → ouvre le fichier dans ObsiGate
|
||||
|
||||
### 68. Health check enrichi
|
||||
- **Effort :** 1 jour | **Impact :** 🟢
|
||||
- **Description :** Endpoint `/api/health` étendu avec métriques détaillées : état de l'index, consommation mémoire, uptime, connexions SSE actives, nombre de backups, dernière indexation.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Métriques index : nombre de fichiers, nombre de tokens, génération courante
|
||||
- [ ] Métriques mémoire : RSS, heap used (via `psutil` ou `/proc/self/status`)
|
||||
- [ ] Métriques uptime : `time.time() - server_start_time`
|
||||
- [ ] Métriques backups : nombre total, âge du plus vieux, espace disque
|
||||
- [ ] Format réponse JSON structuré : `{ status, uptime, index, memory, backups, connections }`
|
||||
- [ ] Endpoint séparé `GET /api/health/detailed` (protégé admin)
|
||||
|
||||
### 69. Éditeur mobile natif — Interface tactile optimisée
|
||||
- **Effort :** 2-3 jours | **Impact :** 🟢
|
||||
- **Description :** Refonte de l'expérience mobile pour l'édition : barre d'outils contextuelle, presse-papiers optimisé, gestes tactiles (swipe pour actions rapides), mode lecture plein écran.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Barre d'outils mobile flottante : **bold**, *italic*, `code`, liste, lien (inspirée de l'éditeur Obsidian mobile)
|
||||
- [ ] Raccourcis swipe : gauche → backlinks, droite → table des matières
|
||||
- [ ] Mode lecture : cache sidebar + header, plein écran, swipe horizontal pour page suivante
|
||||
- [ ] Presse-papiers : bouton « Coller » persistant (iOS contourne restriction clipboard)
|
||||
- [ ] Adaptation CodeMirror : hauteur ajustable, police agrandissable (pinch zoom)
|
||||
|
||||
### 70. Recherche sémantique — Embeddings vectoriels
|
||||
- **Effort :** 4-5 jours | **Impact :** 🟢
|
||||
- **Description :** Moteur de recherche sémantique utilisant des embeddings (sentence-transformers) pour trouver des notes par similarité de sens, au-delà des mots-clés exacts.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Génération d'embeddings : modèle `all-MiniLM-L6-v2` via `sentence-transformers` (Python) ou appel API externe
|
||||
- [ ] Stockage : index vectoriel avec `numpy` + `faiss` (ou `usearch` pour performance)
|
||||
- [ ] Indexation : embedding par chunk de 512 tokens avec recouvrement
|
||||
- [ ] Recherche hybride : combinaison TF-IDF + similarité cosinus (RRF — Reciprocal Rank Fusion)
|
||||
- [ ] UI : toggle « Recherche sémantique » dans la barre de recherche
|
||||
- [ ] UI : score de similarité dans les résultats
|
||||
|
||||
### 71. Tableau de bord administrateur
|
||||
- **Effort :** 2 jours | **Impact :** 🟢
|
||||
- **Description :** Page d'administration dédiée avec monitoring en temps réel, gestion des utilisateurs avancée, et logs d'audit visualisables.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Widgets temps réel : CPU, mémoire, espace disque, requêtes/min (rafraîchissement SSE)
|
||||
- [ ] Gestion utilisateurs : tableau triable, création/édition/suppression, filtre par rôle
|
||||
- [ ] Logs d'audit : visualisation des 500 dernières entrées, filtre par utilisateur/action/date
|
||||
- [ ] Backup stats : graphique d'évolution (taille totale, nombre par vault, âge moyen)
|
||||
- [ ] Protection : accès restreint au rôle `admin` uniquement
|
||||
|
||||
### 72. API publique documentée — OpenAPI 3.1
|
||||
- **Effort :** 1-2 jours | **Impact :** 🟢
|
||||
- **Description :** Documentation OpenAPI complète et interactive (Swagger UI + Redoc) pour l'API REST, avec exemples de requêtes, descriptions en anglais, et schémas Pydantic exposés.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Audit des endpoints existants → compléter les docstrings manquants
|
||||
- [ ] Ajout de `response_model` sur tous les endpoints (40+ actuellement, ~15 sans modèle)
|
||||
- [ ] Exemples dans les schémas : `examples=[...]` pour les endpoints clés
|
||||
- [ ] Tagging des endpoints par catégorie (Files, Vaults, Search, Auth, AI, Backups)
|
||||
- [ ] Serveur mock : `prism` ou `openapi-generator` pour tests sans backend
|
||||
- [ ] Page de documentation intégrée : lien dans le menu header (« API »)
|
||||
|
||||
### 73. Synchronisation multi-appareils — Obsidian Sync compatible
|
||||
- **Effort :** 6-8 jours | **Impact :** 🟢
|
||||
- **Description :** Synchronisation des vaults entre plusieurs instances d'ObsiGate via un protocole de synchronisation décentralisé ou compatible Obsidian Sync. Alternative self-hosted à Obsidian Sync.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Protocole : évaluation CRDT vs OT vs diff/patch pour fichiers markdown
|
||||
- [ ] Transport : WebSocket ou polling HTTPS avec compression
|
||||
- [ ] Merge strategy : LWW (Last Writer Wins) avec historique des deux versions
|
||||
- [ ] Détection de changements : hash SHA-256 par fichier, journal des modifications
|
||||
- [ ] UI : page « Synchronisation » avec statut par appareil, historique des syncs
|
||||
- [ ] Conflits : UI de résolution manuelle (diff côte à côte entre version locale et distante)
|
||||
- [ ] Chiffrement : optionnel, chiffrement AES-256-GCM avant transmission
|
||||
- [ ] Pairing : échange de clé publique + code QR pour appairage des appareils
|
||||
|
||||
---
|
||||
|
||||
## 📊 Résumé des efforts
|
||||
|
||||
| Priorité | Items | Effort total estimé |
|
||||
|---|---|---|
|
||||
| ✅ Complété | #1 → #57 | ~65 jours |
|
||||
| 🔵 P1 | #58 (Playwright) | 2-3 jours |
|
||||
| ⚪ P3 | #59 → #66, #74, #75 (10 items) | 29-39 jours |
|
||||
| ⚪ P4 | #67 → #73 (7 items) | 18-23 jours |
|
||||
| **Total restant** | **18 items** | **49-65 jours** |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Les items P3/P4 ne sont pas ordonnés par priorité interne — à raffiner selon les retours utilisateurs.
|
||||
- L'effort inclut le développement + tests unitaires + intégration CI, mais pas la documentation utilisateur.
|
||||
- Les items marqués 🟢 (nice-to-have) sont de bons candidats pour des contributions externes.
|
||||
- Le mode hors-ligne (#59) et l'i18n (#63) sont les P3 ayant le meilleur rapport effort/valeur.
|
||||
+530
-109
@@ -1,138 +1,559 @@
|
||||
# ObsiGate — Roadmap (détaillé)
|
||||
# ObsiGate — Roadmap
|
||||
|
||||
> **Date :** 2026-06-02 | **Version :** 1.7.0
|
||||
> **Dernier audit :** [AUDIT_TECHNIQUE_2026-05-27.md](./AUDIT_TECHNIQUE_2026-05-27.md)
|
||||
> **Version :** 2.0.0-dev | **Dernière mise à jour :** 2026-06-05
|
||||
> Voir aussi [CHANGELOG.md](./CHANGELOG.md), [AUDIT_TECHNIQUE.md](./docs/AUDIT_TECHNIQUE_2026-05-27.md)
|
||||
|
||||
---
|
||||
|
||||
## Légende
|
||||
|
||||
- ✅ Complété
|
||||
- 🟡 En cours / Partiel
|
||||
- ⬜ À faire
|
||||
- 🔴 Critique
|
||||
| Icône | Signification |
|
||||
|---|---|
|
||||
| ✅ | Terminé |
|
||||
| 🔵 | En cours |
|
||||
| ⚪ | Prévu / Backlog |
|
||||
| 🔴 Impact | Critique / Bloquant |
|
||||
| 🟡 Impact | Utile / Attendu |
|
||||
| 🟢 Impact | Nice-to-have / Confort |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Fonctionnalités complétées (depuis le dernier audit)
|
||||
## ✅ Complété (v1.0.0 → v2.0.0-dev)
|
||||
|
||||
### AI Editor (v1.7.0 — NOUVEAU)
|
||||
- **Toolbar dropdown** : Edit (improve/fix/shorter/longer/simplify), Tone (professional/casual), Translate, Generate (explain/summarize/continue), Custom Rewrite, Toolbox (to list/to table/frontmatter/to canvas)
|
||||
- **Multi-provider** : DeepSeek, OpenRouter, Gemini — configurable via `.env`
|
||||
- **Backend** : `backend/ai.py` + `backend/ai_routes.py` — 16 endpoints REST
|
||||
- **Frontend** : `frontend/js/ai.js` — toolbar intégrée à CodeMirror 6
|
||||
- Auto-save silencieux (2s), loading toasts, cache si pas de clé API
|
||||
### Fondations (v1.0.0 → v1.4.0)
|
||||
|
||||
### Vue graphique — Améliorations (v1.7.0)
|
||||
- **Header flat design 2 lignes** : titre flexible + recherche dédiée + barre statut
|
||||
- **Sticky panels** : tuile info (top-left/right), preview pleine hauteur (droite), boutons close
|
||||
- **Navigation historique** : ← → (avant/arrière) + ↑ (parent)
|
||||
- **Ctrl+survol** : aperçu contenu `/raw` formaté dans tooltip
|
||||
- Filtre tag, hover foncé unifié
|
||||
| # | 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 | 🟡 |
|
||||
|
||||
### Vue graphique — Phases 1-4 (v1.6.0)
|
||||
- [x] Full-vault view (`scope=full`), filtre tag (`?tag=`), backlinks entrants
|
||||
- [x] Métriques de nœud : `incoming_count`, `outgoing_count`, `tag_count`
|
||||
- [x] Recherche visuelle, tooltips survol, couleurs adaptatives CSS
|
||||
- [x] Slider profondeur (0-3), filtre type (dossier/fichier/.md/autre)
|
||||
- [x] Mode focus, plein écran, export PNG
|
||||
- [x] Barnes-Hut O(n log n), cache graphe
|
||||
### UX & Productivité (v1.5.0 → v1.6.0)
|
||||
|
||||
### Qualité & Tests (v1.6.0)
|
||||
- [x] CI/CD Gitea Actions complet : lint (ruff 0 err), type (mypy 0 err), test (180 tests), security (bandit + pip-audit), build (Docker)
|
||||
- [x] sortedcontainers → SortedList pour O(log n) insert/remove
|
||||
- [x] Frontend tests Node.js : validate-imports.mjs + unit.test.mjs
|
||||
| # | 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 | 🟢 |
|
||||
|
||||
### Recherche (v1.5.1)
|
||||
- [x] Stemming français via snowballstemmer
|
||||
- [x] Performance O(T) au lieu de O(S×T), crash guard IndexError
|
||||
### Architecture (v1.5.1)
|
||||
|
||||
### Refactoring (v1.5.1)
|
||||
- [x] Split `app.js` (8 875 lignes → 16 modules ES) dans `frontend/js/`
|
||||
| # | 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 | 🟡 |
|
||||
|
||||
### Fonctionnel (v1.5.0)
|
||||
- [x] Publication publique de documents (token unique, expiration, lecture seule)
|
||||
- [x] Dashboard statistiques (4 cartes : fichiers, tags, taille, vaults)
|
||||
- [x] Webhooks HTTP avec signature HMAC-SHA256 optionnelle
|
||||
- [x] Documentation OpenAPI enrichie (modèles Pydantic documentés)
|
||||
- [x] Gestion conflits Syncthing (détection + résolution)
|
||||
- [x] Index inversé incrémental (hook pattern via `set_index_change_hook`)
|
||||
- [x] Export PDF (WeasyPrint intégré, accessible dans la vue publique)
|
||||
### CI/CD & Qualité (v1.6.0)
|
||||
|
||||
### Corrections (v1.4.0)
|
||||
- [x] Rate limiting login (IP + compte)
|
||||
- [x] Secret redactor (JWT, API keys, tokens, connection strings)
|
||||
- [x] Audit log JSON lines avec rotation 10MB
|
||||
- [x] Backup automatique avant écriture (`.obsigate-backup/`)
|
||||
- [x] Backlinks panel (index inversé + UI)
|
||||
- [x] Gestion fichiers non-supportés (UI message + download)
|
||||
- [x] Clic simple/double clic arborescence
|
||||
- [x] IGNORED_DIRS configurable via `OBSIGATE_IGNORED_DIRS`
|
||||
- [x] Timeout session configurable (`OBSIGATE_ACCESS_TOKEN_TTL`)
|
||||
- [x] TOC scroll avec caractères accentués (slugify unifié Unicode)
|
||||
| # | 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 | 🔴 |
|
||||
|
||||
### Quick Wins (v1.4.0)
|
||||
- [x] `.dockerignore` — contexte de build réduit
|
||||
- [x] `.env` pour les secrets (mot de passe retiré de docker-compose.yml)
|
||||
- [x] Compression GZip (SSE-safe)
|
||||
- [x] Cache-Control pour assets statiques
|
||||
### 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 | 🟡 |
|
||||
|
||||
---
|
||||
|
||||
## 🔜 Prochaines étapes
|
||||
## 🔵 En cours (P1)
|
||||
|
||||
### P1 — Tests & Qualité
|
||||
|
||||
| # | Item | Effort | Impact |
|
||||
|---|------|--------|--------|
|
||||
| ✅ | ~~Tests unitaires backend (175 tests, 49% coverage)~~ | Fait | 🟢 |
|
||||
| ⬜ | Coverage → 70%+ (main.py 26%, watcher.py 23%, router.py 37%) | 2-3j | 🟢 |
|
||||
| ⬜ | Tests E2E (Playwright) | 3-5j | 🟢 |
|
||||
| ⬜ | Git tags semver (`git tag v1.7.0`) | 10min | 🟡 |
|
||||
|
||||
### P2 — Fonctionnalités UX
|
||||
|
||||
| # | Item | Effort | Impact |
|
||||
|---|------|--------|--------|
|
||||
| ⬜ | Palette de commandes (Ctrl+P) | 1j | 🟡 |
|
||||
| ⬜ | Drag & drop fichiers dans arborescence | 1-2j | 🟡 |
|
||||
| ⬜ | Diff viewer (comparer backups `.obsigate-backup/`) | 1j | 🟢 |
|
||||
| ⬜ | Filtres search avancés (created:, modified:, size:) | 1j | 🟡 |
|
||||
| ⬜ | Fichiers récents par vault (dashboard) | 0.5j | 🟡 |
|
||||
| ⬜ | Déduplication IGNORED_DIRS (indexer.py / watcher.py) | 0.5j | 🟢 |
|
||||
| ⬜ | Export PDF — route API + UI dédiée | 1j | 🟡 |
|
||||
|
||||
### P3/P4 — Long terme
|
||||
|
||||
| # | Item | Effort |
|
||||
|---|------|--------|
|
||||
| ⬜ | Mode hors-ligne PWA (IndexedDB + synchro) | 5-7j |
|
||||
| ⬜ | OAuth2/OIDC (Google, GitHub, SSO) | 3-5j |
|
||||
| ⬜ | Plugin système (extensions utilisateur) | 5-10j |
|
||||
| ⬜ | Collaboration temps réel (WebSocket + Yjs) | 5-10j |
|
||||
| ⬜ | i18n (anglais + français) | 2-3j |
|
||||
| ⬜ | MFA (TOTP/WebAuthn) | 3-5j |
|
||||
| ⬜ | Thèmes personnalisés (CSS variables exposées) | 1j |
|
||||
| ⬜ | Export multi-formats (HTML, MD bundle, ePub) | 2-3j |
|
||||
| ⬜ | Notifications web Push API | 2-3j |
|
||||
| ⬜ | Health check enrichi (index, mémoire, uptime, SSE) | 1j |
|
||||
| ⬜ | Rotation des logs Python (RotatingFileHandler) | 0.5j |
|
||||
### 58. Tests E2E Playwright
|
||||
- **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.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Installation Playwright + config (`playwright.config.ts`)
|
||||
- [ ] Fixtures : vault de test avec 5-10 fichiers .md variés
|
||||
- [ ] Test : login admin → dashboard → liste vaults
|
||||
- [ ] Test : navigation sidebar → ouverture fichier → viewer Markdown
|
||||
- [ ] Test : recherche full-text → résultats triés par pertinence
|
||||
- [ ] Test : éditeur CodeMirror → modification → Ctrl+S → backup créé
|
||||
- [ ] Test : rendu Mermaid (flowchart + sequence) dans le viewer
|
||||
- [ ] Test : export PDF → téléchargement vérifié
|
||||
- [ ] Test : mode sombre → toggle → persistence localStorage
|
||||
- [ ] Test : responsive mobile → toolbar + sidebar repliée
|
||||
- [ ] Intégration CI : job `e2e` dans `.gitea/workflows/ci.yml`
|
||||
|
||||
---
|
||||
|
||||
## Chronologie suggérée
|
||||
## ⚪ Backlog — Priorité 3 (P3)
|
||||
|
||||
| Semaine | Actions |
|
||||
|---------|---------|
|
||||
| **S23** (cette semaine) | ✅ Quick Wins + Sécurité (rate limiting, secret redactor, audit, backup) |
|
||||
| **S24** | ✅ Publication publique, Webhooks, Dashboard, OpenAPI |
|
||||
| **S25** | ✅ Split app.js en modules ES, Stemming français |
|
||||
| **S26** | ✅ Graph view (phases 1-4), CI/CD, sortedcontainers |
|
||||
| **S27** | ✅ AI Editor + Graph improvements (sticky panels, nav history) |
|
||||
| **S28** | Coverage 70%+, Export PDF, Palette commandes |
|
||||
| **S29+** | Fonctionnalités P2 au fil de l'eau |
|
||||
### 59. Mode hors-ligne PWA complet
|
||||
- **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.
|
||||
- **Sous-tâches :**
|
||||
- [ ] IndexedDB : stockage local de l'index des fichiers (paths, titles, tags)
|
||||
- [ ] Moteur de recherche offline via IndexedDB (cursor + filtre)
|
||||
- [ ] Cache des fichiers markdown récemment ouverts (derniers 50)
|
||||
- [ ] Stratégie de cache : Network First avec fallback IndexedDB
|
||||
- [ ] File de synchronisation : modifications offline → appliquées au retour réseau
|
||||
- [ ] UI indicateur : badge « Hors-ligne » + compteur de modifications en attente
|
||||
- [ ] Gestion des conflits : détection et résolution manuelle (choix version locale vs serveur)
|
||||
|
||||
### 60. OAuth2 / OIDC — Authentification SSO
|
||||
- **Effort :** 2-3 jours | **Impact :** 🟡
|
||||
- **Description :** Support de fournisseurs OAuth2/OIDC externes pour permettre l'authentification via Google, GitHub, ou tout provider compatible. Complément à l'auth JWT existante (pas de remplacement).
|
||||
- **Sous-tâches :**
|
||||
- [ ] Configuration par provider : `OBSIGATE_OAUTH_GOOGLE_CLIENT_ID`, `OBSIGATE_OAUTH_GITHUB_CLIENT_ID`, etc.
|
||||
- [ ] Endpoint `GET /api/auth/oauth/login?provider=google` → redirection
|
||||
- [ ] Endpoint `GET /api/auth/oauth/callback` → échange code → token OIDC → création/liaison compte local
|
||||
- [ ] Mapping roles : config `OBSIGATE_OAUTH_DEFAULT_ROLE` (défaut `user`)
|
||||
- [ ] UI : boutons « Se connecter avec Google / GitHub » sur la page login
|
||||
- [ ] Sécurité : state parameter anti-CSRF, PKCE, nonce validation
|
||||
- [ ] Stockage : liaison `oauth_provider` + `oauth_sub` dans `users.json`
|
||||
|
||||
### 61. Plugins système — Extensions utilisateur
|
||||
- **Effort :** 4-5 jours | **Impact :** 🟢
|
||||
- **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 :**
|
||||
- [ ] Spécification du format de plugin : `plugin.json` (name, version, hooks, permissions)
|
||||
- [ ] API de hooks : `onFileRender`, `onSearchFilter`, `onEditorAction`, `onSidebarItem`
|
||||
- [ ] Sandbox d'exécution : Web Worker isolé pour le code plugin
|
||||
- [ ] UI : page « Plugins » dans les paramètres (installer, activer/désactiver, désinstaller)
|
||||
- [ ] Distribution : dépôt de plugins communautaire (fichier JSON index)
|
||||
- [ ] Hot-reload : activation/désactivation sans rechargement de page
|
||||
- [ ] Sécurité : manifest de permissions, validation de signature, CSP restrictif
|
||||
|
||||
### 62. Collaboration temps réel — Édition simultanée
|
||||
- **Effort :** 5-7 jours | **Impact :** 🟢
|
||||
- **Description :** Édition collaborative de fichiers markdown via WebSocket + CRDT (Yjs). Plusieurs utilisateurs peuvent éditer le même fichier simultanément avec résolution automatique des conflits.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Serveur WebSocket : endpoint `/ws/collab/{vault}/{path}` avec gestion des rooms
|
||||
- [ ] Intégration Yjs : `Y.Doc` partagé, `Y.Text` pour le contenu markdown
|
||||
- [ ] Awareness : curseurs colorés par utilisateur, sélections visibles
|
||||
- [ ] Synchro backend : persistance périodique du document (debounce 2s)
|
||||
- [ ] Gestion des droits : vérification `check_vault_access` par connexion WS
|
||||
- [ ] UI : indicateur de présence (avatars dans la barre d'outils éditeur)
|
||||
- [ ] UI : curseurs distants dans CodeMirror (extension collaborative)
|
||||
- [ ] 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
|
||||
- **Effort :** 2-3 jours | **Impact :** 🟡
|
||||
- **Description :** Support de l'anglais et du français via un système de clés de traduction. L'interface est actuellement en français uniquement. L'anglais est nécessaire pour une adoption plus large.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Extraction des chaînes : inventaire de toutes les chaînes UI (~300)
|
||||
- [ ] Format : JSON `fr.json` + `en.json` dans `frontend/locales/`
|
||||
- [ ] Fonction `t(key, fallback)` → détection `navigator.language`
|
||||
- [ ] Sélecteur de langue dans les paramètres (persisté localStorage)
|
||||
- [ ] Traduction des messages backend (erreurs API, toasts)
|
||||
- [ ] Documentation multilingue (README.fr.md, README.md)
|
||||
|
||||
### 64. MFA — Authentification multi-facteurs
|
||||
- **Effort :** 2 jours | **Impact :** 🟡
|
||||
- **Description :** Ajout de TOTP (Time-based One-Time Password) et WebAuthn (clés de sécurité) comme second facteur d'authentification pour les comptes admin.
|
||||
- **Sous-tâches :**
|
||||
- [ ] TOTP : génération de secret, QR code, vérification code 6 chiffres
|
||||
- [ ] WebAuthn : enregistrement de clé, assertion, attestation
|
||||
- [ ] UI : page « Sécurité du compte » avec activation/désactivation MFA
|
||||
- [ ] Flow login : mot de passe → challenge TOTP si activé
|
||||
- [ ] Recovery codes : 8 codes de backup à usage unique
|
||||
- [ ] Stockage : `totp_secret` + `webauthn_credential_id` dans `users.json`
|
||||
|
||||
### 65. Thèmes personnalisés — CSS variables
|
||||
- **Effort :** 1-2 jours | **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 :**
|
||||
- [ ] Audit des variables CSS existantes → liste des 30+ variables
|
||||
- [ ] Fichier `themes.json` avec presets (light, dark, high-contrast, sepia)
|
||||
- [ ] UI : sélecteur de thème dans les paramètres (aperçu live)
|
||||
- [ ] Import/export de thème personnalisé (JSON)
|
||||
- [ ] Application dynamique sans rechargement (`document.documentElement.style.setProperty`)
|
||||
|
||||
### 66. Export multi-formats
|
||||
- **Effort :** 1-2 jours | **Impact :** 🟢
|
||||
- **Description :** Export de notes individuelles ou de vaults entiers en HTML standalone, bundle Markdown (.zip), et ePub pour liseuses.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Export HTML standalone : CSS inliné, images en base64, navigation inter-fichiers
|
||||
- [ ] Export MD bundle : ZIP du vault avec structure préservée
|
||||
- [ ] Export ePub : conversion markdown → ePub via `markdown` + `ebooklib`
|
||||
- [ ] 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 `<iframe>` pointant sur `/api/file/{vault}/pdf/stream?path=...`. Approche optimale :
|
||||
- Zéro dépendance JS supplémentaire
|
||||
- Rendu identique à Chrome/Firefox/Safari natif
|
||||
- Support natif du zoom, recherche dans le document, navigation par pages, rotation
|
||||
- L'iframe s'adapte en hauteur (`height: 100%` du content-area)
|
||||
- [ ] **E2. Détection dans le viewer** : Dans `frontend/js/viewer.js`, fonction `renderFileContent()` — ajouter une branche après la détection `data.unsupported` :
|
||||
- Si `data.is_pdf === true` → render l'iframe PDF au lieu du viewer markdown
|
||||
- Si le navigateur ne supporte pas le rendu PDF inline → fallback sur l'UI « binaire » avec bouton download + bouton « Ouvrir dans un nouvel onglet »
|
||||
- [ ] **E3. Barre d'outils PDF** : Dans la barre d'outils du viewer (celle qui a déjà les boutons Copier, Source, .md, PDF, Éditer, pop-out), pour les fichiers PDF :
|
||||
- Remplacer « Copier » / « Source » / « Éditer » par des actions spécifiques PDF
|
||||
- Bouton « Télécharger » (.pdf) — déjà existant, fonctionne
|
||||
- Bouton « Plein écran » — ouvre le PDF dans un nouvel onglet en plein écran
|
||||
- Badge « N pages » indiquant le nombre de pages
|
||||
- Bouton « pop-out » — gardé, ouvre le viewer PDF dans une popup séparée
|
||||
- [ ] **E4. Thème** : L'iframe PDF est en dehors du DOM applicatif donc pas affecté par le thème dark/light. Ajouter un message discret « Le PDF s'affiche avec le thème de votre navigateur » si `_currentTheme === 'dark'` (les PDFs en fond blanc dans un thème sombre peuvent surprendre).
|
||||
- [ ] **E5. Responsive** : L'iframe s'adapte à la largeur du content-area. En mode mobile, hauteur ajustée à la viewport. La toolbar mobile existante fonctionne avec les actions PDF.
|
||||
|
||||
##### F. Frontend — Recherche (0.5 jour)
|
||||
- [ ] **F1. Résultats de recherche** : Les PDFs apparaissent dans les résultats via le TF-IDF existant (le texte extrait est indexé). Ajouter un badge visuel « PDF » à côté du titre dans les résultats de recherche pour distinguer les PDFs des MD — utiliser l'icône `file-text`.
|
||||
- [ ] **F2. Snippets de recherche** : Les extraits de contexte montrent le texte extrait du PDF avec surlignage des termes recherchés — fonctionnement identique aux MD via le mécanisme de snippet existant dans `search.py`.
|
||||
- [ ] **F3. Filtres de recherche avancés** : Ajouter `ext:pdf` comme filtre pour limiter la recherche aux PDFs uniquement (complément aux filtres `created:`, `modified:`, `size:` déjà prévus #34).
|
||||
|
||||
##### G. Docker & Dépendances (0.5 jour)
|
||||
- [ ] **G1. requirements.txt** : Ajouter `pymupdf>=1.24.0` (sinon `pypdf>=4.0` en fallback).
|
||||
- [ ] **G2. Dockerfile** : Vérifier que l'image `python:3.11-slim` dispose des libs système nécessaires pour pymupdf. Si besoin, ajouter `libmupdf-dev` ou utiliser `pypdf` (pure Python) pour éviter la complexité. Recommandation : pypdf pour la simplicité Docker, pymupdf en option pour la performance.
|
||||
- [ ] **G3. Configuration** : Ajouter `OBSIGATE_PDF_MAX_SIZE_MB` (défaut 50) pour limiter la taille des PDFs indexés et `OBSIGATE_PDF_EXTRACT_TIMEOUT` (défaut 30s).
|
||||
|
||||
##### H. Tests (1 jour)
|
||||
- [ ] **H1. Tests unitaires backend** :
|
||||
- `test_pdf_reader.py` : extraction texte PDF simple, PDF vide, PDF avec uniquement des images (OCR non requis — retourne chaîne vide), PDF protégé par mot de passe, PDF corrompu, extraction métadonnées
|
||||
- Fixtures : créer un PDF de test minimal (2 pages, texte simple) via `reportlab` dans les fixtures de test
|
||||
- `test_pdf_indexing.py` : vérifier qu'un PDF dans un vault est correctement indexé, que le texte est recherchable, que `index_document()` gère l'extension `.pdf`
|
||||
- `test_pdf_api.py` : endpoint `view` retourne `is_pdf: true`, endpoint `pdf/stream` retourne `application/pdf`, endpoint `pdf/info` retourne les métadonnées
|
||||
- [ ] **H2. Tests frontend** :
|
||||
- Test d'intégration : naviguer vers un fichier PDF → l'iframe est rendue
|
||||
- Test : fichier PDF dans les résultats de recherche
|
||||
- Test : téléchargement de PDF fonctionnel
|
||||
- [ ] **H3. CI** : Ajouter la fixture PDF de test dans les artefacts de CI. Les tests PDF sont sautés si pymupdf/pypdf n'est pas disponible.
|
||||
|
||||
##### I. Documentation utilisateur (inclus dans l'effort)
|
||||
- [ ] **I1.** Mettre à jour README.md : mentionner le support PDF dans les formats supportés
|
||||
- [ ] **I2.** Ajouter une note dans la FAQ : « Comment visualiser un PDF dans ObsiGate ? »
|
||||
- [ ] **I3.** Documenter les limitations : pas d'OCR (PDFs scannés non recherchables), pas d'annotation PDF, pas d'édition de PDF
|
||||
|
||||
##### J. Points d'attention / Risques
|
||||
- **Performance** : Un PDF de 500 pages peut générer beaucoup de texte → `SEARCH_CONTENT_LIMIT` (100 Ko) limite l'indexation au début du document. Pour les PDFs volumineux, envisager une extraction paginée avec `SEARCH_CONTENT_LIMIT` réparti sur les N premières pages.
|
||||
- **Sécurité** : Les PDFs malveillants (injections JS, liens externes) ne sont pas exécutés dans l'iframe par défaut (sandbox du navigateur). Ajouter `sandbox="allow-same-origin"` sur l'iframe pour renforcer.
|
||||
- **Mémoire** : pymupdf charge le PDF entier en mémoire. Pour les très gros PDFs (>200 Mo), utiliser le streaming ou `pypdf` qui supporte la lecture paresseuse.
|
||||
- **Compatibilité navigateurs** : Le rendu PDF natif fonctionne sur Chrome, Firefox, Edge, Safari. Safari iOS a des limitations sur les iframes PDF. Prévoir le fallback « ouvrir dans un nouvel onglet » pour ces cas.
|
||||
- **PDFs dans les vaults Obsidian** : Obsidian Desktop ne gère pas nativement les PDFs (affichage via iframe système). ObsiGate apporte une valeur ajoutée en offrant la visualisation + recherche.
|
||||
|
||||
---
|
||||
|
||||
*Document généré le 2026-06-02 — Mis à jour depuis l'audit du 2026-05-27.*
|
||||
*Voir aussi : [ROADMAP.md](../ROADMAP.md) (version simplifiée), [CHANGELOG.md](../CHANGELOG.md)*
|
||||
### 75. Éditeur multi-panneaux (Split View)
|
||||
- **Effort :** 5-7 jours | **Impact :** 🟡
|
||||
- **Description :** Système de panneaux divisés permettant d'afficher plusieurs documents côte à côte ou superposés, à la manière d'un éditeur de code (VS Code, Sublime Text). Drag & drop des onglets entre les panneaux, actions depuis le menu contextuel, et raccourcis clavier pour diviser/naviguer entre les panneaux. Le système remplace le `content-area` unique par un gestionnaire de panneaux (PaneManager) qui orchestre 1 à 4 zones d'affichage indépendantes, chacune avec sa propre barre d'onglets.
|
||||
- **Architecture actuelle :**
|
||||
- **DOM** (`frontend/index.html:885-896`) : Un seul bloc `.content-wrapper` contenant `.tab-bar` + `main.content-area#content-area`. Le `content-area` est un conteneur monolithique dont le contenu est remplacé par `renderFile()` à chaque changement d'onglet.
|
||||
- **TabManager** (`frontend/app.js:7988`) : Gère déjà les onglets (ouvrir, fermer, preview, drag-reorder, menu contextuel). État stocké dans `_tabs[]`, `_tabCache{}`, `_activeTabId`. Mais il est couplé au `content-area` unique — il remplace `area.innerHTML` à chaque activation.
|
||||
- **Renderer** : `renderFile()` (`frontend/js/viewer.js:377`) et `renderFileContent()` (`frontend/app.js:3182`) écrivent directement dans `content-area` via `document.getElementById("content-area")`.
|
||||
- **Déjà présent** : Les onglets sont draggables dans la barre (réorganisation interne). Le drag & drop de fichiers depuis l'arborescence n'existe pas encore (#33).
|
||||
- **Sous-tâches :**
|
||||
|
||||
##### A. PaneManager — Gestionnaire de panneaux (2-3 jours)
|
||||
- [ ] **A1. Classe `PaneManager`** (`frontend/js/pane-manager.js`) : Nouveau module responsable de la grille de panneaux.
|
||||
- **État** : `_panes: []` — tableau ordonné de `{id, element, tabBar, tabList, contentArea, tabs: [], activeTabId, tabCache: {}, dirtyTabs: Set}`
|
||||
- **Pane actif** : `_activePaneId` — le dernier panneau ayant reçu le focus
|
||||
- **Disposition** : `_layout: 'horizontal' | 'vertical' | 'grid'` — déterminé automatiquement selon le nombre et l'ordre de création
|
||||
- **Maximum** : 4 panneaux (grille 2×2) pour éviter la surcharge cognitive et les problèmes de performance
|
||||
- [ ] **A2. Création dynamique du DOM** : Quand un 2e panneau est créé, le `.content-wrapper` est transformé :
|
||||
- Le `.tab-bar` et `#content-area` d'origine deviennent le **pane #1**
|
||||
- Un conteneur `.pane-grid` (flex/grid) englobe tous les panneaux
|
||||
- Chaque panneau a sa propre `.pane-container` > `.tab-bar.pane-tab-bar` + `.content-area.pane-content`
|
||||
- Une **poignée de redimensionnement** (`.pane-resize-handle`) est insérée entre chaque paire de panneaux
|
||||
- [ ] **A3. Redimensionnement (resize)** : Implémentation native sans bibliothèque.
|
||||
- Mouse down sur `.pane-resize-handle` → capture du pointeur → calcul des ratios en pixels → mise à jour `flex-basis` en %
|
||||
- Double-clic sur la poignée → réinitialise à 50/50
|
||||
- Persistance des ratios dans `localStorage` par clé `pane-layout-{N}panes`
|
||||
- Contrainte : largeur/hauteur minimum de 200px par panneau
|
||||
- [ ] **A4. Indépendance des panneaux** : Chaque panneau a son propre :
|
||||
- Jeu d'onglets (un fichier peut être ouvert dans plusieurs panneaux simultanément)
|
||||
- État de scroll, toggle source view, position du curseur
|
||||
- Cache de données (`tabCache`) — les données fetchées sont partagées via un cache global pour éviter les requêtes dupliquées
|
||||
- Barre d'outils viewer (copier, source, éditer, pop-out) rattachée au panneau actif
|
||||
|
||||
##### B. Refactoring du TabManager (1 jour)
|
||||
- [ ] **B1. Découplage du DOM global** : Actuellement `TabManager` référence `document.getElementById("content-area")` et `document.getElementById("tab-bar")` en dur. Refactorer pour que chaque instance de Pane TabManager reçoive ses propres éléments DOM en paramètre.
|
||||
- [ ] **B2. Extraction de l'état** : Le `TabManager` actuel est un objet singleton avec état mutable partagé. Le scoper dans une factory `createTabManager(tabBar, tabList, contentArea)` qui retourne une instance indépendante.
|
||||
- [ ] **B3. Compatibilité ascendante** : Le pane #1 (unique par défaut) réutilise les IDs DOM existants (`#tab-bar`, `#tab-list`, `#content-area`) pour ne rien casser. Les panneaux additionnels créent des IDs suffixés (`#tab-bar-2`, `#content-area-2`, etc.).
|
||||
- [ ] **B4. Synchronisation avec le state global** : `currentVault`, `currentPath`, `syncActiveFileTreeItem()` sont mis à jour depuis le panneau actif. Quand on switch de panneau (clic dans un panneau), le state global reflète le document affiché dans ce panneau.
|
||||
|
||||
##### C. Actions de division (1 jour)
|
||||
- [ ] **C1. Menu contextuel des onglets** : Ajouter 3 actions au menu existant (`_showTabContextMenu`) :
|
||||
- « **Diviser à droite** » (`splitRight`) — crée un nouveau panneau à droite avec cet onglet, le ferme dans le panneau source
|
||||
- « **Diviser en bas** » (`splitDown`) — crée un nouveau panneau en dessous
|
||||
- « **Déplacer vers...** » (sous-menu) — liste les autres panneaux existants pour y déplacer l'onglet
|
||||
- [ ] **C2. Raccourcis clavier** (non conflictuels avec le navigateur) :
|
||||
- `Ctrl+Alt+\` → diviser le panneau actif à droite
|
||||
- `Ctrl+Alt+Shift+\` → diviser le panneau actif en bas
|
||||
- `Ctrl+Alt+←/→/↑/↓` → naviguer entre les panneaux (focus)
|
||||
- `Ctrl+Alt+W` → fermer le panneau actif (tous ses onglets sont fermés)
|
||||
- `Ctrl+Shift+Alt+W` → fermer tous les autres panneaux
|
||||
- [ ] **C3. Boutons dans la barre d'onglets** : À droite de chaque `.tab-bar`, ajouter deux mini-boutons (visibles au survol) : « Split Right » (⊞→) et « Split Down » (⊞↓).
|
||||
- [ ] **C4. Fermeture d'un panneau** : Si un panneau est fermé, ses onglets sont perdus (pas déplacés). Si c'est le dernier panneau → retour au mode panneau unique (le DOM redevient comme avant). Si le panneau actif est fermé → le focus passe au voisin le plus proche.
|
||||
|
||||
##### D. Drag & Drop (1 jour)
|
||||
- [ ] **D1. Drag d'onglet entre panneaux** : Étendre le drag & drop existant des onglets.
|
||||
- `dragstart` : mémoriser le `paneId` source + `tabId`
|
||||
- `dragover` sur une `.tab-bar` d'un autre panneau : accepter le drop, afficher un indicateur
|
||||
- `drop` : retirer l'onglet du panneau source, l'ajouter au panneau destination, l'activer. Si c'était le dernier onglet du panneau source et que le panneau a été créé par split → fermer le panneau source (sinon le laisser vide)
|
||||
- `dragend` : nettoyer les indicateurs
|
||||
- [ ] **D2. Drag de fichier depuis l'arborescence** (complément à #33) : Quand un fichier est dragué depuis le tree, au lieu de simplement l'ouvrir dans le panneau actif, permettre de le dropper :
|
||||
- Sur une `.tab-bar` d'un panneau spécifique → ouvre dans ce panneau
|
||||
- Sur une zone vide entre `.pane-resize-handle` → zone de drop fantôme qui crée un nouveau panneau
|
||||
- Sur l'espace après le dernier onglet → ouvre dans ce panneau (comportement par défaut)
|
||||
- [ ] **D3. Drag pour créer un panneau** : Si l'onglet est dragué vers le bord droit ou inférieur du `content-area` actif, une zone de drop « glow » apparaît (comme VS Code). Dropper l'onglet sur cette zone crée un nouveau panneau avec cet onglet.
|
||||
|
||||
##### E. Rendu et performance (0.5 jour)
|
||||
- [ ] **E1. Indépendance du rendu** : Chaque panneau appelle `renderFile()` dans son propre `content-area`. `renderFile()` est déjà conçu pour écrire dans le `content-area` global → nécessite un paramètre `targetElement` optionnel (défaut `#content-area`).
|
||||
- [ ] **E2. Cache de données partagé** : Les données fetchées (`/api/file/{vault}/view`) sont mises en cache au niveau du `PaneManager` pour éviter de refetch le même fichier s'il est ouvert dans 2 panneaux différents. Invalidation du cache quand le fichier est sauvegardé.
|
||||
- [ ] **E3. Rendu paresseux** : Seul le panneau actif fait le rendu complet. Les panneaux inactifs conservent leur DOM intact mais ne sont pas ré-actualisés tant qu'ils ne reçoivent pas le focus (sauf si le fichier a changé sur disque via watcher).
|
||||
- [ ] **E4. Gestion des éditeurs** : Quand un fichier est en cours d'édition dans le panneau A, il est verrouillé en lecture dans le panneau B (badge « En édition ailleurs »). Pas d'édition simultanée du même fichier dans 2 panneaux (géré par le backend qui verrouille via `ETag`/`If-Match` — à implémenter dans #78).
|
||||
|
||||
##### F. CSS & Design (0.5 jour)
|
||||
- [ ] **F1. Grille de panneaux** : CSS Grid pour la disposition.
|
||||
- 1 panneau : pas de grille (layout actuel)
|
||||
- 2 panneaux : `grid-template-columns: 1fr 4px 1fr` (horizontal) ou `grid-template-rows: 1fr 4px 1fr` (vertical)
|
||||
- 3 panneaux : disposition automatique (2 en haut, 1 en bas, ou l'inverse selon l'ordre de création)
|
||||
- 4 panneaux : grille 2×2
|
||||
- [ ] **F2. Poignées de redimensionnement** : Style cohérent avec `.sidebar-resize-handle` existante.
|
||||
- 4px de large, fond `var(--border)`, hover `var(--accent)` avec transition 150ms
|
||||
- Curseur `col-resize` ou `row-resize` selon l'orientation
|
||||
- Barre fine centrale de 2px en `var(--accent)` au survol
|
||||
- [ ] **F3. Barre d'onglets par panneau** : Style identique à la barre d'onglets actuelle.
|
||||
- Onglet actif avec accent-color de fond + border-bottom
|
||||
- Badge « panneau actif » discret : bordure gauche de 2px en `var(--accent)` sur tout le panneau
|
||||
- [ ] **F4. Thème sombre/clair** : Toutes les nouvelles classes CSS utilisent les variables CSS existantes → compatibilité automatique avec le toggle de thème.
|
||||
- [ ] **F5. Responsive / Mobile** : Le split view est désactivé en dessous de 768px. Sur mobile, comportement actuel inchangé (panneau unique, toolbar bottom). Un message dans les paramètres : « Le mode multi-panneaux est disponible sur les écrans larges (≥ 768px) ».
|
||||
|
||||
##### G. Persistance et restauration (0.5 jour)
|
||||
- [ ] **G1. Sauvegarde de la disposition** : Dans `localStorage` sous la clé `obsigate-panes` :
|
||||
```json
|
||||
{
|
||||
"layout": "horizontal",
|
||||
"panes": [
|
||||
{"id": "pane-1", "tabs": ["vaultA::doc1.md", "vaultA::doc2.md"], "activeTab": "vaultA::doc1.md", "width": "50%"},
|
||||
{"id": "pane-2", "tabs": ["vaultB::readme.md"], "activeTab": "vaultB::readme.md", "width": "50%"}
|
||||
]
|
||||
}
|
||||
```
|
||||
- [ ] **G2. Restauration au chargement** : Au démarrage, si `obsigate-panes` existe → restaurer la disposition. Les onglets sont rouverts (fetchés). Si un fichier n'existe plus → l'onglet est ignoré avec un log warning.
|
||||
- [ ] **G3. Reset** : Option dans les paramètres : « Réinitialiser la disposition des panneaux » → supprime `obsigate-panes` du localStorage, retour au mode panneau unique.
|
||||
|
||||
##### H. Compatibilité avec les fonctionnalités existantes (0.5 jour)
|
||||
- [ ] **H1. Palette de commandes (#31)** : Ajouter les commandes « Split Right », « Split Down », « Focus Next Pane », « Focus Previous Pane », « Close Pane », « Close Other Panes ».
|
||||
- [ ] **H2. Pop-out (#??)** : Le bouton pop-out ouvre le document dans une popup séparée (comportement existant). Pas de split view dans la popup (fenêtre indépendante).
|
||||
- [ ] **H3. Mermaid Live Preview (#50)** : La preview Mermaid est locale au panneau actif. Chaque panneau a sa propre preview (gérée par `mermaid-viewer.js` déjà scoped au content-area).
|
||||
- [ ] **H4. AI Editor (#26-29)** : L'éditeur AI s'ouvre dans une modale plein écran (inchangé). Pas d'édition multi-panneau dans l'éditeur.
|
||||
- [ ] **H5. Backup & sauvegarde** : `Ctrl+S` sauvegarde le document du panneau actif uniquement (pas de sauvegarde globale). Chaque panneau gère son propre dirty state.
|
||||
|
||||
##### I. Tests (1 jour)
|
||||
- [ ] **I1. Tests unitaires PaneManager** : Création de panneaux, split right/down, fermeture, redimensionnement, navigation entre panneaux, drag & drop d'onglets entre panneaux.
|
||||
- [ ] **I2. Tests d'intégration frontend** :
|
||||
- Ouvrir 2 fichiers → split right → vérifier que chaque panneau a le bon contenu
|
||||
- Drag d'un onglet du pane 1 vers le pane 2 → vérifier le transfert
|
||||
- Split down → vérifier la disposition verticale
|
||||
- Fermer un panneau → vérifier que l'autre panneau reprend tout l'espace
|
||||
- Redimensionner → vérifier la persistance du ratio
|
||||
- [ ] **I3. Tests E2E (Playwright, #58)** :
|
||||
- Test : split right → 2 panneaux visibles
|
||||
- Test : drag tab entre panneaux
|
||||
- Test : raccourci clavier `Ctrl+Alt+\`
|
||||
- Test : restauration de la disposition au rechargement
|
||||
- [ ] **I4. Tests de régression** : Vérifier que le comportement en mode panneau unique (par défaut) est strictement identique au comportement actuel — même DOM, mêmes IDs, mêmes événements.
|
||||
|
||||
##### J. Points d'attention / Risques
|
||||
- **Complexité du DOM** : Passer d'un content-area unique à un système multi-panneau est le changement architectural le plus profond depuis le refactoring en modules ES — toucher à `renderFile()`, `TabManager`, et tous les appels à `document.getElementById("content-area")` disséminés dans le code.
|
||||
- **Performance mémoire** : 4 panneaux × 10 onglets × contenu HTML = potentiellement lourd. Implémenter le lazy rendering (seul le panneau actif a son DOM dans le document, les autres sont masqués via `display: none` ou `content-visibility: auto`).
|
||||
- **Mobile** : Le split view n'a pas de sens sur mobile → désactivé. Mais le code doit gérer la transition si l'utilisateur bascule en mode desktop (ex: tablette en paysage).
|
||||
- **État global vs local** : `currentVault` et `currentPath` sont utilisés par de nombreuses fonctions (recherche, sidebar, breadcrumb). Le passage au panneau actif doit mettre à jour ces variables de manière fiable — un bug ici casserait la navigation.
|
||||
- **Backup avant split** : Avant la première activation du mode multi-panneau, sauvegarder la disposition actuelle pour pouvoir revenir en arrière en cas de bug.
|
||||
|
||||
---
|
||||
|
||||
## ⚪ Backlog — Priorité 4 (P4)
|
||||
|
||||
### 67. Notifications web — Push API
|
||||
- **Effort :** 2 jours | **Impact :** 🟢
|
||||
- **Description :** Notifications push navigateur pour les changements de vault (fichier modifié, ajouté, supprimé). Utilise la Push API et le service worker existant.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Souscription Push : endpoint `POST /api/push/subscribe` (stockage `subscription` + `vault`)
|
||||
- [ ] Envoi : webhook interne `on_file_change` → dispatch notification via Web Push
|
||||
- [ ] Configuration VAPID : clés publique/privée dans `config.json`
|
||||
- [ ] UI : permission navigateur + toggle activer/désactiver par vault
|
||||
- [ ] Payload : titre du fichier, vault, action (created/modified/deleted)
|
||||
- [ ] Clic sur notification → ouvre le fichier dans ObsiGate
|
||||
|
||||
### 68. Health check enrichi
|
||||
- **Effort :** 1 jour | **Impact :** 🟢
|
||||
- **Description :** Endpoint `/api/health` étendu avec métriques détaillées : état de l'index, consommation mémoire, uptime, connexions SSE actives, nombre de backups, dernière indexation.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Métriques index : nombre de fichiers, nombre de tokens, génération courante
|
||||
- [ ] Métriques mémoire : RSS, heap used (via `psutil` ou `/proc/self/status`)
|
||||
- [ ] Métriques uptime : `time.time() - server_start_time`
|
||||
- [ ] Métriques backups : nombre total, âge du plus vieux, espace disque
|
||||
- [ ] Format réponse JSON structuré : `{ status, uptime, index, memory, backups, connections }`
|
||||
- [ ] Endpoint séparé `GET /api/health/detailed` (protégé admin)
|
||||
|
||||
### 69. Éditeur mobile natif — Interface tactile optimisée
|
||||
- **Effort :** 2-3 jours | **Impact :** 🟢
|
||||
- **Description :** Refonte de l'expérience mobile pour l'édition : barre d'outils contextuelle, presse-papiers optimisé, gestes tactiles (swipe pour actions rapides), mode lecture plein écran.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Barre d'outils mobile flottante : **bold**, *italic*, `code`, liste, lien (inspirée de l'éditeur Obsidian mobile)
|
||||
- [ ] Raccourcis swipe : gauche → backlinks, droite → table des matières
|
||||
- [ ] Mode lecture : cache sidebar + header, plein écran, swipe horizontal pour page suivante
|
||||
- [ ] Presse-papiers : bouton « Coller » persistant (iOS contourne restriction clipboard)
|
||||
- [ ] Adaptation CodeMirror : hauteur ajustable, police agrandissable (pinch zoom)
|
||||
|
||||
### 70. Recherche sémantique — Embeddings vectoriels
|
||||
- **Effort :** 4-5 jours | **Impact :** 🟢
|
||||
- **Description :** Moteur de recherche sémantique utilisant des embeddings (sentence-transformers) pour trouver des notes par similarité de sens, au-delà des mots-clés exacts.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Génération d'embeddings : modèle `all-MiniLM-L6-v2` via `sentence-transformers` (Python) ou appel API externe
|
||||
- [ ] Stockage : index vectoriel avec `numpy` + `faiss` (ou `usearch` pour performance)
|
||||
- [ ] Indexation : embedding par chunk de 512 tokens avec recouvrement
|
||||
- [ ] Recherche hybride : combinaison TF-IDF + similarité cosinus (RRF — Reciprocal Rank Fusion)
|
||||
- [ ] UI : toggle « Recherche sémantique » dans la barre de recherche
|
||||
- [ ] UI : score de similarité dans les résultats
|
||||
|
||||
### 71. Tableau de bord administrateur
|
||||
- **Effort :** 2 jours | **Impact :** 🟢
|
||||
- **Description :** Page d'administration dédiée avec monitoring en temps réel, gestion des utilisateurs avancée, et logs d'audit visualisables.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Widgets temps réel : CPU, mémoire, espace disque, requêtes/min (rafraîchissement SSE)
|
||||
- [ ] Gestion utilisateurs : tableau triable, création/édition/suppression, filtre par rôle
|
||||
- [ ] Logs d'audit : visualisation des 500 dernières entrées, filtre par utilisateur/action/date
|
||||
- [ ] Backup stats : graphique d'évolution (taille totale, nombre par vault, âge moyen)
|
||||
- [ ] Protection : accès restreint au rôle `admin` uniquement
|
||||
|
||||
### 72. API publique documentée — OpenAPI 3.1
|
||||
- **Effort :** 1-2 jours | **Impact :** 🟢
|
||||
- **Description :** Documentation OpenAPI complète et interactive (Swagger UI + Redoc) pour l'API REST, avec exemples de requêtes, descriptions en anglais, et schémas Pydantic exposés.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Audit des endpoints existants → compléter les docstrings manquants
|
||||
- [ ] Ajout de `response_model` sur tous les endpoints (40+ actuellement, ~15 sans modèle)
|
||||
- [ ] Exemples dans les schémas : `examples=[...]` pour les endpoints clés
|
||||
- [ ] Tagging des endpoints par catégorie (Files, Vaults, Search, Auth, AI, Backups)
|
||||
- [ ] Serveur mock : `prism` ou `openapi-generator` pour tests sans backend
|
||||
- [ ] Page de documentation intégrée : lien dans le menu header (« API »)
|
||||
|
||||
### 73. Synchronisation multi-appareils — Obsidian Sync compatible
|
||||
- **Effort :** 6-8 jours | **Impact :** 🟢
|
||||
- **Description :** Synchronisation des vaults entre plusieurs instances d'ObsiGate via un protocole de synchronisation décentralisé ou compatible Obsidian Sync. Alternative self-hosted à Obsidian Sync.
|
||||
- **Sous-tâches :**
|
||||
- [ ] Protocole : évaluation CRDT vs OT vs diff/patch pour fichiers markdown
|
||||
- [ ] Transport : WebSocket ou polling HTTPS avec compression
|
||||
- [ ] Merge strategy : LWW (Last Writer Wins) avec historique des deux versions
|
||||
- [ ] Détection de changements : hash SHA-256 par fichier, journal des modifications
|
||||
- [ ] UI : page « Synchronisation » avec statut par appareil, historique des syncs
|
||||
- [ ] Conflits : UI de résolution manuelle (diff côte à côte entre version locale et distante)
|
||||
- [ ] Chiffrement : optionnel, chiffrement AES-256-GCM avant transmission
|
||||
- [ ] Pairing : échange de clé publique + code QR pour appairage des appareils
|
||||
|
||||
---
|
||||
|
||||
## 📊 Résumé des efforts
|
||||
|
||||
| Priorité | Items | Effort total estimé |
|
||||
|---|---|---|
|
||||
| ✅ Complété | #1 → #57 | ~65 jours |
|
||||
| 🔵 P1 | #58 (Playwright) | 2-3 jours |
|
||||
| ⚪ P3 | #59 → #66, #74, #75 (10 items) | 29-39 jours |
|
||||
| ⚪ P4 | #67 → #73 (7 items) | 18-23 jours |
|
||||
| **Total restant** | **18 items** | **49-65 jours** |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Les items P3/P4 ne sont pas ordonnés par priorité interne — à raffiner selon les retours utilisateurs.
|
||||
- L'effort inclut le développement + tests unitaires + intégration CI, mais pas la documentation utilisateur.
|
||||
- Les items marqués 🟢 (nice-to-have) sont de bons candidats pour des contributions externes.
|
||||
- Le mode hors-ligne (#59) et l'i18n (#63) sont les P3 ayant le meilleur rapport effort/valeur.
|
||||
|
||||
Reference in New Issue
Block a user