#15: Click backup item to preview content in side panel (100KB limit)
#16: Purge all backups per vault (red 'Vider' button)
#17: /api/backups/compress — gzip backups older than N days
#18: /api/backups/auto — backup files modified since N hours
#19: Restore button per backup in manager with timestamp extraction
+ Professional side-panel preview, action buttons on hover, vault grouping
- Config: max_backups_per_file (default 10) in config.json + UI field
- Auto-cleanup: _backup_file deletes oldest backups when limit exceeded
- New endpoints: GET /api/backups (list all), POST /api/backups/delete,
POST /api/backups/purge (by file or vault)
- New module: backup-manager.js - full-page view with filter, grouped by
vault/file, individual delete, per-file purge, stats
- Link from backup viewer footer: 'Gerer tous les backups'
- _get_backup_dir: now resolves relative path using the SPECIFIC vault's
directory (matching _backup_file exactly), not the first vault in index
- Removed _get_backup_root (unused after refactor)
- backups.js: global document click handler for #backup-close (same
pattern as Graph View's initGraphView)
- _backup_file: resolve relative backup path against vault parent (survives CWD changes)
- _get_backup_root: same resolution logic for listing
- backups.js: global document-level capture-phase click handler for .backup-close-btn
(works regardless of innerHTML replacements, text node targets, or event bubbling)
- Vault home (#12): recursive file listing (rglob), rel_dir in metadata,
'recursif' badge, updated roadmap description
- Backup viewer: fix close button (inline onclick + event delegation
fallback for text nodes), improve error messages
- Backup API: remove response_model to prevent Pydantic 500 errors,
add try/except + logging on backups/diff/restore endpoints,
per-entry error handling in _list_backup_files,
encoding safety (errors='replace') on read_text
- AI status: call /api/ai/status at startup in _initAIStatus
instead of waiting for user click on sync badge
Problème: l'indicateur affichait 'IA non configurée' alors que les
actions AI fonctionnaient.
Causes:
1. Deux endpoints /api/ai/status en conflit (main.py + ai_routes.py)
2. ai_routes.py lisait PROVIDERS évalué à l'import (os.getenv figé)
3. Cache côté frontend (_aiStatusChecked) empêchait de re-vérifier
Fixes:
- Suppression de l'endpoint redondant dans main.py
- ai_routes.py: lecture os.getenv au runtime (pas depuis PROVIDERS)
- sync.js: _checkAIStatus() appelée à chaque ouverture du panneau
L'étape 'Building inverted index...' au démarrage bloquait l'event loop
asyncio, empêchant le serveur de répondre aux requêtes HTTP (navigation,
refresh) jusqu'à la fin de l'indexation.
- init_inverted_index() exécuté dans ThreadPoolExecutor via run_in_executor
- get_inverted_index() ne fait plus de rebuild auto bloquant
- Le serveur répond immédiatement, même pendant l'indexation
Point 11a — Indicateur AI Actif:
- Badge sync flashe en blanc pendant traitement AI (animation CSS)
- Événements ai-processing-start/end dans ai.js
- Section statut IA dans panneau sync (configurée/non configurée/en cours)
- Endpoint GET /api/ai/status vérifiant les clés API configurées
Point 11b — Page d'accueil de voute:
- Endpoint GET /api/vault/{vault}/files?dir=... (trié par mtime)
- Module vaulthome.js: affichage style résultats de recherche
- Breadcrumb navigable, mise à jour auto sur clic dossier dans tree
- Déclenché auto quand un vault spécifique est sélectionné
- Backend: 3 nouveaux endpoints
- GET /api/file/{vault}/backups — liste les backups horodatés
- GET /api/file/{vault}/diff — diff unifié entre versions
- POST /api/file/{vault}/restore — restaure une version (backup auto avant)
- Frontend: module backups.js — UI modale avec:
- Sélecteur gauche (backup) / droite (version actuelle ou autre backup)
- Diff coloré: vert pour ajouts, rouge pour suppressions
- Bouton Restaurer avec confirmation
- Menu contextuel: clic droit fichier → 'Backups / Versions'
- CSS: styles complets pour la modale, toolbar, table de diff
- ROADMAP: point 6 marqué comme complété
- Backend: nouvel endpoint POST /api/move/{vault_name} pour déplacer
fichiers et dossiers vers un autre répertoire parent
- Frontend: module dragdrop.js avec délégation d'événements sur le
vault-tree (dragstart, dragover, dragleave, drop)
- CSS: styles pour drag-source, drag-valid-target, drag-over
- Protections: pas de drop sur soi-même, pas dans un enfant,
pas de cross-vault
- Mise à jour de l'index et broadcast SSE après move
- ROADMAP: portion 5 marquée comme complétée
Replace double-nested stem loop (stems × tokens) with single-pass
stem frequency map. For 100 unique tokens per file: 10,000 iterations
→ 100 iterations per file. Critical for large vaults.
Backend (main.py):
- GraphNode: added tags, incoming_count, outgoing_count
- GraphEdge: added 'backlink' relation
- GraphResponse: added 'scope' field
- api_graph: scope=full|directory, tag= filter, backlinks
- Full-vault tree walk with configurable depth 0-3
- Tag index from in-memory file index for fast filtering
- Incoming/outgoing link count per node
Frontend (graph.js + index.html):
- Theme-adaptive colors via CSS custom properties
- Depth slider (0-3) with live reload
- Full-vault toggle button (🌐 Tout / 📁 Dossier)
- Search input with tag filtering + visual highlighting
- Tooltip on hover (name, path, tags, link counts)
- Backlink edges rendered in red dashed
- Node size proportional to link count
- Larger modal (1000px, 85vh)
Add French snowball stemmer to tokenization pipeline:
- Index both original tokens AND their stems in InvertedIndex
- Query terms are also stemmed before lookup
- Stemmed forms accumulate TF from all original forms
- Lazy-init singleton pattern for stemmer
The login() function used 'request: LoginRequest' which shadowed
FastAPI's Starlette Request object. Request.client was accessed on
the LoginRequest Pydantic model instead of the HTTP request, causing
AttributeError: 'LoginRequest' object has no attribute 'client'.
Fix: rename the Pydantic parameter to 'body' and add explicit
'request: Request' for IP extraction and rate limiting.
- Add .dockerignore to exclude .git, __pycache__, docs, etc. from Docker context
- Create .env.example template with documented env vars
- Move OBSIGATE_ADMIN_PASSWORD from docker-compose.yml to env_file: .env
- Add .env.* to .gitignore (excluding .env.example)
- Enable GZipMiddleware for ~70% bandwidth reduction on text responses
- Add Cache-Control: immutable for /static/ assets
- Update ROADMAP: mark all 4 quick wins as done, add audit findings
- Add comprehensive technical audit report (AUDIT_TECHNIQUE_2026-05-27.md)
Remove the old HTML-based PDF download endpoint in favor of the new
WeasyPrint-based one, and replace the generic "Télécharger" button
in popout.html with a dedicated .md download and a new PDF button.
Also remove the unused generic download button from the main file view.
- Implement dark/light theme toggle with persistent preference via
localStorage
- Add a sticky toolbar with theme toggle, Markdown export, and PDF
export buttons
- Update bookmark button to reflect current state with visual feedback
- Introduce CSS custom properties for theming and responsive layout
improvements
Register a hook with the indexer so that file add/remove events
incrementally maintain the inverted index, removing the need for
periodic staleness checks and cooldowns. Rebuild the index once on
startup via init_inverted_index().