137 KiB
Changelog — ObsiGate
Toutes les modifications notables d'ObsiGate sont documentées dans ce fichier.
Format basé sur Keep a Changelog, et Semantic Versioning.
En cours de développement : les changements à venir sont listés dans la section Unreleased. La dernière version livrée est 2.22.1.
[Unreleased]
[2.22.1] — 2026-09-23
Corrigé
- BUG-073 — mobile, fin de contenu masquée. La barre de navigation fixe du bas
(
#mobile-toolbar, 64 px + safe-area) recouvrait le bas de tous les documents et pages en vue mobile (≤ 768 px) : les dernières lignes restaient définitivement sous la barre. Cause : la règle de clearance du bloc mobile ciblait.main-layout, classe absente deindex.html(le vrai conteneur est.main-body) — sélecteur mort, aucun dégagement réservé. Correctif : cible.main-body(padding-bottom: calc(64px + env(safe-area-inset-bottom, 0))), suppression de la règle sœur morte.editor-modal.active ~ .main-layout, reset du dégagement en mode lecture (barre masquée) etbody.np-active .content-arearamené à76 px(le dégagement dock, sans double comptage avec.main-body). Tests :tests/frontend/mobile-toolbar.test.mjs(7, au CI) et E2Etests/e2e/mobile-toolbar.spec.js(2, projetchromium-mobile).
[2.22.0] — 2026-09-23
Ajouté
- #113 — avatar utilisateur. La section « Profil » des Configurations permet de
choisir / importer une image (PNG, JPG, WEBP, 8 Mo max) : elle est recadrée en
carré 256 px côté client, validée côté serveur (data-URL + octets magiques, sans SVG)
et persistée sur le compte (
avatardansdata/users.json, exposé parGET/PATCH /api/auth/meet la réponse de login). L'image s'affiche dans le cercle du profil en bas de la sidebar (initiales en repli) et peut être supprimée. UI : aperçu circulaire avec overlay caméra au survol, boutons Choisir / Supprimer, erreurs en ligne. Clés i18n FR/EN.
Modifié
- #113 — ordre des sections Configurations. La TOC et la page sont réorganisées
dans un ordre naturel et parfaitement synchronisées : Profil en premier,
puis Sécurité, Thèmes, Recherche, Historique récent, Tags, Fichiers cachés,
Synchronisation, Backend, Diagnostics, IA, Sources connectées, Clés API & MCP,
Push, Webhooks, Partages publics, Plugins et À propos en dernier. Les ancres
cfg-tagsetcfg-partages-publicssont désormais portées par leur<section>(cible de défilement = haut de section). Garde-fous : test statiquetests/frontend/settings-order-avatar.test.mjs(ordre TOC = page, pas d'ancre morte).
[2.21.0] — 2026-09-23
Ajouté
- #112 — section compte en bas de la sidebar. L'identité (avatar avec initiales, nom, rôle) et la déconnexion sont regroupées dans un bloc épinglé au bas de la barre latérale, comme sur les sites professionnels ; un clic sur l'identité ouvre le Profil. Nouvelle clef i18n FR/EN pour le rôle.
- #112 — pellicule d'images défilable. La molette de la souris fait défiler horizontalement la bande de miniatures et deux flèches translucides sur les côtés (révélées au survol) remplissent la même fonction.
Modifié
- #112 — en-tête allégé. La version quitte le header pour le menu Options (ligne « Version ») ; le bouton de déconnexion et le nom de l'utilisateur sont retirés du header au profit de la section compte de la sidebar. Le header droit ne conserve que l'indicateur hors-ligne, le contexte vault et le bouton Options.
[2.20.0] — 2026-09-23
Ajouté
- #111 — visionneuse d'images : flèches latérales translucides. Deux boutons
superposés le long des bords du cadre passent à l'image précédente/suivante ;
quasi invisibles au repos, ils se révèlent au survol (et restent visibles sur
les appareils tactiles). Un compteur
n / totalcomplète la barre d'outils.
Modifié
- #111 — navigation d'images fluide. Changer d'image se fait désormais en
place (
showSiblingremplace lesrcdu<img>) : plus de rechargement de la vue ni de nouvel appel/api/browseà chaque flèche, même dans un dossier contenant beaucoup d'images. La liste du dossier est mise en cache (TTL court) et les images voisines sont préchargées ; la pellicule de miniatures reste visible et la vignette active est simplement re-marquée. - #111 — image toujours ajustée au cadre. La visionneuse occupe tout l'espace disponible (zone de contenu en flex, sans défilement de page) et l'image est systématiquement redimensionnée dans son cadre, y compris panneau « Métadonnées » ouvert.
[2.19.2] — 2026-09-23
Ajouté
- Lanceur E2E Windows/PowerShell :
scripts/run-e2e-local.ps1(+ script npmtest:e2e:ps) reproduit localement le job CIe2e(uvicorn natif, auth désactivée, fixtures TestVault/TestDir, port 2029, projetchromium-desktop) sans dépendre debash— indispensable sur les postes Windows où WSL ne démarre pas et où git-bash est bloqué par une politique de contrôle d'application. Le scriptbashreste la référence pour la CI/Linux.
Corrigé
- BUG-072 — visionneuse d'images : le plein écran (lightbox) et le panneau « Métadonnées » sont désormais conservés lors de la navigation entre images (flèches ←/→ et clic sur la pellicule) ; auparavant chaque changement d'image recréait la visionneuse et perdait ces états. Le panneau de métadonnées s'affiche maintenant en barre latérale à droite de l'image (au lieu d'une bande sous la pellicule) et reste visible en plein écran.
[2.19.1] — 2026-09-23
Corrigé
- #110 — lisibilité et positionnement du lecteur média : en mode mobile, la
barre audio passe en grille (barre de progression pleine largeur sur sa propre
ligne) et les actions secondaires (précédent/suivant/agrandir/volume) sont
masquées pour éviter tout chevauchement ; le volume est aussi masqué sur les
écrans intermédiaires en desktop, et
overflow: hiddenempêche le débordement hors de la pilule. La mini-fenêtre vidéo n'est plus ré-aimantée sur les bords : elle se déplace et se redimensionne librement (position absolue mémorisée, centre autorisé), le glisser fonctionnant désormais depuis n'importe quel point de la fenêtre (boutons/curseurs/poignée exclus, seuil de 4 px pour préserver les contrôles natifs de la vidéo).
[2.19.0] — 2026-09-23
Ajouté
- #110 — Lecteur média persistant « Now Playing » : les fichiers audio et
vidéo continuent de jouer pendant la navigation grâce à un contrôleur global
frontend/js/now-playing.jsqui possède un seul élément<audio>/<video>téléporté entre la vue inline (onglet/panneau) et un dock flottant (<body>), sans interruption de lecture. Dock desktop : pilule verre dépoli (artwork, titre, voûte, play/pause, précédent/suivant, barre de progression, volume, retour au média, agrandir, fermer). Mini-fenêtre vidéo flottante déplaçable/redimensionnable avec aimantation aux coins et bouton Picture-in-Picture natif. Mode mobile : mini-player fixé au-dessus de la barre d'outils (safe-areaviewport-fit=cover). Intégration Media Session (écran verrouillé, touches matérielles, Windows SMTC), panneau étendu façon « Now Playing », lecture auto du média suivant/précédent du dossier, reprise après rechargement, notification quand l'onglet est fermé pendant la lecture. Fiche : docs/features/media-viewers-109.md (§ Now Playing, #110).
[2.18.0] — 2026-09-23
Ajouté
- #109 — Support audio & vidéo (lecteurs HTML5 intégrés) : les fichiers audio
(
.mp3 .m4a .aac .wav .ogg .oga .opus .flac) et vidéo (.mp4 .webm .mov .m4v) apparaissent désormais dans l'arborescence et l'index (nom/taille/date uniquement, contenu jamais lu, intégrés àSUPPORTED_EXTENSIONSviamedia_types.py). Nouvel endpointGET /api/media/{vault}?path=…avec support HTTPRange/206 Partial Content(Content-Range,Accept-Ranges,416sur plage invalide,413au-delà deOBSIGATE_MEDIA_MAX_INLINE_MB, défaut 500 Mo) — le helper Range depdf/streama été extrait en_stream_file_with_range()et est partagé.api_file_view()exposeis_audio/is_video/stream_url/media_mimeavant toute lecture texte. Frontend : lecteur audio dédié (artwork, durée vialoadedmetadata) et lecteur vidéo (playsinline, scène noire letterboxée), icônes Lucideaudio-lines/video, pause à la réinitialisation de la vue, repli téléchargement si le codec n'est pas lisible ou le fichier trop volumineux. Les médias sont exclus du contexte textuel BooksLM et du cache hors-ligne du service worker. Fiche : docs/features/media-viewers-109.md.
[2.17.0] — 2026-09-23
Ajouté
- #108 — Support complet des images (arborescence, visionneuse, indexation) :
les images (
.png .jpg .jpeg .gif .svg .webp .bmp .ico) apparaissent désormais dans l'arborescence et l'index comme les autres fichiers — nom/taille/date uniquement, jamais les octets (contenu indexé vide, TF-IDF préservé). Nouveau module partagébackend/media_types.py(extensions + MIME, socle réutilisé par #109). Visionneuse dédiée : zoom molette 0,1×–8×, pan au glisser, double-clic pour réinitialiser, boutons +/−/reset et badge de zoom, navigation ←/→ entre les images du dossier avec pellicule de miniatures, panneau métadonnées (dimensions, taille, type, chemin, date), lightbox plein écran, « Ouvrir l'original » et téléchargement. EndpointGET /api/media/{vault}/thumb(miniature WebP 256 px, cache disque invalidé par mtime, repli sur l'original pour le SVG,pillow>=10.0). Filtreext:png/ext:jpgopérationnel ; compteurs d'images séparés dans/api/dashboard(image_count/total_images). Fiche : docs/features/image-support.md.
Corrigé
- #108-B1 — Affichage isolé d'une image : le
<img>généré parapi_file_view()pointait vers/api/file/{vault}/raw(qui renvoie du JSON) au lieu de/api/image/{vault}(octets + MIME correct). Corrigé côté backend et dansviewer.js(bouton Plein écran), avec encodage d'URL des chemins. - #108-B3 — XSS via SVG :
/api/image(et le repli miniatures) poseContent-Security-Policy: sandboxsur les SVG ouverts directement, pour empêcher l'exécution du JavaScript embarqué ; le middleware n'écrase plus une politique stricte posée par une route.
[2.16.6] — 2026-09-22
Ajouté
- Guides d'utilisation
docs/GUIDES/: nouvel index + 10 guides FR (prise en main, recherche/PDF/Excalidraw, assistant IA & Forge, collaboration temps réel, PWA & hors-ligne, API REST, serveur MCP, authentification & sécurité, déploiement Docker, desktop Tauri). Le guide MCP est déplacé dansdocs/GUIDES/MCP.md;docs/MCP_GUIDE.mddevient une page de redirection.
Modifié
- README.md / README.fr.md : capture d'écran réelle de l'application en tête
(remplace l'illustration ASCII) ; un emoji sur chaque entrée de la table des
matières ; nouvelle section « Guides » avec liens vers
docs/GUIDES/; renvois vers les guides depuis les sections API, Recherche, Sécurité, Desktop et Collaboration.
[2.16.5] — 2026-09-22
Corrigé
- BUG-071 (complément) - spec E2E mobile de la page Configurations :
tests/e2e/config-mobile.spec.js(nouveau, projetchromium-mobile, ignoré enchromium-desktopcommemobile-editor.spec.js) : le hamburger révèle le sommaire, le choix d'une section y défile + lien actif + repli auto, aucun débordement horizontal à 393px. Vérifié en local contre l'instance de test (port 2029, auth désactivée) : 3/3.
[2.16.4] — 2026-09-22
Corrigé
- BUG-071 - Page « Configurations » inutilisable en mode mobile : trois
causes. (1) Le sommaire (
#config-nav) partageait la règle.help-navqui le masque sous 768px, mais — contrairement au Guide — la modale n'avait aucun bouton pour l'afficher : aucun moyen d'atteindre une section. Nouvel hamburger#config-hamburgerdans l'en-tête (même traitement.help-hamburgerque le Guide, libellé traduitconfig.toc_toggleFR/EN). (2) Les liens du sommaire étaient des ancres brutes sans JS :config.jsles intercepte désormais (défilement doux vers la section dans la modale, lien actif, repli automatique du sommaire sur mobile, réinit à l'ouverture). (3) Les grilles 2 colonnes (fournisseur/modèle IA, clé/modèle par fournisseur), les rangées d'ajout à largeurs fixes (jetons, webhooks) et les lignes webhook/jeton/partage en flex une ligne débordaient en 360px : bloc CSS mobile scopé#config-modal(1 colonne, wrap, largeurs inline neutralisées, cibles tactiles 44px, sommaire plafonné à 46vh).data-i18n-attraccepte désormais plusieurs pairesattr:cléséparées par;(titre + aria-label traduits). Tests :tests/frontend/config-mobile.test.mjs(nouveau, 11 — hamburger, i18n, câblage JS, CSS mobile, garde-fou ancres mortes façon BUG-067), enregistré dans le CI.
[2.16.3] — 2026-09-22
Corrigé
- BUG-070 - Activation clé physique WebAuthn impossible (« Validation du
credential WebAuthn échouée ») : deux causes. (1) Les valeurs par défaut
(
rp_id localhost, origineshttp://localhostsans port) rejetaient toute URL réelle — logs :Unexpected client data origin "http://localhost:2020", expected one of ['http://localhost'].rp_id/origines sont désormais dérivés de la requête (hôte exact, port inclus ;X-Forwarded-Host/ProtosiOBSIGATE_TRUST_PROXY=true), la config explicite restant prioritaire (backend/auth/webauthn_mfa.py::resolve_relying_party, appliqué aux 4 endpoints d'enregistrement et de login). (2) Challenge à usage unique fragile au double-clic/retry (challenge was not expected) : les 5 derniers challenges sont conservés et la vérification accepte le challenge correspondant à la cérémonie en cours..env.exampledocumente le nouveau comportement. Vérifié au navigateur avec authentificateur virtuel (Playwright CDP, instance Docker) : enregistrement 200 + clé listée, puis clé de test retirée. Tests :tests/test_webauthn.py(+8 : résolution RP, forwarded, retry, roundtrip sans config).
[2.16.2] — 2026-09-22
Corrigé
- BUG-069 - Login 2FA bloqué sans erreur : après user+mot de passe corrects
sur un compte avec 2FA, la page de login restait affichée sans erreur et le
challenge MFA n'apparaissait jamais. Cause :
showMfaChallenge(frontend/js/auth.js) montait le challenge dans.login-box, inexistant dansindex.html(marquage réel :#login-screen > .login-card) →returnsilencieux. Correctif : montage dans.login-card(repli#login-screen) + erreur visible (mfa.challenge_unavailable, FR/EN) au lieu d'un retour silencieux si le point de montage manque. Vérifié de bout en bout au navigateur (Playwright, instance Docker) : challenge affiché, code erroné → erreur, code valide → connecté. Tests :tests/frontend/mfa-settings.test.mjs(+2 contrôles d'ancrage DOM).
[2.16.1] — 2026-09-22
Corrigé
- BUG-068 - Configuration : section « 🔒 Sécurité du compte » inachevée :
boutons
config-btn-primary/config-btn-dangerdéfinis depuis les variables du thème (frontend/style.css) ; QR code TOTP généré en local par le backend (POST /api/auth/mfa/totp/setup→qr_data_url, SVGdata:viasegno,backend/requirements.txt) au lieu de l'image tierce bloquée par la CSP (img-src 'self' data: blob:, secret TOTP exposé) ; codes de récupération affichés aussi à la première activation WebAuthn ; carte « Mot de passe » (changement viaPOST /api/auth/change-password) et échappement des libellés de clés WebAuthn. Tests :tests/test_mfa.py::test_mfa_setup_returns_local_qr_data_url,tests/frontend/mfa-settings.test.mjs(nouveau, 9 contrôles).
[2.16.0] — 2026-09-22
Modifié
- #86 - Optimisation globale des performances (phase 3) : ferme les deux derniers
points de la phase 3 (recherche via inverted index, PDF lazy et caps regex déjà livrés
via BUG-033/BUG-040/BUG-025). Scan différentiel :
_scan_vaultréutilise les entrées inchangées (size+modified) d'un snapshot précédent — seulsos.walk+stattournent à chaque rebuild (build_index,reload_single_vault). Extraction excalidraw différée : le scan ne lit plus les.excalidraw/.excalidraw.md(flagexcalidraw_text_pending),enrich_pdf_texts()extrait leur texte après index comme pour les PDF. Garde-fouMAX_REPLACE_FILE_BYTES(5 Mio) surreplace_in_files. Tests :tests/test_perf_phase3.py(9). Voir docs/features/perf-phase3-86.md.
[2.15.0] — 2026-09-22
Ajouté
- #107 - Configuration : gestion des clés API & MCP — nouvelle section
« 🔑 Clés API & MCP » dans le panneau de configuration : création, liste
(créée / expire / dernière utilisation) et révocation de jetons longue
durée utilisables aussi bien sur l'API REST que sur le serveur MCP
(
/mcp) — un seul et même jeton Bearer pour les deux. Choix d'expiration à la création : 1 jour, 1 mois, 6 mois, 1 an, sans fin. Le secret n'est affiché qu'une fois (jamais persisté en clair,data/api_tokens.jsonne contient que les métadonnées) ; révocation immédiate des deux côtés, plafond 50 clés par utilisateur, isolation par utilisateur, auditconfig_change. Corrigé au passage : le store de révocation (revoked_tokens.json) bornait toute entrée à 7 jours — un jeton longue durée révoqué « reprenait vie » après purge ; il est désormais calé sur l'expiration réelle du jeton. Voir docs/features/api-mcp-tokens-107.md.
[2.14.1] — 2026-09-22
[2.14.0] — 2026-09-19
Ajouté
- #106 - Assistant IA : actions instantanées contextuelles + catalogue de prompts :
la zone d'accueil remplace les 3 suggestions statiques par des actions suggérées
selon le contexte détecté (sélection dans l'éditeur → concis/corriger/expliquer ;
fichier de code → expliquer/bugs/tests ; 2+ docs → fusionner/comparer/frictions ;
1 doc → résumé 3 points/checklist/frontmatter ; répertoire ou général sinon), avec
badge de contexte dans l'en-tête. Un bouton « Toutes les actions » ouvre un
tiroir catalogue (recherche instantanée, 6 catégories, 25 actions i18n FR/EN).
Nouveau prompt « Générer le frontmatter YAML » au format complet du vault
(titre, auteur, dates ISO-8601, tags, aliases, booléens, NomDeVoute, Description)
et « Mettre à jour le frontmatter » (conserve les champs existants, actualise
modification_date, recalcule tags/aliases/Description, complète les manquants) — tous deux basculent en mode agent et appliquent le résultat via la carte de confirmation. Détail : features/ai-quick-actions.md.
[2.13.1] — 2026-09-18
[2.13.0] — 2026-09-18
Ajouté
- #105 - Guide d'utilisation : audit de couverture, téléchargement Markdown/PDF,
section Architecture : le guide intégré est aligné sur l'application réelle après
~35 features livrées. Huit nouvelles sections — 🏗️ Architecture (diagramme Mermaid
des grandes composantes : clients SPA/PWA/Tauri → serveur FastAPI REST, index de
recherche, rendu markdown, IA, MCP, WebSocket, webhooks → vaults,
data/*.json, backups), 📊 Diagrammes (Mermaid : zoom, plein écran, copie SVG/code, thèmes), ⭐ Bibliothèque (signets, recherches sauvegardées, backlinks & graphe, conflits Syncthing, pièces jointes), 📴 Hors-ligne (PWA, file d'attente IndexedDB, replay, watcher), 👥 Collaboration (Yjs/CRDT, curseurs awareness), 🖥️ Desktop (Tauri, updater signé, assistant premier lancement), 🔌 API (OpenAPI 3.1 :/docs,/redoc,/api,/openapi.json, auth Bearer/cookie, serveur MCP, automatisation) et 🌍 Multilingue — plus des compléments dans les sections existantes (recherche sémantique hybride, notifications web push, export PDF, export HTML/ePub/ZIP, anti-doublons d'upload, vue multi-panneaux, MFA TOTP/WebAuthn, tableau de bord admin). NouveauGET /api/guide/download?format=md|pdf&lang=fr|en(tag OpenAPI « Guide ») : le document est généré depuis la modale d'aide réelle et les locales, il reflète donc exactement le guide affiché, dans la langue de l'utilisateur ; boutons Markdown et PDF ajoutés dans l'en-tête du guide. En desktop (Tauri,body.desktop-mode) et sur grand écran web, le guide s'élargit (conteneur jusqu'à 1760 px, contenu 1280–1440 px) pour une lecture confortable. Source de vérité du nouveau contenu :scripts/guide_content.py(locales générées, jamais éditées à la main). Tests :tests/test_guide.py(11).
Modifié (ajustements retour utilisateur sur #105)
- Boutons de téléchargement du guide : icônes seules (plus de libellé « Markdown »/ « PDF »), tooltip i18n explicite sur chaque bouton.
- PDF du guide : le bloc Mermaid de la section Architecture est converti en diagramme
rendu (PNG pré-rendu commité via
scripts/build_guide_diagrams.py+scripts/render_guide_diagram.mjs, inséré pardiagram_png_for()), au lieu du code brut ; le Markdown garde le fenced```mermaid(copiable). - PDF du guide : emoji rendus en couleur au lieu de rectangles —
fonts-noto-color-emojiajouté à l'image Docker et"Noto Color Emoji"en fin de pile de polices du PDF (la TOC était correcte car elle utilise DejaVu, qui n'a pas les emoji pleine chasse).
Corrigé
- BUG-067 - Guide : entrée « 📱 Mobile » morte : le sommaire pointait vers
#help-mobile-editor, section inexistante (l'édition mobile n'était qu'un<h3>de la section Édition). Le bloc est devenu une section dédiée ancrée ; l'ancre#help-ia(également morte) a été corrigée en#help-aiet un attributdata-i18n-placeholderdupliqué nettoyé. Garde-fou :test_guide_nav_has_no_dead_anchors.
[2.12.2] — 2026-09-18
Corrigé
- BUG-066 — Configuration : icônes manquantes dans la table des matières :
les entrées « Fichiers cachés » et « Partages publics » du sommaire de la page de
configuration n'affichaient pas d'icône (les titres de section, eux, en avaient une).
Les libellés i18n
config.section_hidden(🗂️) etconfig.section_shares(📤) sont alignés sur leurs titres de section, en FR et EN. Test de non-régression ajouté danstests/frontend/unit.test.mjs(toutes les entrées du sommaire doivent porter une icône dans les deux langues).
[2.12.1] — 2026-09-18
[2.12.0] — 2026-09-18
Ajouté
- #104 - Configuration : redesign UI de la section « 🤖 Clés API Intelligence Artificielle » :
la longue liste plate de champs devient une interface structurée et dépliable —
- En-tête de section : titre + sous-titre explicatifs et une barre de recherche
(
#cfg-ai-search) pour filtrer les fournisseurs (normalisée, insensible à la casse et aux accents), avec état vide « Aucun fournisseur ne correspond ». - Carte « Configuration par défaut » visuellement distincte : grille 2 colonnes
fournisseur / modèle par défaut, et capacités du modèle rendues en badges colorés
(nouveau
renderCapabilityBadges()dansfrontend/js/ai.js, uniquement les capacités actives) au lieu des cases à cocher. - Fournisseurs d'API en cartes dépliables (rendu dynamique depuis
AI_PROVIDER_NAMESpar_renderAIProviderCards(),frontend/js/config.js) : état replié = logo (initiale), nom, badge de statut (« Configuré » vert / « Non configuré » gris) et corbeille discrète pour supprimer la clé (visible uniquement si configurée, confirmation conservée) ; état déplié = libellés au-dessus des champs, API key à 60 % / modèle à 40 % (grille3fr 2fr). Les boutons « Configuré/Supprimer » redondants à l'intérieur des champs disparaissent — le statut vit désormais uniquement dans l'en-tête de la carte. - Barre d'actions : « Sauvegarder » (primaire) et « Tester » (outline) dans un footer
sticky en bas de section, toujours accessible pendant le défilement, avec le statut de
test à côté.
Styles
.ai-keys-*/.ai-provider-*en variables CSS (profondeur fond/carte, espacement généreux, chevron animé, focus visible, responsive 1 colonne < 600 px) ; textes i18n FR/EN (config.ai_*). Les ID d'éléments (cfg-<provider>-key/model/badge/delete) et le comportement de sauvegarde/test/suppression restent inchangés (rétrocompatible pickers IA). Tests :tests/frontend/config-ai-keys.test.mjs(7 — rendu, badges, bascule, filtre, save, suppression, badges de capacités).
- En-tête de section : titre + sous-titre explicatifs et une barre de recherche
(
[2.11.6] — 2026-09-18
Corrigé
- BUG-064 - Éditeur Excalidraw : le diagramme ne s'affiche jamais (canvas vide) — deux
causes cumulées :
- La feuille de style d'Excalidraw n'était jamais chargée (
@excalidraw/excalidrawexige un import CSS explicite). Sans elle, l'éditeur est non stylisé et.excalidrawn'a aucune hauteur fixe : la boucle de redimensionnement d'Excalidraw fait grossir le canvas jusqu'au plafond codé en dur de2^25(33 554 432 px), que le navigateur ne peut pas dessiner → scène blanche. Correctifs :<link>vers…/@excalidraw/[email protected]/dist/prod/index.cssdansfrontend/excalidraw-editor.htmlet ajout dehttps://esm.shàstyle-srcde la CSP (backend/main.py). appState.collaboratorsest uneMapsérialisée en objet JSON ({}) par l'app Excalidraw / le plugin Obsidian ; réinjectée viainitialData, Excalidraw 0.18 appelait.forEach()dessus et plantait (e.appState.collaborators.forEach is not a function).sanitizeAppState()la reconvertit enMapet écarte la géométrie de viewport importée (width,height,offsetLeft,offsetTop) pour les deux formats (.excalidrawet.excalidraw.md). Tests de non-régression :tests/frontend/excalidraw-viewer.test.mjs(CSS lié + CSP),tests/test_security_hardening.py::TestCspExcalidrawStylesheet,tests/e2e/excalidraw.spec.js(hauteur de canvas bornée) + fixturestest_vault/diagram-app-export.excalidraw.
- La feuille de style d'Excalidraw n'était jamais chargée (
- BUG-064 (complément) - Éditeur Excalidraw : pleine largeur quand la navigation est masquée :
la règle
.sidebar.hidden ~ .content-wrapper .content-area { max-width: 1200px }(colonne de lecture centrée) s'appliquait aussi au viewer Excalidraw. Ajout de.content-area:has(iframe[src*="excalidraw-editor.html"])enmax-width: none; margin: 0, comme pour les viewers PDF/image (BUG-062). Fichier :frontend/style.css. - BUG-065 - Excalidraw : l'auto-save rechargeait la page en pleine édition : chaque
modification déclenchait, 2 s plus tard, un
PUT /api/file/…/save; l'écriture émettaitindex_updated(SSE) qui re-rendait la vue et recréait l'iframe — un refresh visible qui interrompait le dessin. L'auto-save est supprimée (frontend/js/excalidraw-viewer.js) : sauvegarde explicite par le bouton « 💾 Save » ouCtrl+S. En complément,reloadExternalWritene re-rend plus la vue quand un iframe Excalidraw est déjà ouvert sur le fichier (iframe[data-excalidraw-*]), et le badge « Modified » ne se déclenche plus sur les changements d'appState(resize, zoom) mais uniquement sur le contenu (signature des éléments). - #78 (complément) - Bouton plein écran pour les diagrammes Excalidraw : nouveau bouton
#btn-fullscreendans la barre d'outils de l'éditeur (frontend/excalidraw-editor.html) qui bascule le mode plein écran natif ; l'iframe est créée avecallow="fullscreen" allowfullscreen(frontend/js/excalidraw-viewer.js). Fichiers :frontend/excalidraw-editor.html,frontend/js/excalidraw-viewer.js,tests/frontend/excalidraw-viewer.test.mjs. - #78 (complément) - Barre d'outils Excalidraw en icônes, verticale à droite : les boutons
Save / PNG / SVG / plein écran passent en icônes seules (34×34 px) dans une colonne
collée au bord droit (
right: 0), débutant à45%de la hauteur, empilée verticalement, avec infobulles etaria-label. L'icône du bouton Save devient une coche après une sauvegarde réussie. Fichier :frontend/excalidraw-editor.html.
[2.11.5] — 2026-09-17
Corrigé
- BUG-063 (complément) — Viewer PDF : la table des matières ne déplaçait toujours pas la
page : le premier correctif réassignait
iframe.srcavec le seul fragment#page=N, ce qui ne change que le fragment → navigation same-document que le lecteur PDF natif ignore (il n'applique#page=Nqu'au chargement). Diagnostic en Chrome headful par comparaison de captures.navigatePdfToPage()ajoute désormais un paramètre de query horodaté (&_pdfpage=<ts>#page=N) pour forcer un vrai rechargement de l'iframe. Fichiers :frontend/js/viewer.js,tests/frontend/pdf-viewer.test.mjs,tests/e2e/pdf-viewer.spec.js.
[2.11.4] — 2026-09-17
Corrigé
- BUG-061 — Assistant IA : le bouton « Plein écran » n'agrandissait plus le panneau : la
largeur du panneau est écrite en style inline par la poignée de redimensionnement (et par la
largeur persistée en
localStorage) ; cet inline l'emportait sur la règle.bookslm-panel.fullscreen { width: 100vw }, donc le panneau restait à sa largeur courante. La règle plein écran est désormais prioritaire (!important). Fichier :frontend/style.css. - BUG-062 — Viewer PDF : largeur incomplète quand la navigation est masquée : la règle de
colonne de lecture centrée (
.sidebar.hidden … { max-width: 1200px }) s'appliquait aussi aux viewers plein cadre. Les conteneurs PDF et image sont maintenant exemptés (:has(.pdf-viewer-container)/:has(.image-viewer-container)→max-width: none). Fichier :frontend/style.css. - BUG-063 — Viewer PDF : la table des matières ne naviguait pas : les liens faisaient
contentWindow.location.hash = 'page=N', mais le lecteur PDF natif vit dans une fenêtreabout:blanket l'affectation n'atteignait jamais le document. Nouveau helpernavigatePdfToPage()qui recharge l'iframe avec le fragment#page=N; les entrées portent undata-pageet sont câblées par des écouteurs (plus d'onclickinline). Fichiers :frontend/js/viewer.js,frontend/style.css. - Tests :
tests/frontend/ai.test.mjs(+1),tests/frontend/pdf-viewer.test.mjs(TOC, plein largeur),tests/e2e/pdf-viewer.spec.js(TOC#page=N, largeur, fixturetest_vault/sample-pdf-toc.pdf).
[2.11.3] — 2026-09-17
Corrigé
- BUG-035 —
secret_redactor: faux positifs sur les hashs hex : la règle qui masquait tout jeton hexadécimal de 40 à 64 caractères mutilait les hashs git/SHA légitimes des notes. Le masquage des chaînes hexadécimales n'a désormais lieu que si un mot-clé de secret (secret,token,key,password,bearer…) figure dans les 60 caractères précédents ; un contexte de hash (commit,sha256,hash,checksum,git,etag…) exempte explicitement la chaîne. Fichier :backend/secret_redactor.py. - BUG-036 — Collaboration WebSocket : jeton accepté en query string : le JWT n'est plus lu
depuis
?token=(URLs journalisées par les proxies et l'historique navigateur). Le cookie HttpOnlyaccess_token, envoyé automatiquement par le navigateur lors du handshake same-origin, est le seul transport supporté ; les trames brutes dépassantMAX_MESSAGE_CHARS(16 Mio) sont rejetées avant analyse. Fichier :backend/collab.py. - BUG-037 — Mode sans authentification : au démarrage, un avertissement explicite est
journalisé quand
OBSIGATE_AUTH_ENABLED=false. Le serveur refuse désormais de démarrer s'il est lié à une adresse non-loopback sans l'opt-in expliciteOBSIGATE_ALLOW_INSECURE=true, pour empêcher l'exposition publique d'une instance sans authentification (admin anonyme). Fichiers :backend/auth/middleware.py,backend/main.py. - BUG-038 — Argon2 : coût mémoire recalibré :
memory_costpasse de 64 Mio à 19 Mio (m=19456 Kio, t=2, p=1, recommandation OWASP actuelle) pour supprimer le risque d'épuisement mémoire sous connexions simultanées ; les anciens hachages restent valides et sont migrés automatiquement (needs_rehash). Fichier :backend/auth/password.py. - BUG-039 — Énumération de comptes au login : les comptes inconnus, désactivés, verrouillés
et limités par le budget par compte répondent tous un
401 Identifiants invalidesavec un temps équivalent (hachage factice), au lieu d'un429/403distinctif ; seul le rate-limit par IP (non lié à un compte) conserve le429. Fichier :backend/auth/router.py. - BUG-040 — Extraction PDF différée au scan :
_scan_vaultne lit plus que les métadonnées des PDF ; l'extraction de texte intégrale (100 kio) est déléguée àenrich_pdf_texts(), exécutée après la construction de l'index/inverted index (démarrage) et après chaque réindexation. Un vault contenant de nombreux/gros PDF démarre sans être bloqué ; le texte reste recherchable une fois l'enrichissement terminé. Fichiers :backend/indexer.py,backend/main.py. - Tests :
tests/test_api_main.py(redactor hex),tests/test_auth.py(coût Argon2, garde-fou d'instance non authentifiée),tests/test_auth_api.py(login uniforme),tests/test_collab.py(jeton query rejeté, trame surdimensionnée),tests/test_pdf.py(scan différé + enrichissement).
[2.11.2] — 2026-09-17
Modifié
- Tests E2E PDF (BUG-060) : le helper d'ouverture de fichier de
tests/e2e/pdf-viewer.spec.jsétend désormais le vault dans l'arborescence s'il est replié (cas d'une instance avec authentification activée) au lieu de supposer le vault déjà déplié.
[2.11.1] — 2026-09-17
Corrigé
- BUG-060 — Viewer PDF : l'affichage des pages ne fonctionnait pas : au clic sur un fichier
.pdf, seule la barre d'outils « PDF — N pages » s'affichait, le corps restant vide. La CSP durcie en BUG-034 poseobject-src 'none', directive qui gouverne<embed>/<object>, alors que le viewer rendait le PDF via<embed type="application/pdf">: le lecteur natif était bloqué. Le rendu passe désormais par une<iframe>(autorisée parframe-src 'self', le stream/api/file/{vault}/pdf/streamétant same-origin) ;object-src 'none'est conservé. Fichiers :frontend/js/viewer.js,tests/frontend/pdf-viewer.test.mjs(nouveau),tests/e2e/pdf-viewer.spec.js(nouveau, fixturetest_vault/sample-pdf.pdf).
[2.11.0] — 2026-09-17
Ajouté
- #103 — Clés des sources connectées configurables depuis la page Configurations : nouvelle
section « 🔗 Sources connectées & recherche » (admin) permettant de saisir ses propres jetons
sans toucher aux variables d'environnement : clés de recherche web à clé (Tavily, Brave,
SerpAPI, Exa), URL Gitea + token, token GitHub. Stockage dans
data/api_keys.json(même fichier que les clés des fournisseurs IA) via les endpointsGET/POST/DELETE /api/config/tool-keys(GET masque les secrets, URLs en clair ; liste blanche stricte ; admin requis). Les outils lisent la valeur stockée en priorité puis l'environnement (Infisical) —backend/tools/secrets.py. Fichiers :backend/tools/{secrets,web,connected}.py,backend/main.py,frontend/index.html,frontend/js/config.js,frontend/locales/{fr,en}.json,tests/test_tool_keys.py(nouveau),tests/test_connected_tools.py.
[2.10.1] — 2026-09-17
Corrigé
- BUG-059 — Assistant IA : un clic dans la conversation faisait sauter le texte au bas de la
fenêtre : dans une conversation ouverte (post ancré en haut), tout clic — lien de fichier,
étapes, sélection de texte — dépinait l'ancre via le gestionnaire
mousedownet retirait le padding d'ancre, bornant le scroll à la nouvelle hauteur max (saut au bas). Le dépintage est désormais réservé aux vrais gestes de scroll : molette, tactile et poignée de scroll uniquement (isScrollbarPress). Fichiers :frontend/js/bookslm.js,tests/frontend/ai.test.mjs. - #92 —
create_pdfde l'Assistant : tableaux mal formatés : l'outilcreate_pdf(génération PDF via l'assistant) utilisait un rendu reportlab simplifié sans support des tableaux. Il passe désormais par le même pipeline que le bouton « Télécharger PDF » de la page document (mistune + plugintable+ WeasyPrint), avec repli automatique sur le rendu simple si WeasyPrint n'est pas disponible (GTK absent). Fichiers :backend/tools/documents.py,tests/test_document_tools.py.
[2.10.0] — 2026-09-17
Ajouté
- #92 — Assistant IA : écosystème d'outils phase 2 (web étendu, sources connectées, documents) :
- Recherche web à clé : fournisseurs optionnels essayés avant SearXNG — Tavily, Brave
Search, SerpAPI (Google) et Exa (
OBSIGATE_TAVILY_API_KEY,OBSIGATE_BRAVE_API_KEY,OBSIGATE_SERPAPI_API_KEY,OBSIGATE_EXA_API_KEY), avec ordre personnalisable viaOBSIGATE_WEB_PROVIDERS. - Transverse : réessais réseau avec backoff maison (
OBSIGATE_WEB_RETRY) et cache SQLite des résultats web avec TTL (OBSIGATE_WEB_CACHE_TTL, 900 s par défaut,OBSIGATE_WEB_CACHE_PATH). - Rendu dynamique :
fetch_url(render=True)délègue les pages SPA à un worker Playwright isolé (dépendance optionnelle, dégradation propre si non installée). - Crawl de site :
crawl_site(WRITE, confirmation) capture jusqu'à 20 pages d'un même hôte et enregistre un condensé Markdown dans un vault. - Sources connectées : Gitea (
OBSIGATE_GITEA_URL/OBSIGATE_GITEA_TOKEN) et GitHub (OBSIGATE_GITHUB_TOKEN) —git_list_repos,git_search_issues,git_get_file(READ, rate-limités, audités). Les drives cloud (Google Drive / OneDrive) restent orientés serveur MCP externe (#79), conformément à la feuille de route. - Production de documents :
create_xlsx(openpyxl),create_docx(python-docx),create_csv,create_pdf(reportlab) — outils WRITE avec confirmation et sauvegarde dans le vault (backup avant écrasement). - Chaque outil : libellé
labels.py+ clés i18nai.step.*FR/EN + tests unitaires mockés (httpx). Dépendances ajoutées :openpyxl,python-docx,reportlab. Fichiers :backend/tools/{webcache,webrender,connected,crawler,documents,web,schemas,labels}.py,backend/services/mutations.py,tests/test_{web_cache,web_search_providers,webrender,connected_tools,document_tools,crawler}.py.
- Recherche web à clé : fournisseurs optionnels essayés avant SearXNG — Tavily, Brave
Search, SerpAPI (Google) et Exa (
[2.9.1] — 2026-09-17
Corrigé
- BUG-058 — Éditeur « Editer » : la barre de numérotation de ligne ne suit pas le thème :
CodeMirror peint son gutter (colonne des numéros de ligne) avec des valeurs claires codées
en dur (
#f5f5f5, bordure#ddd), si bien qu'en thème sombre la barre restait gris clair alors que le fond de l'éditeur suivait le thème. Le gutter est désormais dérivé des variables CSS du thème ObsiGate (color-mix(in srgb, var(--text-primary) …)pour un fond subtil,--text-secondarypour les numéros,--borderpour la séparation), ce qui le fait suivre tous les thèmes et modes (sombre, clair, contraste élevé, sépia). Fichiers :frontend/style.css. Tests :tests/frontend/editor-inline.test.mjs.
[2.9.0] — 2026-09-17
Ajouté
- #102 — Assistant IA : bouton « Ajouter la section » par bloc de code : chaque bloc
de code d'une réponse de l'assistant (par ex. une section
markdown) affiche un bouton discret « Ajouter la section » qui insère uniquement ce bloc dans le document ouvert (sans les délimiteurs de code), au lieu de la réponse complète. Fichiers :frontend/js/bookslm.js,frontend/style.css,frontend/locales/{fr,en}.json. Tests :tests/frontend/ai.test.mjs.
Corrigé
- BUG-057 — Assistant IA : bouton « Ajouter » inopérant dans l'éditeur Forge : le
bouton ne ciblait que
state.editorView(CodeMirror de « Editer ») et affichait « Aucun document ouvert dans l'éditeur » en Forge._insertIntoEditor()prend désormais en charge les trois surfaces : CodeMirror, l'iframe Forge (délégation parpostMessage({ type: 'parent-insert' })→insertAtCursordanseditor-poc.html) et le textarea de repli. Fichiers :frontend/js/bookslm.js,frontend/editor-poc.html. Tests :tests/frontend/ai.test.mjs,tests/frontend/editor-inline.test.mjs.
[2.8.4] — 2026-09-17
[2.8.3] — 2026-09-17
Corrigé
- BUG-056 - Éditeur Forge en plein écran : l'Assistant IA s'ouvre en arrière-plan :
le panneau de l'assistant est monté dans le document parent, alors que le plein écran
Forge porte sur l'iframe — l'API Fullscreen n'affichant que l'élément plein écran et ses
descendants, le panneau restait invisible. Le plein écran est désormais quitté avant
d'ouvrir le panneau : côté parent (
sync.js, surforge-open-ai— le plein écran peut appartenir au document parent et non à l'iframe) et côté iframe (editor-poc.html,openAssistant), la demande d'ouverture étant émise une fois la sortie effective. Fichiers :frontend/editor-poc.html,frontend/js/sync.js. Tests :tests/frontend/forge-completion.test.mjs(+1),tests/frontend/editor-inline.test.mjs(+1).
[2.8.2] — 2026-09-17
Corrigé
- BUG-055 (complément) - Éditeur Forge : une complétion acceptée au
Tabétait supprimée 1–2 s plus tard : l'auto-sauvegarde (2 s) déclenche un événement SSEindex_updatedsur le fichier en cours, et le parent rechargeait alors le tampon de Forge depuis le disque — écrasant une complétion (ou toute frappe) faite après la sauvegarde. Le rechargement SSE est désormais ignoré tant que le tampon local est modifié (isDirty) ; seul un écrit externe (assistant IA) force le rechargement. L'auto-sauvegarde ne repasse plus l'état « enregistré » si des modifications sont arrivées pendant la requête (Forge et éditeur CodeMirror), et l'acceptation du ghost annule la requête de prédiction en attente. Fichiers :frontend/editor-poc.html,frontend/js/utils.js. Tests :tests/frontend/forge-completion.test.mjs(+3),tests/frontend/editor-inline.test.mjs(+1).
[2.8.1] — 2026-09-17
Corrigé
- BUG-055 - Éditeur Forge : autocomplétion Tab naturelle et fiable : la touche
Tabdéclenchait simultanément trois actions indépendantes (indentation, complétion d'un mot du document, acceptation de la prédiction IA), ce qui insérait un ou deux espaces avant le mot complété — et le retour arrière effaçait alors l'ajout. La gestion deTabest désormais unifiée avec une priorité claire (liste de suggestions ouverte → prédiction IA → complétion de mot du document → indentation), une seule action par appui. Les helpers de complétion sont extraits en fonctions pures partagées avec CodeMirror (getWordFragment,findWordCompletions,normalizeGhost,chooseTabAction) ; plusieurs candidats affichent une liste positionnée au curseur ; la complétion fantôme n'affiche plus un miroir transparent de tout le document (source du décalage visuel et des espaces fantômes) mais uniquement la prédiction, positionnée exactement au curseur et nettoyée dès que le curseur bouge ou que la vue défile ; une complétion de mot ne peut plus introduire d'espace. Le prompt/api/ai/inline-completeest simplifié et borné à 128 tokens pour des suggestions plus courtes et plus rapides. Fichiers :frontend/editor-poc.html,frontend/js/autocomplete.js,backend/ai.py. Tests :tests/frontend/forge-completion.test.mjs(nouveau, 28 tests).
[2.8.0] — 2026-09-17
Ajouté
- #101 - Forge : Assistant IA partagé et plein écran (Forge & Editer) : le bouton
« AI Panel » de Forge ouvre désormais le panneau Assistant IA existant (barre latérale)
au lieu d'un mini-chat isolé — même contenu, même fournisseur/modèle, même historique, mêmes
menus
/et@, sans duplication. Forge lit la sélection du sélecteur de l'assistant (localStorage['obsigate_ai_picker'], même origine) et l'injecte dans ses appels/api/ai/*et sa complétion fantôme (repliollamasi aucun fournisseur choisi) ; au passage, les endpoints erronés sont corrigés (make-longer/make-shorter,target_lang). Un bouton plein écran natif est ajouté à Forge (iframeallow="fullscreen") et à l'éditeur Editer (plein écran sur le conteneur, fonctionne en modale comme en inline, sortie à la fermeture). Libellés i18n FR/EN. Fichiers :frontend/editor-poc.html,frontend/js/sync.js,frontend/js/viewer.js,frontend/js/utils.js,frontend/index.html,frontend/style.css,frontend/locales/{fr,en}.json. Tests :tests/frontend/editor-inline.test.mjs(+10).
[2.7.5] — 2026-09-17
Corrigé
- BUG-054 - Éditeur « Editer » : le bouton Sauvegarder restait bloqué sur le spinner de
chargement : le bouton
#editor-saveest un nœud DOM partagé entre toutes les sessions d'édition (y compris en mode en ligne). Une sauvegarde manuelle y remplaçait le crochet par un spinner et le désactivait, mais cet état n'était jamais remis à zéro : après une sauvegarde réussie (l'éditeur se ferme puis se rouvre), après une sauvegarde Forge, ou après un échec de requête (lecatchne restaurait ni l'icône ni l'état), le spinner persistait jusqu'à un rechargement complet de la page. Un helperresetSaveButton()restaure désormais le crochet et réactive le bouton à l'ouverture (openEditor), à la fermeture (closeEditor) et en cas d'échec (saveFile). Fichier :frontend/js/utils.js. Tests :tests/frontend/editor-inline.test.mjs(+4).
[2.7.4] — 2026-09-17
Corrigé
- BUG-053 - Assistant IA (mode agent) : le fichier demandé n'est pas créé : le prompt
système du mode Général (et du dossier vide) enseignait encore le protocole texte
obsigate-action, si bien que le modèle décrivait l'action dans un bloc texte au lieu d'appeler l'outil natifcreate_file— rien n'était donc créé (et le bloc, volumineux, était tronqué avant sa fermeture). En mode agent, le prompt demande désormais d'appeler directement les outils natifs (create_file,create_directory, …) et interdit les blocsobsigate-action; le chat classique conserve le protocole texte. La limite de sortie de l'agent passe à 8 192 jetons pour laisser place au contenu complet d'un fichier. Fichiers :backend/bookslm.py,backend/bookslm_routes.py. Tests :tests/test_bookslm.py(+3).
[2.7.3] — 2026-09-16
Corrigé
- BUG-052 - Assistant IA : recherche web sans réponse finale (étapes et sources affichées, aucun texte) :
quand le budget d'itérations (
DEFAULT_MAX_ITERATIONS = 10) ou le quota d'appels d'outils était épuisé pendant que le modèle enchaînait encore des recherches/lectures, la boucle d'agent renvoyait un contenu vide → la conversation n'affichait que les étapes et les sources. La boucle effectue désormais un dernier appel sans outil qui demande au modèle de synthétiser les informations recueillies (_finalize_answer), avec un repli déterministe listant les sources si cet appel échoue ou reste vide. Les appels d'outils non atteints du lot en cours de quota reçoivent un résultatdeferredpour garder la conversation valide. Fichier :backend/agent/loop.py. Tests :tests/test_agent_loop.py(+2).
[2.7.2] — 2026-09-16
Corrigé
- BUG-051 (complément) - Repli Bing : en-têtes de navigation navigateur : un
User-Agentnavigateur seul ne suffit pas — Bing renvoie des résultats factices (SERP sans rapport avec la requête) aux appels dépourvus des en-têtes de navigation habituels. Les fournisseurs HTML (DuckDuckGo, Bing) envoient désormaisAccept-Language,Sec-Fetch-*etUpgrade-Insecure-Requests(BROWSER_HEADERS). Vérifié en conteneur : recherche « Canadien de Montréal 2026-2027 » → résultats NHL / RDS / Wikipédia pertinents (provider: bing). Fichier :backend/tools/web.py.
[2.7.1] — 2026-09-16
Corrigé
- BUG-051 - Assistant IA : « je ne peux pas accéder à internet » malgré la recherche web :
lorsque l'instance SearXNG auto-hébergée ne remontait aucun résultat (moteurs amont
suspendus/CAPTCHA),
web_searchrenvoyait une liste vide et le modèle concluait à une absence d'accès réseau. L'outil essaie désormais une chaîne de repli sans clé — SearXNG, puis DuckDuckGo (endpoint HTML sans JS), puis Bing (page de résultats HTML) — et ne s'arrête qu'au premier fournisseur qui renvoie des résultats (providerdans le résultat,OBSIGATE_WEB_FALLBACK=0pour désactiver les replis). Le message d'avertissement final nomme les fournisseurs essayés. Fichier :backend/tools/web.py. Tests :tests/test_web_tools.py(+4).
[2.7.0] — 2026-09-16
Ajouté
- Assistant IA — 30 skills intégrés (au lieu de 11) et prompts enrichis : les skills
déclenchés par
/couvrent désormais l'extraction/structuration (/extract,/timeline,/glossary,/tag), la transformation (/translate,/adapt,/clean,/summary-progressive), l'analyse critique & décision (/critique,/compare,/prioritize,/swot,/debate), l'apprentissage (/quiz,/reading-note,/qa-generator) et la méta-gestion (/link,/anonymize,/estimate). Tous les prompts sont réécrits (rôle, structure de sortie Markdown, cas limites) et complétés par un blocCOMMON_RULEScommun (français, notes traitées comme données, anti-hallucination, signalement des contradictions, conservation des noms/dates/chiffres, réponse « Aucune information exploitable fournie. » si les notes sont insuffisantes). Fichier :backend/skills.py. Tests :tests/test_skills.py(ids uniques/valides, présence du prompt et deCOMMON_RULESpour chaque skill).
[2.6.2] — 2026-09-16
Corrigé
- BUG-050 — Assistant IA : échec de la création d'un sous-dossier contenant un fichier :
lors d'une pause de confirmation, la boucle d'agent ne renvoyait le résultat que du seul
appel confirmé alors que le message assistant annonçait tous les appels d'outils du tour —
la conversation devenait invalide (identifiant
tool_call_idsans réponse) et la reprise échouait. Les appels non atteints reçoivent désormais un résultatdeferredexplicite (backend/agent/loop.py). En complément,create_directoryest idempotent côté outil IA (succès si le dossier existe déjà,backend/services/mutations.py) et les consignes (create_filecrée les dossiers parents) invitent le modèle à un seul appel avec un chemin imbriqué (backend/bookslm.py,backend/tools/service.py). Tests :tests/test_agent_loop.py(+1),tests/test_tools_mutations.py(+1),tests/test_api_main.py(+1).
[2.6.1] — 2026-09-16
[2.6.0] — 2026-09-16
Ajouté
- Barre de filtrage de la sidebar sur « Récents » et « Sauvegardes » (#99) : la barre
de recherche de la sidebar agit désormais sur les onglets Récents
(
filterRecentFiles, filtrage titre/chemin/vault/aperçu/tags) et Sauvegardes (filterSavedSearches, cumulable avec les pills Tous/Recherches/Répertoires) — insensible à la casse et aux accents, avec message d'absence de résultat et placeholders dédiés (sidebar.filter_recent,sidebar.filter_saved). Le routage deinitSidebarFilterest unifié (routeFilter/routeClear) pour couvrir les cinq onglets. Fiche : docs/features/sidebar-filters.md. - Assistant IA — Deep Research en pastille (#100) : « Deep Research » ajoute désormais une pastille (comme les skills) au lieu d'écrire la directive dans la zone de saisie ; le mode Agent est activé et la directive est injectée au moment de l'envoi, sans polluer le message affiché.
Corrigé
- BUG-049 — icône du bouton « + » de l'assistant invisible : la règle générique
.bookslm-input-area buttonécrasait.bookslm-btn-plus(padding: 8px 16pxsur une largeur de 32 px ⇒ largeur de contenu nulle ⇒ SVG à 0 px). Sélecteur porté à.bookslm-input-area button.bookslm-btn-plus, l'icôneplusest de nouveau visible (vérifié en navigateur : SVG 0 px → 18 px). Tests :tests/frontend/ai.test.mjs(+1),tests/frontend/sidebar-filters.test.mjs(nouveau, 8 tests).
[2.5.2] — 2026-09-16
[2.5.1] — 2026-09-16
[2.5.0] — 2026-09-16
Ajouté
- Assistant IA — filtre de recherche dans la sidebar « Historique IA » (#98) : la barre
de filtrage de la sidebar agit désormais sur l'onglet Historique IA.
filterAIHistory()(frontend/js/config.js) filtre les sessions par titre, aperçu, répertoire, contexte ou libellé de mode — insensible à la casse et aux accents (_aiNorm) — avec cache sessions (_aiSessionsCache), message « aucune conversation ne correspond à la recherche » (bookslm.history_no_match) et placeholder dédiésidebar.filter_ai. Le routage saisie / touche casse / bouton « × » vers ce filtre est géré dansinitSidebarFilter(frontend/js/sidebar.js) quand l'onglet IA est actif.
Corrigé
- BUG-048 — menu d'ajout « + » : « Contextes » et « Skills » ouvrent enfin leurs menus
« @ » / « / » : le clic sur une entrée du panneau
.bookslm-ext-menuremontait au gestionnaire du panneau qui annulait le rendu asynchrone du menu (jamais affiché) ;e.stopPropagation()sur les entrées corrige le comportement. Le bouton « + » porte l'icône Lucideplus. Tests :tests/frontend/ai.test.mjs(+3), nouvelle suitetests/frontend/ai-sidebar.test.mjs(6 tests).
[2.4.0] — 2026-09-16
Ajouté
- Assistant IA — historique permanent des conversations (#95) : les échanges sont
désormais persistés côté backend (
backend/ai_history.py,data/ai_history/{user}.json, cap 200, écriture atomique) et plus seulement en localStorage. Nouveaux endpointsGET/PUT/DELETE /api/ai/bookslm/history[…](liste résumée sans messages → full, upsert qui force l'id, suppression, isolation par utilisateur). Le panneau synchronise ses sessions au chargement (debounce 600 ms, repli hors-ligne sur le cache local), migre les anciennes clésbookslm-sessions-*/bookslm-history-*et notifie la sidebar via l'événementbookslm:history-updated. - Assistant IA — accès rapide à l'historique dans la sidebar (#96) : cinquième onglet de
navigation
#sidebar-tab-ai(icônemessages-square) listant les conversations par ordre chronologique ; un clic ouvre la conversation dans le panneau Assistant IA (openWithSession). - Assistant IA — panneau « + » extensible (#97) : le bouton « Attach an image » est
remplacé par un bouton « + » ouvrant un panneau modulaire (registre
_extensionssimple à étendre) : Fichiers, Image, Contextes, Skills, Deep Research (mode agent + prompt pré-rempli), Recherche web et Canva (les deux derniers en « Bientôt »). - Assistant IA — bouton de soumission arrondi (#94) :
.bookslm-btn-sendcirculaire (40 px), icône Lucidearrow-upremplaçant l'avion ✈️. - Fiche feature : docs/features/ai-assistant-history.md.
Tests :
tests/test_bookslm.py(+11),tests/frontend/ai.test.mjs(+3).
[2.3.4] — 2026-09-16
Documentation
- AGENTS.md — versionnage automatique : la méthode obligatoire de fin de tâche rappelle
désormais que
VERSION(racine) est la source unique de vérité (MAJEUR.MINEUR.CORRECTIF), incrémentée à chaque commit par le hook.githooks/prepare-commit-msg, que les dérivés (package.json, desktop Tauri, READMEs,docs/ROADMAP.md,CHANGELOG.md) sont resynchronisés dans le même commit, et que le tagvX.Y.Zest publié au push. Ligne ajoutée à la cartographie documentaire. Ménage : suppression des captures de diagnostic (diag-*.png,ogdiag.png,state.png) et du script jetabletmp-verify-inline.cjsrestés à la racine du dépôt.
[2.3.3] — 2026-09-16
Corrigé
- #93 (complément) — garde-fous de session d'édition inline : une session d'édition
(« Editer » ou Forge) qui possède la zone de contenu était détruite par deux chemins
qui vident cette zone sans la libérer d'abord — l'activation d'un onglet
(
TabManager.activate,PaneTabManager.activate) et l'événement SSEindex_updatedsur le fichier affiché (un fichier modifié hors de l'app relançaitopenFile()et écrasait le DOM de l'éditeur). L'activation d'onglet appelle désormaisdetachInlineEditor()avant le placeholder de chargement, et l'écriture externe passe parreloadExternalWrite()(le tampon de l'éditeur est rechargé depuis le disque au lieu d'un re-rendu de la vue lecture). Nouveau helperqueryEditor()(editor-inline.js) : l'en-tête, le pied et la marque voyagent avec le conteneur en mode inline, unmodal.querySelector()les manquait ;closeEditor()remet le conteneur dans la modale avant de restaurer en-tête/pied/marque. Fiche : docs/features/editeur-inline.md. Tests :tests/frontend/editor-inline.test.mjs(25). - Documentation — hooks git obligatoires à l'installation :
docs/CONTRIBUTING.mddocumente l'appel àscripts/install-hooks.sh(versionnage automatique) dans les étapes de mise en place d'un clone.
[2.3.2] — 2026-09-16
[2.3.1] — 2026-09-16
[2.3.0] — 2026-09-16
Ajouté
- #89 Drag & Drop complet de fichiers/dossiers & intégration Assistant IA — solution
complète de glisser-déposer de fichiers individuels, multiples et dossiers récursifs
depuis l'OS vers le vault (
POST /api/vault/{vault}/batch-upload, FileSystem API), overlay plein écran à double cible (dépôt vault vs analyse IA), surbrillance des cibles dans l'arborescence, et dropzone BooksLM pour injection de contexte ou pièces jointes. Fiche : docs/features/drag-and-drop-ai.md. - #89 Pretty print fichiers texte — bouton « Pretty » dans le mode lecture des fichiers non-markdown (code, JSON, configs, logs...) : formatage JSON, numéros de ligne avec gouttière et coloration syntaxique highlight.js alignée sur le thème clair/sombre, avec colorateur générique de secours (chaînes, commentaires, nombres, niveaux de log) quand highlight.js ne reconnaît pas le langage (txt, log, plaintext).
- #88 Assistant IA : contexte applicatif — l'assistant en mode Général
reçoit l'état vivant de l'interface (
app_context) et connaît désormais les documents ouverts, le répertoire et le vault courants, la recherche en cours (requête + premiers résultats affichés) et les fichiers récemment modifiés du utilisateur. Le prompt Général est enrichi d'un bloc « Contexte applicatif actuel » (build_general_system_prompt(vaults, app_context, recent_files)). Fiche : docs/features/ai-app-context.md. - #90 Barre d'actions du document — regroupement fonctionnel : les boutons
en haut du document sont réordonnés en quatre groupes séparés par des
barres verticales (
span.action-sep) :[TOC] [pop-out] [Bookmark] | [Editer] [Source] [.md] [Forge] | [Copier] [PDF] [Export] | [Partager]. Le bouton « Pretty » (fichiers texte) rejoint le groupe édition, les boutons non applicables à un type de fichier sont omis et les spacers d'un groupe vide ne sont pas rendus. Fichiers texte non markdown :[pop-out] [Bookmark] | [Editer] [Source] [.ext] [Pretty] | [Copier] | [Partager](pas de TOC, pas de PDF/Export). La fenêtre pop-out (popout.html) est alignée sur le même ordre markdown (groupes nav / édition / export / partage). Tests :tests/frontend/toolbar-order.test.mjs(9). - #91 Assistant IA — zone de discussion façon Notion : le fil de discussion est
reformaté (messages chronologiques, post utilisateur en bulle alignée à droite,
réponse assistant pleine largeur sans fond, appels d'outils agent repliés en
bloc « N étapes ») ; après l'envoi, la question est ancrée en haut de la zone
visible (
scrollIntoViewblock:start+scroll-margin-top) et la réponse se diffuse en dessous — le streaming ne provoque plus de saut de scroll ; un libellé discret au-dessus de chaque réponse rapporte le fournisseur et le modèle réellement utilisés (SSEprovider/model) ; une barre d'actions au survol sous chaque bloc : « Copier » (texte brut + toast) et, pour les réponses, « Ajouter » (insertion dans le document ouvert dans l'éditeur Forge). Complément : la section « N étapes » affiche désormais des libellés humains produits par le backend (backend/tools/labels.py, événements SSE en direct pendant l'exécution + ligne « Réflexion »), et deux nouveaux outils web principaux sont disponibles en mode agent :web_search(SearXNG auto-hébergé) etfetch_url(page publique → texte, garde SSRF). Le reste des catégories d'outils Notion est documenté pour le futur dans la fiche. Fiche : docs/features/ai-assistant-conversation-ux.md Tests :tests/frontend/ai.test.mjs(76),tests/test_tool_labels.py(8),tests/test_web_tools.py(8). - #91 Assistant IA — section d'étapes enrichie : l'en-tête affiche « N étapes »
suivi d'un chevron ▶ / ▼ (fin du préfixe « > ») et, pendant l'exécution, un
indicateur animé de trois points devant le libellé ; la barre de chargement
au-dessus de la zone de saisie est supprimée (en chat simple, l'indicateur s'affiche
dans la bulle de réponse en attente). Chaque note de raisonnement devient une
sous-section « Réflexion ▶ / ▼ » contenant le texte du modèle, et les recherches
web ajoutent une sous-section « Sources (N) » avec les liens cliquables
(le backend émet
sources [{title, url}]dans l'événement SSEtool, résultatsweb_searchplafonnés à 8 + pagefetch_url). Les états d'ouverture (étapes, réflexion, sources) sont mémorisés sur le message : un token re-rendu ne referme jamais ce que l'utilisateur a déplié.web_searchsignale désormais explicitement au modèle une instance SearXNG sans résultat (moteurs suspendus/CAPTCHA) au lieu de le laisser relancer la même recherche jusqu'au quota. Tests :tests/frontend/ai.test.mjs(82),tests/test_bookslm.py::TestToolEventSources(4),tests/test_web_tools.py(10).
Modifié
- #93 Édition inline — « Editer » et « Forge » remplacent la vue lecture : le conteneur
d'édition (
#editor-container, CodeMirror ou iframe Forge) est déplacé dans la zone de contenu du document (#content-area, ou le panneau actif en vue fractionnée) au lieu d'être affiché dans l'overlay plein écran : le mode édition remplace le document en mode lecture (bandeau, métadonnées, tags et contenu). L'overlay reste monté mais neutralisé (transparent, sans capture des clics,z-indexconservé) car le ruban d'édition mobile y est ancré. Retour à la lecture par ✓ (sauvegarde), ✕, Échap ou l'ouverture d'un autre fichier — trois garde-fous libèrent proprement la session (destruction CodeMirror/Yjs, retrait de l'iframe Forge) avant qu'un rendu concurrent n'efface son DOM :renderFile(), l'activation d'onglet (TabManager.activate,PaneTabManager.activate, qui vident la zone avant de rendre) et l'événement SSEindex_updated(un fichier modifié hors de l'app recharge le tampon de l'éditeur au lieu de re-rendre la vue lecture). L'overlay n'est conservé qu'en repli, quand la cible n'est pas le document affiché (ex. fichier créé depuis la palette). Conséquence directe : l'éditeur ne recouvre plus le panneau de l'assistant IA, qui peut donc mettre le document à jour sous les yeux de l'utilisateur —app_context.editingannonce au modèle le document en cours d'édition (et sa surface), et toute écriture d'outil (edit_file,append_to_file,create_file,restore_backup) recharge le document affiché depuis le disque (obsigate:file-written) : le tampon de l'éditeur est rafraîchi au lieu d'écraser la modification de l'assistant par son auto-sauvegarde, et l'iframe Forge reçoitparent-reload. Aucun identifiant DOM n'a changé (autosave, collab Yjs, barre IA, aperçu Mermaid, Forge intacts). Fiche : docs/features/editeur-inline.md. Tests :tests/frontend/editor-inline.test.mjs(19),tests/test_bookslm.py::TestGeneralPrompt(3 nouveaux).
Corrigé
-
BUG-047 — La version affichée ne suivait pas les livraisons : la version provenait du dernier tag git, or aucun tag n'était créé lors des livraisons → l'application restait bloquée sur
2.2.1alors que 66 commits avaient été livrés (et les numéros codés en dur divergeaient :package.json1.0.0, desktop Tauri 2.0.0,Dockerfile2.2.1, README 1.7.0). Le fichierVERSION(racine du dépôt) devient la source unique de vérité (MAJEUR.MINEUR.CORRECTIF), incrémenté automatiquement à chaque commit par le hook versionné.githooks/prepare-commit-msg(!:/BREAKING CHANGE→ MAJEUR,feat→ MINEUR, sinon CORRECTIF) ;.githooks/post-commitcrée le tagvX.Y.Zetpush.followTagsle publie à chaque push. Tous les dérivés sont resynchronisés dans le même commit :package.json, desktop Tauri (tauri.conf.json,Cargo.toml,Cargo.lock),docs/ROADMAP.md,README.md/README.fr.mdet rotation deCHANGELOG.md([Unreleased]→[X.Y.Z] — date). Le backend (backend/version.py), l'image Docker (COPY VERSION) et le desktop Tauri lisent ce même fichier : plus aucun numéro codé en dur dans le pipeline. Outils :scripts/bump_version.py(+scripts/bump_version.sh),scripts/install-hooks.sh. Tests :tests/test_version.py, dont un garde-fou de cohérence globale (VERSION ↔ package.json ↔ desktop ↔ CHANGELOG ↔ ROADMAP ↔ README). Vérifié :curl /api/healthsur l'instance de test →2.3.0. -
BUG-045 — Double barre de défilement dans l'éditeur « Editer » : sur les documents longs,
#editor-body(qui gardaitoverflow:auto) et le scroller CodeMirror débordaient simultanément — la barre d'outils IA empilée sous un.cm-editorenheight:100%faisait déborder le corps, et un ancien override global.cm-scrollerforçait une seconde zone défilante. La surface d'édition est passée en flex column : le corps ne défile plus (overflow: hidden), l'éditeur remplit l'espace restant et seul le scroller CodeMirror défile. Repli textarea (fallback) cohérent ; la règle mobile BUG-020 est préservée. -
BUG-046 — « Échec de l'action : [object Object] » (assistant IA, ajout de texte au document courant) : trois causes corrigées ensemble. (1) La carte de confirmation « Appliquer » reprenait un payload dont le message était absent après une première application échouée (continuation
payload:null) → réponse 422 du backend. (2) Cette réponse 422 porte undetailtableau d'objets (format validation FastAPI) quenew Error(detail)réduisait en[object Object]dans le toast — nouvelle méthode_responseError()qui aplatit tableaux et objets en message lisible. (3) Le prompt des modes documents/dossier ne nommait pas le vault : le modèle inventait parfois"vault":"test"et l'outil d'écriture échouait silencieusement — le prompt indique désormais le vault exact et enjoint les outilsedit_file/append_to_file/create_filede l'utiliser. Cache service workerSW_VERSIONv20. -
#91 — Bulle utilisateur trop étroite & ancre d'envoi retardée : le plafond de largeur était appliqué deux fois (wrapper 80 % × bulle 85 % ≈ 68 % du fil), d'où des lignes très courtes ; le plafond est désormais porté par la seule bulle (90 %), le wrapper s'étirant → ~81 % de la largeur du fil (≈48 caractères/ligne au lieu de ~28). Par ailleurs, le rendu du message assistant « placeholder » écrasait la position de défilement juste après l'envoi (l'ancre était annulée : la question restait en bas jusqu'au premier token).
_isLoadingest maintenant posé avant ce rendu et l'ancrage d'envoi est instantané — la question passe en haut du fil dès la soumission et y reste pendant tout le streaming. -
#91 — Question complètement en haut : la marge
scroll-margin-top(14 px) laissait visible la fin de la réponse précédente au-dessus du post, et une réponse courte ne remplissait pas la fenêtre — le navigateur bloquait alors le défilement et la question restait à mi-hauteur. La marge est supprimée et lepadding-bottomdu fil est augmenté de la place manquante tant que l'ancre est active (retiré au dé-épinglage, aucun remplissage si le fil tient dans la fenêtre). Le rendu final post-streaming conserve aussi l'ancre, et la libération se fait sur molette, toucher ou clic dans le fil. Enfin,overflow-anchor: nonesur le fil neutralise le ré-ancrage automatique de Chrome (le fil est reconstruit à chaque token, ce qui décalait la vue de ~33 px) et une passe de correction recale le défilement d'après la géométrie mesurée. -
#91 — Tag fournisseur/modèle incomplet : le backend renvoyait le modèle demandé par le client (
req.model or ""), donc une valeur vide dès que le client s'en remet au défaut du fournisseur — le tag se réduisait à « openrouter »._effective_model()résout désormais le modèle réellement utilisé dans les deux flux SSE (/chatet/agent) : le tag affiche « openrouter · openai/gpt-4o-mini ». Couvert partests/test_bookslm.py::TestEffectiveModelet le test SSE de l'endpoint agent. -
BUG-041 — Assistant IA bloqué sur un répertoire vide : l'assistant ne renvoie plus
⚠ Error: Aucun fichier markdown trouvé dans ce dossier(HTTP 404). Le contexte vide dégrade vers le prompt Général augmenté d'un bloc « Dossier vide », et la requête aboutit sans contexte documentaire. -
BUG-042 — Liens de fichiers de l'assistant non fiables : les liens des réponses suivent désormais une règle déterministe — un simple nom de fichier copie le nom dans le presse-papiers, un chemin de dossier est révélé dans l'arborescence, un chemin de fichier l'ouvre. Le chemin est résolu contre l'index du vault (exact → suffixe → basename unique) avant d'agir ; les chemins non résolus copient le nom au lieu d'afficher
File not found. Les noms/chemins contenant des espaces sont pris en charge (Mon dossier/Ma note.md) : liens markdown (y compris cibles<…>et%20), code inline, et mentions brutes liées uniquement si le chemin existe dans l'index du vault. Les caractères accentués sont reconnus (classes de caractères Unicode\p{L}\p{N}\p{M}, comparaison normalisée NFC : une mention décomposéee+accent correspond à une entrée d'index précomposée) et un chemin préfixé par le nom du vault (TestVault/Recettes/Pizza Maison.md) est résolu puis ouvert dans ce vault, le préfixe étant retiré. -
BUG-043 — Liste des fournisseurs de l'assistant non synchronisée avec la configuration : ajouter ou supprimer une clé API dans la configuration du projet met désormais à jour immédiatement le menu Fournisseur de la barre latérale de l'assistant, sans recharger la page. Le picker ne lisait
/api/ai/statusqu'une fois, à sa construction, et le panneau de l'assistant est un singleton monté pour toute la session : la liste restait figée. Le nouveaurefreshAIPickers()(frontend/js/ai.js) reconstruit chaque picker monté dans son emplacement.ai-picker-slotet est appelé après l'enregistrement (saveAIKeys) et la suppression (deleteAIKey) d'une clé (frontend/js/config.js). Un emplacement est conservé même sans fournisseur configuré, donc le premier fournisseur ajouté s'y monte aussi. Une sélection dont le fournisseur n'est plus configuré est purgée deobsigate_ai_picker(retour au défaut au lieu d'un modèle fantôme). -
BUG-044 — Capacités des modèles erronées (aucun modèle Mistral vision) : la bulle ⓘ et le sélecteur de modèle par défaut n'affichaient aucun modèle Mistral « Vision capable », alors que
GET https://api.mistral.ai/v1/modelsen déclare 28 (mistral-medium,mistral-small,ministral-*,magistral-*,mistral-ocr-*,mistral-vibe-cli-*). Cause : la table de capacités était entièrement statique et aucun de ses motifs ne correspondait aux familles Mistral actuelles (seulpixtral, retiré de l'API, les matchait). ObsiGate lit désormais les capacités déclarées par le fournisseur (nouveaubackend/provider_capabilities.py, snapshot mis en cache parGET /api/config/ai-models) : Mistral (capabilities.completion_chat/vision/audio_transcription/audio_speech) et OpenRouter (architecture.input_modalities/output_modalities) sont pris en charge ; la table statique ne sert plus qu'à combler les drapeaux non déclarés et de repli hors ligne. La table est également corrigée (familles vision Mistral,mistral-ocr= vision sans chat) et le défaut du fournisseur Mistral ne prétend plus qu'un modèle non reconnu sait produire des embeddings (BUG-044bis :mistral-large-latest/codestral-latestétaient annoncés « Embeddings »). Conséquence : la porte vision de l'assistant (frontend/js/bookslm.js,backend/bookslm_routes.py) accepte enfin les images avec un modèle Mistral vision.
Sécurité
- #84 Consolidation & sécurité — phase 1 (BUG-021 → BUG-034) — traitement des
vulnérabilités de la revue statique du 2026-09-13 :
- BUG-021/022 — XSS stocké : nouveau sanitizer serveur en liste blanche
(
backend/services/sanitizer.py, stdlib) appliqué au rendu markdown ; échappement systématique dutitle, du frontmatter et du JSON de la page publique/s/{token}(</script>neutralisé). Tests :tests/test_security_hardening.py(9). - BUG-023 — brute-force MFA : rate-limit IP + compte et verrouillage de compte sur
mfa/totp/verify,mfa/recoveryetmfa/webauthn/verify(_enforce_mfa_rate_limit). - BUG-024 — traversal inter-vaults :
resolve_safe_pathcompare désormais les chemins par segment (Path.relative_to+ repli casse-insensible), plus par préfixe de chaîne :vaultne peut plus lirevault-evil. - BUG-025 — ReDoS : validation des regex utilisateur (longueur max, rejet des
quantificateurs imbriqués/backrefs), contenu tronqué et nombre de matchs plafonné
(
backend/services/regex_safety.py), appliqué à la recherche avancée et au find/replace. - BUG-026 — SSRF webhooks : validation d'URL (HTTPS par défaut, IP privées/boucle
interdites, résolution DNS vérifiée au dispatch, redirections non suivies) et
externalisation du secret dans
data/webhook_secrets.json(0600) ou variableOBSIGATE_WEBHOOK_SECRET_<ID>(plus de secret en clair danswebhooks.json). - BUG-027 — sessions : rotation du refresh token à chaque usage, révocation du JTI de l'access token au logout, et vérification de la révocation dans le middleware.
- BUG-028 — politique de mot de passe : validation centralisée (8–128 caractères) à la
création, à la modification admin et au changement ;
password_changed_atinvalide tous les jetons émis avant un changement de mot de passe. - BUG-029 — race
users.json: verrouthreading.RLockautour des cycles lecture-modification-écriture. - BUG-030 — audits : l'adresse IP réelle du client (
X-Forwarded-ForsiOBSIGATE_TRUST_PROXY=true) est injectée danscurrent_useret consignée dans les audits. - BUG-031 — rate-limiter : budget par compte en plus du budget par IP (rotation d'IP neutralisée) ; limite mono-process documentée.
- BUG-032 — indexation :
_scan_vaultutiliseos.walk(followlinks=False)et refuse tout symlink sortant de la racine du vault. - BUG-033 — recherche O(N) : la recherche simple et l'outil IA
search_fulltextutilisent l'inverted index (repli sur le scan pendant la construction). - BUG-034 — CSP & jetons : le token d'accès n'est plus persisté dans
sessionStorage(mémoire + cookieHttpOnly) ; directives CSP durcies (object-src,base-uri,form-action,frame-ancestors). Reste : migration CSP par nonce (exige la conversion des gestionnaires d'événements inline).
- BUG-021/022 — XSS stocké : nouveau sanitizer serveur en liste blanche
(
Ajouté
-
#83 Barre d'outils d'édition mobile — ruban style Obsidian Android — la barre de mise en forme flottante est remplacée par un ruban horizontal défilable ancré juste au-dessus du clavier virtuel : fond anthracite aux coins arrondis, insertion/enrobage au curseur ou sur la sélection. Commandes : annuler/refaire,
[[ ]](lien interne), fichiers/modèles, tag#, pièce jointe, H1–H6, gras, italique, barré (~~), surligné (==), code en ligne/bloc, citation, lien externe, listes à puces/numérotées, case à cocher, indenter/désindenter, coller, A−/A+. Une icône ⚙ ouvre un panneau de personnalisation (ajouter / retirer / réordonner, persistancelocalStorage). i18n FR/EN. Helpers purs exportés (formatChange,normalizeRibbonCommands,moveRibbonCommand). Tests JSDOMtests/frontend/mobile-editor.test.mjs(+13) et E2E mobile. -
#82 Assistant IA — menu
@instantané — nouvel endpointGET /api/vault/{vault}/paths(liste plate et plafonnée de l'index des chemins). Le frontend la récupère une fois par vault (préchargée à l'ouverture du panneau) puis filtre côté client pendant la frappe : l'affichage des fichiers/répertoires est immédiat, au lieu d'une requête réseau par caractère. Tests :tests/test_api_main.py(+2),tests/frontend/ai.test.mjs(+1). -
#82 Assistant IA — Section « Fournisseur & modèle » compacte — la barre de sélection du modèle devient discrète : la liste complète des types d'endpoints (Chat, Embeddings, Vision…) est remplacée par un bouton d'information ⓘ qui affiche les capacités dans une bulle au survol, au clic ou par appui long (mobile). Le choix du modèle passe par une liste déroulante avec recherche (filtrage insensible aux accents) au lieu d'un
<select>natif, tout en conservant un select masqué pour les commandes admin (/model). Le picker partagé (frontend/js/ai.js) est utilisé par l'assistant et la barre d'édition IA. Tests JSDOM (tests/frontend/ai.test.mjs+1). -
#81 Assistant IA — Commandes
@//, skills, images & capacités des modèles — l'assistant devient plus flexible et multimodal. Panneau redimensionnable à la souris (largeur 320–1000 px persistée). Commande@pour attacher des contextes ad-hoc (fichiers, répertoires) au contexte courant. Commande/pour lancer des skills réutilisables (/research,/resume,/actions,/reformuler,/correction,/brainstorm,/plan,/ask,/meeting-note,/livrable) ou des commandes admin (/help,/providers,/provider,/model,/keys) ;/create-new-skillpermet de créer des skills personnalisés persistés par utilisateur (data/skills.json). Analyse d'images : coller une image dans la zone de saisie ou référencer une image d'un répertoire (@image.png), avec garde-fou de compatibilité vision. Capacités des modèles (Chat, Embeddings, Rerank, Images, Video, Audio Speech, Audio Transcriptions, Vision) affichées à la sélection dans l'assistant et le panneau de configuration. Backend : table curéebackend/model_capabilities.py, endpointsGET /api/ai/model-capabilitiesetGET /api/ai/skills, support multimodal dansbackend/ai_chat.pyetbackend/bookslm_routes.py. Tests :tests/test_model_capabilities.py(16),tests/test_skills.py(16),tests/test_ai_vision.py(12)- extension
tests/test_ai_models.pyettests/frontend/ai.test.mjs(+9). Détail : docs/features/ai-assistant-commands.md.
- extension
-
#77 Desktop — Signature des mises à jour Tauri — la paire de clés
minisignde l'updater est générée et sa clé publique est embarquée dansdesktop/tauri.conf.json(plugins.updater.pubkey, remplace le placeholder) ;bundle.createUpdaterArtifacts: trueproduit les fichiers.sigpar artefact. Le workflow.gitea/workflows/desktop-build.ymlexpose les secretsTAURI_SIGNING_PRIVATE_KEY/TAURI_SIGNING_PRIVATE_KEY_PASSWORDaux builds Windows et Linux, avec repli automatique en build non signé si le secret est absent (CI toujours verte). Procédure complète : docs/DEVELOPMENT_AND_RELEASES.md. -
#77 Desktop — Manifeste de mise à jour
latest.json— nouveauscripts/updater_manifest.pyqui construit le document consommé par l'updater Tauri (version, notes,pub_date,platformsWindows/Linux avec signature.siget URLs des assets). Il est généré automatiquement parscripts/publish_release.py(écritdesktop/latest.json, l'ajoute aux assets de la release, upload des.sig) et peut être lancé seul. L'endpoint de l'updater (desktop/tauri.conf.json) pointe désormais sur le manifeste versionné.../raw/branch/main/desktop/latest.json. Les builds locaux (build-windows.bat,build-linux.sh) détectent automatiquementdesktop/obsigate-updater.keypour signer, ou désactivent les artefacts de mise à jour s'il est absent. Tests :tests/test_updater_manifest.py(8). -
#77 Desktop — Protocole de tests E2E manuels — nouveau
docs/DESKTOP_E2E_CHECKLIST.md: prérequis, étapes et résultat attendu pour les 6 scénarios OS non automatisables (installation, tray, notifications natives, association.md, auto-update, désinstallation), avec emplacements des logs/config et procédure de signature de l'updater Tauri. -
#70 Recherche sémantique — Embeddings vectoriels — la recherche comprend désormais le sens de la requête en plus des mots-clés. Embeddings : chaque document est découpé en chunks de 512 mots (recouvrement 64) puis vectorisé (384 dim) via
all-MiniLM-L6-v2(sentence-transformers), un endpoint/embeddingscompatible OpenAI, ou un provider de repli sans dépendance (hachage déterministe). Stockage vectoriel :numpy/faiss(IndexFlatIP) si disponibles, sinon cosinus pur Python. Recherche hybride : fusion du classement TF-IDF et du classement sémantique par RRF (Reciprocal Rank Fusion), avecsemantic_scorepar résultat. Indexation incrémentale branchée sur le watcher (un fichier modifié régénère son embedding). UI : toggle « Recherche sémantique » (~, raccourciAlt+S) dans la barre de résultats + affichage du score de similarité, clés i18n FR/EN. Nouveau modulebackend/semantic_search.py, paramètresemanticsur/api/search/advanced, dépendances optionnelles dansbackend/requirements-semantic.txt. Tests :tests/test_semantic_search.py(27) +tests/frontend/semantic-search.test.mjs(4). Détail : docs/features/semantic-search.md. -
#69 Éditeur mobile natif — Interface tactile optimisée — refonte de l'expérience d'édition sur téléphone/tablette. Barre d'outils flottante dans l'éditeur (gras, italique, code, liste à puces, lien) opérant directement sur la sélection CodeMirror (ou le textarea de secours), avec toggle (re-appui pour dé-formater) ; bouton « Coller » persistant qui contourne la restriction presse-papiers d'iOS (repli sur un message d'aide si l'accès est refusé). Adaptation CodeMirror : zoom par pincement à deux doigts, boutons A−/A+ et hauteur ajustable via une poignée (valeurs persistées en
localStorage). Côté lecture : raccourcis swipe (gauche → liens entrants, droite → table des matières) et mode lecture plein écran (masquage de l'en-tête, des barres latérales et de la barre mobile, API Fullscreen, swipe horizontal pour passer au fichier suivant/précédent du même dossier). Nouveau modulefrontend/js/mobile-editor.js, stylesfrontend/style.css, clés i18n FR/EN. Tests :tests/frontend/mobile-editor.test.mjs(22 tests). Détail : docs/features/mobile-editor.md. -
#62 Collaboration temps réel — Édition simultanée — plusieurs utilisateurs peuvent éditer le même document markdown en même temps, façon Google Docs. WebSocket : nouvel endpoint
/ws/collab/{vault}/{path}(une room par fichier) qui relaie les changements instantanément ; authentification manuelle (cookieaccess_tokenou paramètretoken) +check_vault_accessetresolve_safe_pathpar connexion. Yjs/CRDT côté client :Y.Doc/Y.Textfusionnent les modifications concurrentes sans conflit ni perte. Awareness : curseurs distants colorés et sélections dans CodeMirror, indicateur de présence (avatars + statut) dans l'en-tête de l'éditeur. Persistance serveur : le dernier texte reçu est écrit sur disque avec un debounce de 2 s (backend/collab.py). Reconnexion automatique avec backoff exponentiel et merge de l'état au retour. Nouveau module frontendfrontend/js/collab.js(provider + liaison Y.Text↔CodeMirror + curseurs distants) etbackend/collab.py(CollabManager). Tests :tests/test_collab.py(17 tests, dont 5 clients simultanés) ettests/frontend/collab.test.mjs. Détail : docs/features/collaboration.md. -
#79 Phase F — Durcissement & documentation (rate limiting, redaction, OpenAPI/MCP, E2E) — clôture de la feature #79. Rate limiting par jeton et par outil (
backend/tools/ratelimit.py, fenêtre glissante ; identité = JTI du jeton sinon id/username ;OBSIGATE_TOOL_RATE_LIMIT,OBSIGATE_TOOL_RATE_LIMIT_PER_TOOL,OBSIGATE_TOOL_RATE_WINDOW; erreurrate_limited). QuotasBOOKSLM_MAX_*:BOOKSLM_MAX_TOOL_CALLSplafonne les appels d'outils par run d'agent (stopped="quota_exceeded") etBOOKSLM_MAX_TOOL_READ_BYTESplafonneread_file. Redaction systématique de tout résultat d'outil avant retour au LLM (backend/tools/redaction.py, récursif : diffs, extraits de recherche, lectures). OpenAPI : tagMCP+ injection du path/mcp(Streamable HTTP) dans le schéma ; nouveaudocs/MCP_GUIDE.md(config Claude Desktop/Cursor, tools/resources/prompts, sécurité, variables, dépannage). Tests E2E :tests/test_ai_e2e.py(agent read→confirm→write, quota, rate limit, redaction, flux MCPread→propose→apply→resources/read). -
#79 Phase E — Serveur MCP (Streamable HTTP) — nouvel endpoint
/mcpexposant ObsiGate à des clients MCP externes (Claude Desktop, Cursor…) via le SDK Pythonmcp==1.9.4. Les deux fronts (assistant in-app et MCP) consomment la même couche d'outils. Tools : outils de lecture/recherche directs ; outils d'écriture/destructifs en two-steppropose_<tool>/apply_<tool>avec jeton JWT signé, usage unique et TTL (OBSIGATE_MCP_CONFIRMATION_TTL, défaut 300 s) et blacklist de JTI persistée (data/mcp_used_tokens.json, anti-rejeu). Resources :vault://<name>etvault://<name>/<path>(lecture seule, secrets redactés). Prompts :summarize-directory,generate-note,find-related. AuthAuthorization: Bearer <JWT>→get_current_user; permissions et toggle destructif par vault (aiDestructiveTools) appliqués. Nouveaux modulesbackend/mcp/server.pyetbackend/mcp/confirmations.py; dépendancesmcp==1.9.4+sse-starlette==2.1.3(compatibles FastAPI 0.110 / starlette 0.37). Tests :tests/test_mcp.py(13 tests). -
#79 Phase D — Catalogue d'outils mutations + confirmations two-step — 11 nouveaux outils IA destructifs ou d'écriture exposés par la couche partagée (
backend/tools/service.py) :create_file,create_directory,edit_file,append_to_file,rename_file,rename_directory,move_path,replace_in_files,delete_file,delete_directory,restore_backup. Nouveau service réutilisablebackend/services/mutations.py(anti path-traversal, garde lecture seule, backup automatique avant écriture/suppression/restauration) ; les routes/api/file/{vault}(POST/PATCH/DELETE),/api/file/{vault}/save,/api/directory/{vault}(POST/PATCH/DELETE),/api/move/{vault},/api/file/{vault}/restoreet/api/search/replacedélèguent désormais à cette couche.backend/services/backups.pyexposecreate_backup(source unique, réutilisée parmain._backup_file). Confirmations two-step : tout outilWRITE/DANGEROUSdéclencheToolConfirmationRequired(carte Apply in-app,propose/applycôté MCP en phase E). Toggle par vaultaiDestructiveTools(défaut : activés) pour désactiver delete/rename/move/replace. Tests :tests/test_tools_mutations.py(40 tests). -
#79 Phase C — Catalogue d'outils lecture & recherche — 10 nouveaux outils IA exposés par la couche partagée (
backend/tools/service.py) :list_all_files,read_file_raw,get_backlinks,list_backups,diff_backup,get_graph,search_advanced,search_paths,suggest_tags,list_recent. Nouveaux services réutilisablesbackend/services/backups.py,graph.py,recent.pyet extensions devaults.py(list_all_files) /search.py(advanced_search_vaults,search_paths). Les routes/api/recent,/api/vault/{vault}/files,/api/file/{vault}/backups,/api/file/{vault}/diff,/api/graph/{vault},/api/tree-searchet/api/search/advanceddélèguent désormais à ces services (source unique de vérité). Filtrage systématique par permissions vault ; secrets redactés surread_file/read_file_raw. Tests :tests/test_tools.py(+17 tests). -
#79 Assistant IA — Outils (function calling) & serveur MCP — Phase 0 (couche d'outils partagée) — nouveau paquet
backend/tools/:context.py(ToolContext),registry.py(décorateur@tool- schéma JSON),
schemas.py,service.py,audit.py(journalisation JSONL des appels), façadebackend/tools/api.py. Services lecture/recherche livrés (list_vaults,list_directory,read_file,search_fulltext,list_tags) ; permissionscheck_vault_access+resolve_safe_pathappliquées par outil. Tests :tests/test_tools.py(30 tests).
- schéma JSON),
-
#79 Phase B (partielle) — function calling in-app — abstraction tool-calling provider-agnostique
backend/ai_chat.py(chat_completion,ToolCall,LLMResponse, OpenAI-compat- Gemini) ; agent loop
backend/agent/loop.py(boucle tool→résultat→tool, limite 10 itérations, truncation) ; endpoint opt-inPOST /api/ai/bookslm/agent(events SSEtool/message/confirmation) ; fallback automatique en chat simple si le provider rejette les tools. Tests :tests/test_agent_loop.py,tests/test_ai_chat.py,tests/test_bookslm.py.
- Gemini) ; agent loop
-
#79 A2/B4/B5 — services partagés, SSE streaming & confirmations UI — extraction de la logique lecture/recherche dans
backend/services/(errors.py,paths.py,vaults.py,files.py,search.py) : routes REST et outils IA consomment la même source ;ServiceErrormappée versHTTPException(routes) etToolError(outils)./api/ai/bookslm/chatstreame réellement les tokens (stream_completion, OpenAI-compat + Gemini ; GZip ignoré pour les SSE BooksLM). Mode agent côté UI (toggle, événementstool/confirmation, carte Apply + aperçu diff LCS) et repriseconfirm/confirm_messagesde l'agent loop (confirmation one-shot). Tests :tests/test_services.py,tests/test_ai_chat.py,tests/test_agent_loop.py,tests/test_bookslm.py. -
#79 G — Sélection fournisseur/modèle par défaut —
ai_default_provider+ai_default_modelspersistés dansdata/config.json, rechargement à chaud dansbackend/ai.py, sélecteurs « Fournisseur par défaut » / « Modèle par défaut » dans#cfg-ai, i18n FR/EN. -
#80 Assistant IA — Rendu Markdown, liens fichiers/paths & sessions — réponses de l'assistant rendues en Markdown formaté (titres, listes, tableaux, citations, code, emphase) via un renderer auto-contenu ; les fichiers et chemins mentionnés deviennent des liens cliquables (clic fichier → ouverture dans le viewer, clic répertoire → révélation/surlignage dans l'arborescence, outils
open_file/reveal_in_treecôté in-app) ; gestion des sessions par contexte (historique consultable, rechargeable, supprimable) avec migration de l'ancienne conversation unique. Tests :tests/frontend/ai.test.mjs.
Modifié
-
Barre inférieure mobile — couleurs harmonisées — les boutons « settings.search », « Recherche » et « Onglets » adoptent la couleur d'accent (
--accent) de l'icône et du libellé du bouton « Commandes » (frontend/style.css), pour un rendu uniforme de la barre du bas. -
#82 Assistant IA — sélecteurs Fournisseur/Modèle alignés à droite et réordonnés — dans la barre de l'assistant, le groupe de sélection est aligné sur le bord droit et l'ordre devient capacité du modèle (ⓘ) → fournisseur → modèle. La bulle de capacités s'ouvre désormais vers la droite (
left: 0) pour ne pas déborder de la barre latérale. Tests JSDOM (tests/frontend/ai.test.mjs). -
scripts/bump_version.sh— synchronisation de la version desktop — le script met désormais à jourdesktop/tauri.conf.json,desktop/Cargo.tomletdesktop/Cargo.lock(obsigate-desktop) avant de créer un commit de releasechore(release): vX.Y.Zpuis le tag.--pushpousse la branche et le tag ;--no-filesconserve l'ancien comportement (tag seul) et--dry-runprévisualise. Indispensable au bon fonctionnement de l'updater Tauri (dont le manifeste reprend la version detauri.conf.json). -
.gitea/workflows/desktop-build.yml— déclenchement manuel uniquement — le workflowDesktop Buildne s'exécute plus surpush(aucun runner self-hosted[windows/linux, desktop]enregistré) mais uniquement viaworkflow_dispatch. Le build Windows se fait en local (desktop/build-windows.bat) ; le workflow reste disponible pour un futur runner. -
Assistant IA (
frontend/js/bookslm.js) — l'ancien rendu Markdown minimal est remplacé par un renderer bloc/inline complet ; l'en-tête expose un bouton « Historique des sessions » ; les badges de sources ouvrent désormais réellement le fichier (événementobsigate:open-file). -
Assistant IA (
frontend/js/bookslm.js) — bouton « mode agent » dans l'en-tête (persisté enlocalStorage) : l'envoi bascule vers/api/ai/bookslm/agent, affiche la trace des appels d'outils et les cartes de confirmation (Apply + aperçu diff) ;/chatreste le défaut. i18n FR/EN.
Modifié
- #82 Assistant IA — barre latérale épurée & suivi visuel des requêtes — la section
« Fournisseur & modèle » perd ses intitulés (titre de section et libellé « Fournisseur : »)
pour rester discrète ; la description du contexte Général n'est plus affichée (bandeau de
statut masqué, description accessible au survol de l'en-tête) ; le placeholder
« Posez une question sur ces documents… » est retiré ; l'indice clavier
« Entrée pour envoyer · Ctrl+Entrée… » est retiré ; le bouton « Envoyer » devient un emoji
compact (✈️). Les sélecteurs Fournisseur/Modèle adoptent la taille des autres contrôles du
site (police 0,8 rem, hauteur 34 px, rayon 6 px) au lieu d'être plus fins. Un indicateur
d'activité apparaît pendant le traitement et détaille le workflow : envoi, réception de la
réponse, appels d'outils (
Outil : X…), attente de confirmation, succès ou échec. Le menu contextuel de la racine d'une vault expose aussi « 🧠 BooksLM », comme les répertoires. i18n FR/EN (ai.activity_*,ai.composer_label). Tests JSDOM (tests/frontend/ai.test.mjs+2).
Corrigé
-
BUG-017 — Accueil mobile : tuiles « Favoris » / « Récents » mal dimensionnées — les cartes de tableau de bord débordaient horizontalement à cause d'un titre/chemin
nowrap: ajout demin-width: 0etoverflow: hiddensur.dashboard-card(et ses en-têtes/pieds) pour que la grille mobile (1 colonne) respecte la largeur du viewport. Les tuiles « Partagés » étaient déjà conformes. -
BUG-018 — Éditeur mobile : boutons « Annuler » / « Sauvegarder » inaccessibles — l'en-tête cumulait marque, titre, nom de fichier et actions, poussant les boutons hors écran. Sur mobile, les éléments secondaires (marque, séparateur, espaceur, présence) sont masqués, les champs titre/nom de fichier deviennent compressibles (
flex,min-width: 0) et le libellé « Saved » est masqué (le point d'état reste). La barre d'outils IA reçoit des cibles tactiles plus grandes. -
BUG-019 — Éditeur mobile : barre d'outils masquée par le clavier — le ruban est désormais ancré via
window.visualViewport(--kb-offset) pour rester juste au-dessus du clavier, quel que soit le défilement, et le scroller CodeMirror reçoit unpadding-bottompour ne pas cacher le curseur. -
BUG-020 — Éditeur mobile : double barre de défilement —
.editor-bodypasse enoverflow: hidden+ colonne flex ; seul le scroller CodeMirror défile. -
BUG-016 — Mobile : le bouton « mode lecture » recouvre le bouton d'envoi de l'assistant — le bouton flottant
📖(#me-reading-btn,z-index: 890) passait au-dessus du panneau assistant plein écran sur mobile (z-index: 100) et masquait le bouton d'envoi✈️. Il n'apparaît désormais que lorsqu'un fichier est ouvert (state.currentPath) et que le panneau assistant est fermé ; sa visibilité est resynchronisée à chaque mutation de#content-areaet sur les événementsbookslm:opened/bookslm:closed. Tests JSDOM (tests/frontend/mobile-editor.test.mjs+2) et E2E mis à jour. -
BUG-015 — Assistant IA : sélection des menus
/et@invisible au clavier — l'élément actif utilisaitbackground: var(--surface2), or--surface2est identique à--bg-primary(fond du menu) dans le thème sombre par défaut : la ligne sélectionnée par les flèches ↑/↓ ne se distinguait pas. L'état actif (et le survol) utilise désormais--bg-hoveravec une barre d'accent à gauche (box-shadow: inset 3px 0 0 var(--accent)), pour les menus de commandes/mentions et la liste de modèles. Test JSDOM mis à jour. -
BUG-014 — Assistant IA : navigation clavier ↑/↓ des menus inopérante — la navigation était liée au
keydowndu champ de saisie : dès que le focus quittait la zone de texte (clic sur le menu, bouton Envoyer…), les flèches ne faisaient plus défiler les items. La gestion est déplacée au niveau du panneau en phase de capture, ce qui fonctionne quel que soit l'élément focalisé dans la barre latérale. Tests JSDOM (tests/frontend/ai.test.mjs+1). -
BUG-013 — Assistant IA : « Aucun vault actif pour ouvrir ce lien » — les liens fichiers/répertoires des réponses utilisaient
_resolveVault()seul, nul en mode Général sans document ouvert._openFileLink()et_revealPath()utilisent désormais_activeVault()qui retombe sur le vault de contexte, le vault sélectionné dans la barre latérale, puis le premier vault disponible. Tests JSDOM (tests/frontend/ai.test.mjs+1). -
Assistant IA — caches obsolètes —
SW_VERSIONporté à v6 et migration de purge élargie à tous les cachesobsigate-*(cléobsigate-sw-migration→v3), afin que les clients bloqués sur une ancienne version récupèrent bien le menu@et les correctifs. -
BUG-012 — Assistant IA : la commande
@n'affichait aucun menu en mode Général — sans vault résolu, la détection@ne listait rien (menu masqué) et le backend ignorait ensuite le contexte. Le menu est désormais toujours rendu et_mentionVault()retombe sur le vault de contexte de la barre latérale, puis sur le premier vault disponible, avant de basculer survault=allpour les recherches. Tests JSDOM (tests/frontend/ai.test.mjs+1). -
BUG-011 — Assistant IA : noms de modèles illisibles dans la liste déroulante — la liste déroulante des modèles s'ouvrait alignée à gauche (
left: 0) et pouvait dépasser le bord droit de la barre latérale (ancrée à droite), rendant les noms tronqués/illisibles ; elle est désormais alignée à droite du déclencheur, large de 340 px (bornée à100vw - 24px), et les noms reviennent à la ligne (overflow-wrap: anywhere, police 0,78 rem) avec l'info-bulletitlecomplète. Tests JSDOM (tests/frontend/ai.test.mjs). -
BUG-010 — Assistant IA : la commande
@n'ajoutait pas le contexte en mode Général — les fichiers/répertoires choisis via@étaient bien ajoutés sous forme de puces, mais le vault n'était pas transmis : en mode Général (_vaultnul), la requête partait sansvaultet le backend ignorait le contexte ad-hoc. La sélection capture maintenant levaultrenvoyé par/api/tree-search,_contextVault()le propage aux requêtes/contextet/chat, et les recherches suivantes restent dans ce vault. Tests JSDOM (tests/frontend/ai.test.mjs). -
BUG-009 — Assistant IA : liste de modèles corrompue et clés i18n brutes — deux causes : (1) les fichiers de locale n'étant pas content-hashed, un
fr.jsonen cache HTTP affichait les clés brutes (ai.model_search,mobile_editor.*, …) ; le chargement i18n utilise désormaiscache: 'no-store'etSW_VERSIONest incrémenté pour purger les anciens caches du service worker. (2) la liste de modèles (OpenRouter = plusieurs centaines d'entrées) pouvait se retrouver non stylée si la feuille de style était en cache ; le picker applique maintenant les styles critiques en ligne (popover, liste en colonne, optionsdisplay:block), plafonne le rendu à 200 entrées avec un indicateur « … N autres — affinez la recherche », et la recherche filtre le reste. Tests JSDOM (tests/frontend/ai.test.mjs+1). -
BUG-007 — Assistant IA : menus
/et@(navigation ↑/↓ et filtrage accentué) — la détection des commandes/mentions et le filtrage des skills utilisaient des motifs ASCII ([a-z0-9-],\w) : dès qu'un caractère accentué était saisi (/résumé,@café), le menu se fermait. Les motifs sont désormais Unicode (\p{L}) et la recherche est insensible aux accents (normalisation NFD). La navigation clavier est fiabilisée par un jeton de séquence qui ignore les rendus asynchrones obsolètes (frappe rapide) et fait défiler l'élément actif dans la vue (scrollIntoView). Tests JSDOM (tests/frontend/ai.test.mjs+3). -
BUG-008 — Assistant IA : commande
@et contexte ad-hoc en mode Général — deux causes : (1) le backend ignorait les fichiers/répertoires ad-hoc en mode Général car_resolve_system_promptne résolvait pas de vault (backend/bookslm_routes.py) ; le vault optionnel est maintenant résolu dès que du contexte@est présent. (2) sans vault courant, la recherche de mention échouait ; elle bascule désormais survault=all. Tests backend (tests/test_bookslm.py+2) et frontend. -
#77 Desktop — bannière de premier lancement réaffichée à chaque démarrage — l'état du wizard « Choisissez votre vault » n'était mémorisé que dans le
localStoragede la webview et le paramètrehasVaultPathn'était jamais renseigné. Ajout d'un booléen persistantwizard_donedans la config desktop (desktop/src/main.rs, commandesget_wizard_state/complete_wizard, rétro-compatible via#[serde(default)]) : la bannière ne réapparaît plus après un choix de dossier ou un clic « Plus tard », même si le stockage webview est vidé. Tests Rust (desktop/src/main.rs) et JSDOM (tests/frontend/desktop.test.mjs) ajoutés. -
Build Docker — « No space left » lors de
apt-get install— le stage builder installait inutilementlibpango-1.0-0,libpangocairo-1.0-0etshared-mime-info(libs runtime de WeasyPrint, sans usage à la compilation), dupliquant les téléchargements apt. Ces paquets sont retirés du builder etapt-get cleanest ajouté dans les deux stages pour purger les.debconservés par défaut sous Debian trixie, réduisant l'empreinte disque et le temps de build. En complément, purge du cache de build (docker builder prune) ayant libéré l'espace nécessaire. -
BUG-005 — Chargement mobile incomplet derrière Cloudflare (
og.dracodev.net) — le service worker utilisait une stratégie cache-first avec un nom de cache fixe (obsigate-v1) et un précache de chemins erronés (/frontend/js/…) : le Cache Storage du SW (distinct du cache navigateur/Cloudflare) servait un ancien build indéfiniment, même après purge. Le middleware renvoyait en plusCache-Control: public, max-age=31536000, immutablesur des assets non fingerprintés. Correctifs :frontend/sw.jsréécrit (network-first pour HTML/JS/CSS, caches versionnésSW_VERSION, précache corrigé/static/js/…,skipWaiting/clients.claim) ; migration ponctuellelocalStorage(suppression des anciens caches) en remplacement du kill-switch de session ; en-têtesno-cachesur/static,index.html,manifest.jsonet pages HTML ; rechargement unique surcontrollerchange. Tests :tests/frontend/sw.test.mjs+TestStaticCaching. Guide Cloudflare : docs/PWA_GUIDE.md. -
Typage backend (mypy) — 33 erreurs corrigées (
backend/main.py,indexer.py,auth/router.py,pdf_reader.py,export.py,bookslm_routes.py) : annotations de types, gardesNonesurget_user(),PdfReader: Anyet importPROVIDERSmanquant dansmain.py(bug latent : le modèle par défaut n'était jamais prépendé à la liste des modèles live). L'étapemypydu CI devient bloquante (elle était en mode advisory). -
README — lien « Contributing » corrigé vers
docs/CONTRIBUTING.md(était cassé vers la racine) ; arbre du projet mis à jour. -
#79 façade
backend/tools/api.py— gestion des namespace packages et du__init__ignoré lors du chargement des modules d'outils.
Documentation
-
Guide d'architecture IA —
docs/AI_ARCHITECTURE_GUIDE.md(architecture, catalogue d'outils, sécurité, phases). -
Refonte de la documentation — la Roadmap ne contient plus que le travail à venir + un index compact ; les fonctionnalités livrées sont archivées dans
docs/archive/COMPLETED_v1-v2.mdet documentées par feature dansdocs/features/. -
Méthode de livraison unifiée — nouveau
docs/DELIVERY_WORKFLOW.md(Definition of Done : tests, documentation, commit, push, CI) etAGENTS.md(instructions obligatoires lues à chaque session). Référencés depuis la Roadmap, CONTRIBUTING et ISSUES_TODOLIST.
[2.2.1] — 2026-09-11
Ajouté
- Mobile — assistant AI central — bouton assistant dans la barre du bas (commit
50b823e).
Corrigé
- Version affichée dans l'UI — affichait
2.1.0au lieu de2.2.0(commit84bda90). - Mobile — boîte d'édition de l'assistant relevée au-dessus de la barre du bas (commit
f307ecb).
[2.2.0] — 2026-09-11
Ajouté
-
Assistant AI contextuel (sidebar) — l'assistant s'adapte désormais au contexte d'ouverture :
- Répertoire (
directory) : ouvert depuis le menu contextuel d'un dossier — le contexte est le dossier et son contenu (mode historique BooksLM). - Documents (
documents) : ouvert via le bouton flottant quand un ou plusieurs documents sont ouverts — le contexte est la liste des fichiers ouverts (tous onglets/panneaux). - Général (
general) : ouvert via le bouton flottant quand aucun document n'est ouvert — l'assistant répond sur l'application et peut proposer des actions (créer un fichier/dossier) via un blocobsigate-actionrendu en carte avec bouton « Appliquer » (aucune exécution automatique). - Le contexte est affiché dans le header de la sidebar (icône, titre, sous-titre) et les suggestions de questions s'adaptent au mode.
- Backend :
/api/ai/bookslm/contextet/chatacceptentmode(directory/documents/general) etcontext_files;collect_files_context(),empty_context()etbuild_general_system_prompt()dansbackend/bookslm.py; le mode documents dégrade proprement vers le mode général si aucun fichier n'est lisible. - Tests :
tests/test_bookslm.py(+13 tests modes/documents/général) ettests/frontend/ai.test.mjs(+8 tests détection de contexte, clavier, actions, header).
- Répertoire (
-
#72 API publique documentée — OpenAPI 3.1 enrichie — documentation interactive complète de l'API REST, générée automatiquement et enrichie.
- Backend
backend/openapi_docs.py: métadonnées de tags (18 catégories : System, Auth, Files, PDF, Vaults, Search, Bookmarks, Backups, Export, AI, BooksLM, Sharing, Webhooks, Conflicts, Admin, Plugins, Push, Frontend), assignation automatique des tags par préfixe de route (normalisation des tags des routeurs), schémas de sécurité (bearerAuth,cookieAuth), exemples de requête/réponse sur les endpoints clés, réponses d'erreur documentées (401/403/404/422/500), serveur etexternalDocs. backend/schemas.py: nouveauxresponse_modelPydantic pour les endpoints qui n'en avaient pas (recent, bookmarks, saved searches, backups, diff/restore/backlinks, pdf/info, replace, vaults, attachments, settings, config, AI keys/models, diagnostics, dashboard, webhooks, shares, conflicts, BooksLM context, AI status).backend/main.py:FastAPI(...)enrichi (description Markdown, contact, licence, tags) et overrideapp.openapipour post-traiter le schéma ; page de documentation/api(HTML autonome listant les endpoints groupés par catégorie, liens vers/docs,/redoc,/openapi.json).- Frontend : entrée « API » dans le menu d'options (i18n FR/EN) ouvrant Swagger UI.
- Tests :
tests/test_openapi.py(58 tests — version 3.1, tags, sécurité, erreurs, exemples, schémas de réponse, pages/api,/docs,/redoc,/openapi.json).
- Backend
-
#61 Plugins système au complet — système de plugins permettant d'étendre ObsiGate (renderers personnalisés, filtres de recherche, actions d'éditeur), sandboxé pour la sécurité.
- Backend
backend/plugins.py: validation du manifestplugin.json(name regex lowercase, semver, hooks/permissions autorisés), stockage par vault<vault>/.obsigate-plugins/, lifecycle complet (install/uninstall/enable/disable via marker.disabled), validation ZIP à l'upload (anti path-traversal, max 100 fichiers, 500KB/fichier, 5MB upload), 9 endpoints/api/plugins/*(list/get/hooks/code/template + install/uninstall/enable/disable admin-gated), template API, scan au démarrage par vault (get_plugin_registry().scan_vault). - Frontend
frontend/js/plugins.js: PluginManager (install/uninstall/enable/disable/view-code), sandbox d'exécution via Web Worker (code chargé par blob URL, protocolepostMessagestructuré, isolation DOM/localStorage/network selon permissions), hooks dispatch (executeHook,onFileRender,onSearchFilter,onEditorAction,onSidebarItem,onFileCreate,onFileDelete,onVaultMount), UI Settings > Plugins. - Sécurité : manifest de permissions (
read_files,write_files,network_request, … restreint), CSP stricte sansimportScripts, limites de taille. - Tests :
tests/test_plugins.py(44 tests — validation manifest, lifecycle manager, validation ZIP/directory, démarrage scan) +tests/frontend/plugins.test.mjs(21 tests JSDOM — protocole sandbox Worker, isolation DOM, lifecycle mirror). - CI : job lint ajoute
node plugins.test.mjsaux tests JSDOM. - Docs :
docs/PLUGINS.md(manifest, hooks, permissions, modèle de sécurité, API, guide).
- Backend
-
#78 Excalidraw — finitions : décompression lz-string du format plugin Obsidian, création depuis la modale et le menu contextuel, extraction du texte pour la recherche (B5), support
.excalidraw.md(commitac16fc1+ correctifs associés). -
#67 Notifications web (Push API) :
backend/push.py,POST /api/push/subscribe, clés VAPID dansconfig.json, toggle par vault, payload (fichier/vault/action) et ouverture du fichier au clic (commitac16fc1). -
#68 Health check enrichi :
GET /api/health/detailed(admin-gated) exposant index, mémoire (RSS/heap), uptime, connexions SSE, backups et espace disque (commitac16fc1). -
#77 Desktop — jumplist vaults récents dans le menu Démarrer (commit
ac16fc1). -
#75 Split View — matrice E2E complète (37 tests) : 16 tests
split-view.spec.js(raccourcis, drag & drop, persistance, navigation clavier) + 21 testssplit-view-matrix.spec.jscouvrant 100% des cas d'ouverture/fermeture.
Documentation
- #78 Excalidraw — doc utilisateur finalisée :
.excalidraw/.excalidraw.mdajoutés aux formats supportés (README FR/EN), nouvelle section « Diagrammes Excalidraw » dans le guide intégré (i18n FR/EN) et note de compatibilité avec le plugin Obsidian Excalidraw. - Roadmap : #78 marqué terminé (B5 extraction texte, C8 menu contextuel, F3 E2E
tests/e2e/excalidraw.spec.js, H1-H3 docs) ; F2 non retenu. Points optionnels #61 (dépôt communautaire de plugins) et #74 (D2/E4/H2/I2) explicitement marqués non retenus.
Corrigé
-
Assistant AI — sidebar qui ne s'ouvrait plus & erreur de contexte :
- Le bouton flottant passait le chemin d'un fichier comme répertoire de contexte, d'où l'erreur « Aucun fichier markdown trouvé dans ce dossier » à l'ouverture sur un document. Le contexte est maintenant détecté automatiquement (documents ouverts → mode documents, sinon mode général).
- Un panneau précédemment masqué (
obsigate-bookslm-hidden=truepersisté en localStorage) restait caché lors d'une ouverture explicite → l'ouverture force désormais l'affichage. - Ergonomie du header : le sélecteur fournisseur/modèle était sur la même ligne que les actions et masquait ces dernières ; il est déplacé sur une seconde ligne dédiée (toolbar) pour que « nouvelle conversation / export / plein écran / fermer » restent toujours visibles.
- Clavier de la boîte de chat :
Entréeenvoie la requête,Ctrl+Entréeinsère un saut de ligne (au lieu de l'inverse), avec un texte d'aide sous le champ.
-
Outil AI de l'UI — fiabilisation :
- BooksLM (chat IA par répertoire) : les requêtes
context/chatn'envoyaient pas le jeton d'authentification (échec 401 quand l'auth est activée) et la réponse SSE était mal parsée (le backend émet{token: ...}, le front lisaitdata.content) → les réponses n'apparaissaient jamais. Le message utilisateur envoyé était le placeholder assistant vide au lieu du texte saisi. Corrigé : requêtes authentifiées viaAuthManager(retry 401), parsing SSEevent:/data:avec supporttoken/error, historique construit avant l'ajout du message courant, barre de progression basée surmax_total_chars. - AI Editor : le raccourci
Ctrl/Cmd+J(complétion inline) était re-lié à chaque ouverture de l'éditeur (N listeners → N requêtes) ; un seul listener global est désormais posé. Les libellés de la barre d'outils AI étaient codés en dur (FR/EN mélangés) → i18n complet. La réécriture personnalisée utilisaitwindow.prompt()→ remplacée par une modale accessible (Échap annule, Ctrl/Cmd+Entrée valide).
- BooksLM (chat IA par répertoire) : les requêtes
-
Recherche cassée (régression plugins) :
onSearchFilter()était appelée avec 2 arguments au lieu de 3 (frontend/js/search.js), renvoyaitundefined, faisait échouer le rendu des résultats et basculait silencieusement sur la recherche hors-ligne (0 résultat). Corrigé → la recherche full-text et l'autocomplétion refonctionnent (20 tests E2E). -
.excalidraw.mdcorrompu à l'ouverture : Excalidraw émetonChangeau montage, ce qui déclenchait un auto-save 2 s plus tard réécrivant le fichier au format JSON brut et détruisant le format plugin Obsidian (frontend/excalidraw-editor.html+frontend/js/excalidraw-viewer.js). Les changements initiaux sont désormais ignorés et le formatfrontmatter + compressed-jsonest préservé à la sauvegarde. -
Splash / tests E2E :
window.__OBSIGATE_BOOTEDétait posé avant la fin deinit()— le splash disparaissait trop tôt et les tests E2E interagissaient avant que les handlers soient liés. Le flag est maintenant posé après résolution deinit()(frontend/js/app.js), etgoHome()/login()attendent ce signal. -
Tests E2E Excalidraw : réécriture de
tests/e2e/excalidraw.spec.jsavec les sélecteurs réels (modale.obsigate-modal+#file-ext-select/#file-name-input, menu contextuel,canvas.first()pour les deux couches canvas).
[2.1.0] — 2026-09-08
Ajouté
- #74 Support PDF au complet —
GET /api/file/{vault}/pdf/info(métadonnées sans contenu), streaming avec HTTP Range / 206 Partial Content (rendu progressif des gros PDF), configOBSIGATE_PDF_MAX_SIZE_MB(50) +OBSIGATE_PDF_EXTRACT_TIMEOUT(30s). 16 tests supplémentaires (26 au total danstest_pdf.py). - #64 MFA — WebAuthn (second facteur en plus du TOTP) : enregistrement de clés de
sécurité / biométrie (Windows Hello, Touch ID), assertion au login, gestion des clés dans
« Sécurité du compte », codes de récupération émis à l'activation. Nouveau module
backend/auth/webauthn_mfa.py(libwebauthn==2.6.0, challenges in-memory TTL 180s à usage unique). 10 tests avec authentificateur virtuel (CBOR réel, ECDSA P-256). i18n FR/EN (13 clés). Config :OBSIGATE_WEBAUTHN_RP_ID/_RP_NAME/_ORIGINS. - #77 Desktop — port auto-increment :
pick_free_port()scanne 17890..17899 si le port est occupé (2 instances côte à côte possibles) + 3 tests Rust (19 au total côté desktop). - Docs : sections PDF README FR/EN (Range, /pdf/info), variables env, guide WebAuthn.
- #75 Split View — matrice E2E complète (37 tests) : 16 tests
split-view.spec.js(raccourcis, drag & drop, persistance, navigation clavier) + 21 testssplit-view-matrix.spec.jscouvrant 100% des cas d'ouverture/fermeture : boutons ⊞→/⊞↓, menu contextuel (diviser bas, fermer, fermer les autres/à droite/tout, fermer le panneau, flyout « Déplacer vers… »), palette (diviser, focus suivant/précédent, reset), fermeture par X/double-clic/molette/Ctrl+W, auto-collapse d'un panneau vidé, plafond 4 panneaux, drag-to-split bas, pas de doublon à la réouverture. - Fix
scripts/run-e2e-local.sh: chemins Windows viacygpath(les env vars MSYS n'étaient pas converties → vaults vides, les 16 tests split-view échouaient en local alors qu'ils passaient en CI).
Corrigé
- PDF stream 500 :
api_pdf_streamplantait systématiquement (NameError: current_usernon injecté — endpoint jamais couvert par un test). Désormais authentifié + testé. - Indexation incrémentale des PDF : le chemin watcher (
_index_single_file_sync) lisait les PDFs enread_text()→ contenu garbage indexé. Utilise maintenantextract_pdf_text()comme le scan complet.
[2.0.0] — 2026-08-25
ObsiGate passe à 2.0.0 avec sa version bureau native et de nombreuses fonctionnalités de productivité (branch main, 341 commits après le tag v1.8.0).
Ajouté
- Application bureau native (Tauri 2) — Porte d'entrée desktop pour vaults Obsidian (
desktop/) - Dashboard administrateur (#71, backend) — 4 endpoints admin-gated pour monitoring serveur
GET /api/admin/stats— CPU/RAM/Disk/Uptime via psutilGET /api/admin/audit— 500 dernières entrées d'audit avec filtresuser/action/limit/offsetGET /api/admin/backup-stats— compte + taille + age par vaultGET /api/admin/stream— Server-Sent Events qui push les stats toutes les 5s- Nouveau module
backend/admin.py+ 13 tests pytest
- Tests JSDOM PaneManager (#75, sous-tâche I2) — 9 tests d'intégration frontend via JSDOM 30
- Couvre : factory, init, open/activate/close, isolation entre panes, drag/drop smoke
- Wired dans le CI Gitea (
.gitea/workflows/ci.yml) - Shell Rust + Tauri 2 (tray icon, notifications OS, auto-update depuis Gitea)
- Backend Python embarqué (python-embed) avec health check, démarrage ~2s
- Sélecteur de dossiers natif, config persistante des vaults (type VAULT vs DIR)
- Fenêtre console masquée, arrêt propre via tray, associations de fichiers
.md - Packages : Windows MSI + NSIS, Linux .deb + AppImage
- Pipeline CI/CD Desktop (
.gitea/workflows/desktop-build.yml) - Thème système auto (clair/sombre) + RAM réduite (~80 MB au repos)
- Écran de chargement inline (staging backend/frontend pour le bundle desktop)
- Internationalisation FR/EN complète — 1357 clés i18n, préférence serveur, guide entièrement traduit
- Vue multi-panneaux (Split View #75) — PaneManager + TabManager par panneau
- Split droite/bas, drag & drop cross-pane, drag-to-split (bord droit/bas)
- Boutons de split, navigation clavier, fermeture auto des panneaux vides
- Mermaid.js — Rendu de diagrammes (22 templates, thème dark synchronisé)
- Header bar Code/Preview, zoom, export SVG + PNG, plein écran / focus 480px
- Pré-processeur Obsidian (
[[liens]],![[img]],==highlight==)
- Gestionnaire de Backups complet — Page, filtres, purge, restauration
- Diff viewer (unifié + côte à côte), preview 100 Ko, compression gzip
- Backup automatique, auto-nettoyage (max_backups_per_file)
- Support Excalidraw (#78) — Fichiers
.excalidraw/.excalidraw.md- Décompression lz-string, création depuis modale/menu contextuel
- Éditeur Forge + autocomplétion IA — Auto-height, Mermaid QI, ghost text
- Autocomplétion Ollama (qwen2.5-coder), Alt+/, auto-continuation listes/checkboxes
- Support PDF + multi-format — PDF avec TOC, images/CSV/JSON/TXT
- 15 thèmes clair/sombre (#65) — Système de thèmes configurable
- Mode hors-ligne PWA (#59) — Badge hors-ligne + gestion des conflits
- Tests E2E Playwright (#58) — Suite desktop (44 tests) + CI
Modifié
desktop/— Nouveau : application Tauri (Cargo.toml, build.rs,src/main.rs, scripts build)frontend/— i18n, split view, Mermaid, Excalidraw, Forge, thèmesbackend/— Endpoints backups, Excalidraw, multi-format, versioning gitdocs/ROADMAP.md— Feuille de route passée à 2.0.0-dev, items #75/#78 complétés
[1.8.0] — 2026-06-04
Ajouté
- Palette de commandes — Navigation rapide fichiers + commandes
- Raccourcis :
Ctrl+Pfichiers,Ctrl+Alt+Espacecommandes,Ctrl+Shift+Espace - Navigateur de dossiers : créer/supprimer fichiers/dossiers par sélection
- Opérateurs
+include/-excludedans la recherche globale - 6 nouvelles commandes : créer/supprimer fichiers/dossiers, éditer, pop-out
- Raccourcis :
- Barre d'outils mobile de bas de page — Type Obsidian 📱
- Explorateur, recherche, palette, changement d'onglet
- Indicateur de vault contextuel sous le header, pull-to-refresh
- Diff viewer des backups — Comparaison unifiée + côte à côte, restauration
- Indicateur AI Actif — Badge/statut IA dans le header (lecture runtime des clés)
- Page d'accueil de vault — Liste récursive des fichiers par date
- Drag & drop de fichiers dans l'arborescence
- Guide d'aide enrichi — Recherche, scroll spy, slide-over mobile, section webhooks
- Indexation inversée non-bloquante (
run_in_executor) - Infrastructure de tests — Couverture pytest portée à 70% (+109 tests)
Modifié
frontend/— Palette, mobile toolbar, aide, AI indicator, vault homebackend/— Indexation non-bloquante, statut IA, diff/restauration backupsdocs/— Guide d'utilisation enrichi (navigation clavier), CHANGELOG/README/ROADMAP
Corrigé
- Correctifs UI mobile, palette, aide et AI toolbar (thème clair, menu clipping)
[1.7.0] — 2026-05-30
Ajouté
- AI Editor Toolbar — Barre d'outils IA intégrée à l'éditeur CodeMirror
- Menus dropdown : Edit (improve, fix spelling, shorter, longer, simplify), Tone (professional, casual), Translate (multi-langue), Generate (explain, summarize, continue), Custom Rewrite, Toolbox (to list, to table, frontmatter, to canvas)
- Multi-provider : DeepSeek / OpenRouter / Gemini, configurable via
.env - 16 endpoints REST :
POST /api/ai/{edit|tone|translate|generate|rewrite|toolbox} - Auto-save silencieux (2s debounce) avec feedback visuel toast
- Cache toolbar si pas de clé API configurée
- Messages d'erreur clairs (401→clé invalide, etc.)
- Graph view — Améliorations majeures
- Header flat design 2 lignes responsives (titre flexible + recherche dédiée + barre statut)
- Navigation historique ← → + ↑ Parent
- Sticky panels : tuile info (top-left/right) avec métadonnées, preview pleine hauteur (droite)
- Ctrl+survol → aperçu contenu formaté (via
/raw) dans tooltip - Filtre par tag dans la vue graphe
- Boutons close ✕ sur les panneaux
- Fond hover foncé unifié
Modifié
- backends/ai.py — Nouveau module IA backend
- backends/ai_routes.py — Routes API pour l'éditeur IA
- frontend/js/ai.js — Module frontend de la toolbar IA (404 lignes)
- frontend/js/graph.js — Refonte complète : sticky panels, nav historique, aperçu markdown
[1.6.0] — 2026-05-29
Ajouté
- Vue graphe des wikilinks — Canvas force-directed
- Phase 1 : Full-vault view, filtre par tag, backlinks entrants, métriques de nœud
- Phase 2 : Recherche visuelle, tooltips survol, couleurs adaptatives, slider profondeur (0-3)
- Phase 3 : Filtre par type (dossier/fichier/.md/autre), mode focus, plein écran, export PNG
- Phase 4 : Barnes-Hut O(n log n), cache graphe
- sortedcontainers → O(log n) insert/remove dans l'index inversé
- CI/CD Pipeline complet — Gitea Actions
- Ruff linting (0 erreur)
- Mypy type checking (0 erreur)
- Pytest — 175 tests, 49% coverage
- Bandit SAST — scan de sécurité
- Pip-audit — vulnérabilités dépendances
- Docker build — vérification image
- Coverage artifact — uploadé via
actions/upload-artifact@v3 - Runner Gitea auto-hébergé (
gitea/act_runner:0.2.11sur Alpine/Proxmox)
Modifié
backend/search.py— Remplacement bisect → SortedList pour le vocabulaire.gitea/workflows/ci.yml— Pipeline CI/CD complet
Corrigé
- Résolution de 28 erreurs mypy pré-existantes
- Fix lint ruff + faux positifs bandit + pip-audit non-bloquant
[1.5.1] — 2026-05-28
Ajouté
- Split
app.js(8 875 lignes) en 16 modules ES- Modules :
state.js,utils.js,auth.js,ui.js(76KB),viewer.js(47KB),sidebar.js,search.js(45KB),config.js,dashboard.js,graph.js(33KB),sync.js,legacy.js,ai.js - Import/export validator CI (
tests/frontend/validate-imports.mjs) - Tests unitaires frontend (
tests/frontend/unit.test.mjs)
- Modules :
- Stemming français —
snowballstemmerintégré au tokenizer- "manger" trouve "mangé", "mangeons", "mangeait"
- Performance O(T) au lieu de O(S×T) — fix freeze 15min sur gros vaults
- Crash guard : try/except snowballstemmer IndexError sur tokens exotiques
Modifié
frontend/js/app.js→ entrypoint ES module important les 16 modulesbackend/search.py— tokenizer avec FrenchStemmer
Corrigé
- Nombreuses régressions post-split : imports manquants, exports oubliés, shadowing de variables
- Réécriture state.js → mutable object (
export const state = {...}) pour éviter les erreursassignment to constant - Plus de 60 correctifs d'imports/exports validés par le validator CI
- Tags de template
{{...}}exclus du tag cloud via filtre configurable
[1.5.0] — 2026-05-26
Ajouté
- Publication publique — Génération de lien partageable avec token unique (64-char hex)
- Expiration configurable, lecture seule, sans authentification
backend/share.py+ endpoints CRUD + vue publique/s/{token}- Export PDF via WeasyPrint (lazy import) dans la vue publique
- Support fichiers non-Markdown dans la vue publique
- Webhooks — Notifications HTTP POST vers services externes
- Signature HMAC-SHA256 optionnelle
backend/webhooks.py+ CRUD endpoints + UI dans Configuration
- Dashboard statistiques — Métriques agrégées par vault
- Widget
DashboardStatsWidgetavec 4 cartes (fichiers, tags, taille, vaults) - Widget "Derniers fichiers ouverts" avec timestamps
- Widget
- Documentation OpenAPI enrichie — Tous les modèles Pydantic documentés (
Field(description=...))- Visible dans
/docs(Swagger UI) et/redoc
- Visible dans
- Documentation complète —
ANALYSE_REVIEW.md,AUDIT_TECHNIQUE.md,ROADMAP.md
Modifié
frontend/js/app.js— Nouveau système d'onglets (TabManager), dashboardfrontend/style.css— Dashboard, share view, webhooks UIbackend/main.py— Nouveaux endpoints share/webhooks/dashboardbackend/search.py— Index inversé incrémental (hook pattern)
[1.4.0] — 2026-05-25
Ajouté
- Rate limiting — IP-based (10 tentatives/15min) + lockout par compte (5 tentatives)
backend/ratelimit.py— RateLimiter thread-safe avec nettoyage périodique- Intégré à
backend/auth/router.py
- Secret redactor — Masquage automatique des secrets dans les aperçus
backend/secret_redactor.py— JWT, API keys, tokens GitHub, clés privées, connection strings
- Audit log — Traçage des écritures/suppressions/changements de config
backend/audit.py— JSON lines dansdata/audit.log, rotation 10MB
- Backup automatique avant écriture
- Sauvegarde du contenu original dans
.obsigate-backup/avant PUT/DELETE
- Sauvegarde du contenu original dans
- Backlinks panel — Fichiers avec wikilinks pointant vers le fichier courant
GET /api/file/{vault}/backlinks+ index inversé dansindexer.py
- Gestion des conflits Syncthing
- Détection automatique des fichiers
.sync-conflict-* - Résolution : garder local, garder conflit
GET /api/conflicts,POST /api/conflicts/resolve
- Détection automatique des fichiers
- Syncthing conflicts dashboard — Vue dédiée dans l'UI
- IGNORED_DIRS configurable —
OBSIGATE_IGNORED_DIRS(liste séparée par virgules) - Timeout de session configurable —
OBSIGATE_ACCESS_TOKEN_TTL/OBSIGATE_REFRESH_TOKEN_TTL
Modifié
backend/main.py— +audit, +backup, +redaction, +backlinks endpoint, +conflicts endpointbackend/indexer.py— +backlink index, +IGNORED_DIRSbackend/auth/router.py— +IP rate limitingbackend/auth/jwt_handler.py— +configurable TTLbackend/watcher.py— +configurable IGNORED_DIRS
[1.3.0] — 2026-05-23
Ajouté
- Authentification complète — JWT + Argon2id
- Login/logout, refresh tokens, cookies httpOnly
- Contrôle d'accès par vault
- Interface d'administration : création/modification/suppression d'utilisateurs
- "Se souvenir de moi", sessions persistantes
backend/auth/— 5 modules (~600 lignes)
- CodeMirror 6 — Éditeur de fichiers intégré
- Syntax highlighting multi-langage
- Sauvegarde, annulation, fallback textarea si CM6 échoue
- Auto-sync frontmatter YAML
- Find-in-page (Ctrl+F) avec regex, barre de navigation
- PWA complet —
manifest.json,sw.js, icônes multi-tailles- Installation via bouton "Installer l'application"
- Thème synchronisé entre fenêtres popout
- Service Worker avec mise en cache
- Synchronisation temps réel — Watchdog + SSE
- Surveillance automatique des fichiers
- Mise à jour incrémentale de l'index
- SSE Manager avec reconnexion automatique
backend/watcher.py— watchdog observer avec debounce 2s
- Drag & drop / rename — Menu contextuel arborescence
- Création fichier/dossier, rename inline, suppression
- Boutons d'action toujours visibles, support mobile long-press
Modifié
frontend/app.js— Refonte majeure : authentication flows, code editor, PWA, syncfrontend/style.css— CodeMirror themes, auth forms, PWA UI, responsivedocker-compose.yml— auth env vars, data volume, non-root userbackend/main.py— auth middleware, file CRUD, code editor API, SSE, PWArequirements.txt— argon2-cffi, python-jose, python-multipart
[1.2.0] — 2026-05-18
Ajouté
- Rendu d'images Obsidian — Support complet de toutes les syntaxes
- Standard Markdown avec attributs HTML
- Wiki-link embed (
![[path/to/image.png]]) - Markdown standard (
) - Résolution multi-stratégies (7 niveaux)
- Indexation des attachements au démarrage
- Cache de résolution pour performance
- Système de fichiers cachés — Configuration par vault
includeHidden,hiddenWhitelist— UI + env vars- Filtrage côté client (hideHiddenFiles)
Modifié
backend/attachment_indexer.py— Scan et indexation d'imagesbackend/image_processor.py— Prétraitement Markdown pour les syntaxes d'imagesGET /api/image/{vault}?path=— Serve images avec MIME typesPOST /api/attachments/rescan/{vault}— Rescan manuelHIDDEN_FILES_GUIDE.md,IMAGE_RENDERING_GUIDE.md
[1.1.0] — 2026-05-15
Ajouté
- Recherche TF-IDF avancée — Moteur complet avec index inversé
- Opérateurs :
tag:,vault:,title:,path:,ext: - Autocomplétion intelligente (fichiers, tags, historique)
- Facettes, pagination, tri par pertinence
- Snippets surlignés
<mark>, accent-insensitive - Search history (localStorage, max 50, LIFO, dédupliqué)
- Opérateurs :
- Recherche regex —
extractRegexSnippet()avec surlignage - Recherches sauvegardées — CRUD API + sidebar UI
- Sauvegarde/chargement/suppression de requêtes
- Filtres de recherche — toggles (case, mot entier, regex), chemins
- Onglets sidebar — vaults, tags, récents, saved searches
- Auto-expand vault dans le filtre sidebar
- Tree search — Filtrage temps réel de l'arborescence avec surlignage
Modifié
backend/search.py— Nouveau : InvertedIndex, TF-IDF, autocompletefrontend/app.js— Refonte recherche : nouveau système de requêtes, facettes, pagination, autocompletefrontend/style.css— Search results cards, autocomplete dropdown, tags
[1.0.0] — 2026-05-10
Ajouté
- Première version stable — Porte d'entrée web pour vaults Obsidian
- Multi-vault — Visualisation de plusieurs vaults simultanément
- Arborescence — Navigation hiérarchique avec lazy-loading
- Rendu Markdown — Conversion HTML via mistune (tables, task lists, footnotes, strikethrough)
- Extraction YAML frontmatter — Tous les champs parsés avec carte pliable, badges, tags
- Vue Source (raw) — Toggle markdown brut + frontmatter
- Édition — PUT/DELETE fichiers via API
- Breadcrumbs — Navigation avec auto-expand et highlight
- TOC — Table des matières avec scroll spy et barre de progression
- Thème clair/sombre — Toggle localStorage + synchronisé fenêtres popout
- Popout — Fenêtre détachée pour affichage standalone
- Configuration UI — Paramètres persistants par vault
- Docker multi-stage — Image ~180MB, linux/amd64 + arm64 + arm/v7 + i386
- Healthcheck — Endpoint
/api/health - Headers sécurité — CSP, X-Frame-Options, XSS-Protection, Referrer-Policy
- Path traversal protection —
_resolve_safe_path()symlink-aware - Security hardening — Utilisateur non-root (UID 1000)
- Compression GZip (SSE-safe), Cache-Control immutable
- build.sh — Script de build automatisé
Architecture
- Backend : FastAPI 0.110.3, uvicorn 0.30.0, Python 3.11+
- Frontend : Vanilla JS SPA (0 dépendances npm), CSS custom properties
- Indexation : In-memory, mise à jour incrémentale, watcher watchdog
- Recherche : TF-IDF inversé, sans I/O disque