44 KiB
44 KiB
ObsiGate — Roadmap
Version : 2.0.0-dev | Dernière mise à jour : 2026-06-05 Voir aussi CHANGELOG.md, AUDIT_TECHNIQUE.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
e2edans.gitea/workflows/ci.yml
- Installation Playwright + config (
⚪ 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éfautuser) - 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_subdansusers.json
- Configuration par provider :
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
- Spécification du format de plugin :
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.Docpartagé,Y.Textpour 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_accesspar 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
- Serveur WebSocket : endpoint
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.jsondansfrontend/locales/ - Fonction
t(key, fallback)→ détectionnavigator.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_iddansusers.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.jsonavec 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'arborescenceapi_file_view()(backend/main.py:2303) : UnicodeDecodeError sur lecture → retourneunsupported: truefrontend/js/viewer.js:377: sidata.unsupported→ affiche le message binaire + bouton downloadpdf_export.py: exporte du MD → PDF (WeasyPrint) — aucun rapport avec la lecture de PDF existants- Icone PDF déjà présente dans
EXT_ICONSfrontend (.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\fentre 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é parSEARCH_CONTENT_LIMIT).
- A3. Fallback pypdf : Si pymupdf non disponible (exception d'import), fallback automatique sur
pypdfavec un log warning. Code structuré avec une interface abstraite (PdfReaderprotocol) pour swap transparent.
B. Backend — Indexation des PDF (1 jour)
- B1. Ajout à
SUPPORTED_EXTENSIONS: Ajouter.pdfau set dansbackend/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.pdfet appelerextract_pdf_text()au lieu deread_text(). Le texte extrait alimente le pipeline TF-IDF existant — aucun changement nécessaire danssearch.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 deread_text(), détecter.pdfpar 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
htmlcontient un rendu texte simple (pas de markdown) : texte paginé ou première page formatée
- Extraire le texte avec
- C2. Nouvel endpoint
GET /api/file/{vault}/pdf/stream: Sert le fichier PDF brut avecContent-Type: application/pdfetContent-Disposition: inlinepour visualisation dans le navigateur. Supporte leRangeheader (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-textde Lucide) est déjà mappée dansEXT_ICONS(frontend/js/utils.js:129). Une fois.pdfdansSUPPORTED_EXTENSIONS, les PDFs apparaissent automatiquement dans l'arborescence via l'APIlist_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 typeapplication/pdfest 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, fonctionrenderFileContent()— ajouter une branche après la détectiondata.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 »
- Si
- 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:pdfcomme filtre pour limiter la recherche aux PDFs uniquement (complément aux filtrescreated:,modified:,size:déjà prévus #34).
G. Docker & Dépendances (0.5 jour)
- G1. requirements.txt : Ajouter
pymupdf>=1.24.0(sinonpypdf>=4.0en fallback). - G2. Dockerfile : Vérifier que l'image
python:3.11-slimdispose des libs système nécessaires pour pymupdf. Si besoin, ajouterlibmupdf-devou utiliserpypdf(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 etOBSIGATE_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
reportlabdans les fixtures de test test_pdf_indexing.py: vérifier qu'un PDF dans un vault est correctement indexé, que le texte est recherchable, queindex_document()gère l'extension.pdftest_pdf_api.py: endpointviewretourneis_pdf: true, endpointpdf/streamretourneapplication/pdf, endpointpdf/inforetourne 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 avecSEARCH_CONTENT_LIMITré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
pypdfqui 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-areaunique 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-wrappercontenant.tab-bar+main.content-area#content-area. Lecontent-areaest un conteneur monolithique dont le contenu est remplacé parrenderFile()à 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é aucontent-areaunique — il remplacearea.innerHTMLà chaque activation. - Renderer :
renderFile()(frontend/js/viewer.js:377) etrenderFileContent()(frontend/app.js:3182) écrivent directement danscontent-areaviadocument.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).
- DOM (
- 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
- État :
- A2. Création dynamique du DOM : Quand un 2e panneau est créé, le
.content-wrapperest transformé :- Le
.tab-baret#content-aread'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
- Le
- A3. Redimensionnement (resize) : Implémentation native sans bibliothèque.
- Mouse down sur
.pane-resize-handle→ capture du pointeur → calcul des ratios en pixels → mise à jourflex-basisen % - Double-clic sur la poignée → réinitialise à 50/50
- Persistance des ratios dans
localStoragepar clépane-layout-{N}panes - Contrainte : largeur/hauteur minimum de 200px par panneau
- Mouse down sur
- 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
TabManagerréférencedocument.getElementById("content-area")etdocument.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
TabManageractuel est un objet singleton avec état mutable partagé. Le scoper dans une factorycreateTabManager(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
- « Diviser à droite » (
- C2. Raccourcis clavier (non conflictuels avec le navigateur) :
Ctrl+Alt+\→ diviser le panneau actif à droiteCtrl+Alt+Shift+\→ diviser le panneau actif en basCtrl+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 lepaneIdsource +tabIddragoversur une.tab-bard'un autre panneau : accepter le drop, afficher un indicateurdrop: 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-bard'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)
- Sur une
- D3. Drag pour créer un panneau : Si l'onglet est dragué vers le bord droit ou inférieur du
content-areaactif, 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 proprecontent-area.renderFile()est déjà conçu pour écrire dans lecontent-areaglobal → nécessite un paramètretargetElementoptionnel (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 duPaneManagerpour é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) ougrid-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-handleexistante.- 4px de large, fond
var(--border), hovervar(--accent)avec transition 150ms - Curseur
col-resizeourow-resizeselon l'orientation - Barre fine centrale de 2px en
var(--accent)au survol
- 4px de large, fond
- 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
localStoragesous la cléobsigate-panes:{ "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-panesexiste → 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-panesdu 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.jsdé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+Ssauvegarde 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: noneoucontent-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 :
currentVaultetcurrentPathsont 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(stockagesubscription+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
- Souscription Push : endpoint
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
psutilou/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)
- Barre d'outils mobile flottante : bold, italic,
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-v2viasentence-transformers(Python) ou appel API externe - Stockage : index vectoriel avec
numpy+faiss(ouusearchpour 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
- Génération d'embeddings : modèle
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
adminuniquement
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_modelsur 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 :
prismouopenapi-generatorpour 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.