Files
ObsiGate/CHANGELOG.md
T
bruno 011ec84f23 feat: outils IA de lecture et d'edition des classeurs existants #153
A6 : list_xlsx_sheets (noms + dimensions + truncated), xlsx_to_markdown
(table bornee 100x20 pour le contexte LLM), update_xlsx_cells et
append_xlsx_rows (WRITE + confirmation, via edit_xlsx_cells : verrou,
ecriture atomique, neutralisation des formules, 409 lossy). Les lignes
ajoutees sont coercees comme dans le viewer (A10). Labels de step
ai.step.xlsx_* FR/EN ; update_xlsx_cells/append_xlsx_rows branches sur
MUTATING_TOOLS et FILE_WRITE_TOOLS (refresh viewer via obsigate:file-written).

Tests : test_spreadsheet_tools.py 17 (risque, confirmation, persistance,
garde formule heritee, coercion) + contre-preuve (cells vide -> 2 echecs).
ruff 0, mypy 0, i18n parity, validate-imports.

đŸ€– Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
2026-09-28 12:05:16 -04:00

168 KiB
Raw Permalink Blame History

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.33.0.


[Unreleased]


[2.33.0] — 2026-09-28


[2.32.0] — 2026-09-28


[2.31.0] — 2026-09-28

Correction

  • BUG-090 — troncature silencieuse d'une feuille .xlsx au-delĂ  de 500 lignes × 40 colonnes. render_sheets() renvoie les dimensions dĂ©clarĂ©es par la feuille (total_rows/total_cols), les plafonds du moteur (max_rows/max_cols) et un flag truncated : la visionneuse affiche un bandeau « Feuille tronquĂ©e — 500 lignes affichĂ©es sur 520 » (i18n FR/EN) au lieu de prĂ©senter une table courte comme complĂšte. La ligne d'en-tĂȘtes est dĂ©sormais figĂ©e au dĂ©filement vertical (thead sticky, top: auto sur les numĂ©ros de ligne pour Ă©viter leur empilement en haut Ă  gauche). #153 A8/R5.

Ajouté

  • #153 A9 — chargement paresseux d'une feuille par fenĂȘtres. GET /api/file/{vault}/xlsx/sheet?sheet=&offset=&limit= renvoie un bloc de lignes (XlsxSheetWindowResponse, plafond 1 000 lignes par requĂȘte, has_more de pagination) avec les vraies coordonnĂ©es A1 et numĂ©ros de ligne de la feuille — une fenĂȘtre se comporte exactement comme le rendu complet. Erreurs typĂ©es : 404 feuille inconnue, 415 fichier non-.xlsx. La lecture des valeurs calculĂ©es en cache (#153 A12) s'applique aussi aux fenĂȘtres.
  • #153 A9bis — « Charger la suite » sous une feuille tronquĂ©e. Un pied de page annonce la progression et fetch la fenĂȘtre suivante au clic ou Ă  l'approche du bas du tableau (sentinelle de dĂ©filement). Les lignes ajoutĂ©es passent par le mĂȘme pipeline d'Ă©dition que le rendu initial : Ă©ditables et sauvegardables immĂ©diatement. Un fetch Ă©chouĂ© restore le bouton (retry possible) ; feuille complĂšte → pied de page masquĂ©.

[2.30.0] — 2026-09-27

Correction

  • BUG-089 — un reindex manuel ne reconstruisait pas l'index inversĂ©. reload_index() / reload_single_vault() remplacent l'entrĂ©e de vault en bloc, ce qui n'Ă©met pas les notifications incrĂ©mentales : la recherche TF-IDF continuait de servir un index pĂ©rimĂ© aprĂšs un reindex. Les deux fonctions appellent dĂ©sormais init_inverted_index(). Au passage, backend/search.py lisait l'index via from backend.indexer import index — une liaison par valeur du dict : un rechargement du module backend.indexer recrĂ©ait le dict cĂŽtĂ© indexer alors que la recherche Ă©crivait dans l'ancien, et l'index inversĂ© n'indexait plus rien. Tous les accĂšs passent par _indexer.index. TrouvĂ© en Ă©crivant le test de recherche d'A5 : il passait isolĂ©ment et Ă©chouait en suite complĂšte selon l'ordre.

Ajouté

  • #153 A5 — les tableurs sont indexĂ©s par leur contenu. extract_indexable_text() extrait les noms de feuilles et les 20 premiĂšres lignes (plafond 5 000 caractĂšres, 20 feuilles) pour le TF-IDF et la recherche sĂ©mantique. Un mot tapĂ© dans une cellule rend dĂ©sormais le classeur trouvable ; la lecture binaire pour l'affichage est inchangĂ©e et un classeur chiffrĂ©/corrompu s'indexe par son seul nom.
  • #153 A10 — la saisie est typĂ©e comme dans Excel. Une valeur TRUE/FAUX/OUI/NON devient un boolĂ©en, une date JJ/MM/AAAA (avec HH:MM optionnel) devient une vraie date — et dans l'ordre français : 01/02/2026 est le 1á”‰Êł fĂ©vrier. Une saisie ressemblant Ă  une formule n'est jamais convertie.
  • #153 A12 — la valeur calculĂ©e s'affiche sous la formule. Quand une cellule porte encore le rĂ©sultat de son dernier calcul Excel, celui-ci s'affiche dans une ligne discrĂšte sous la formule. La seconde lecture data_only=True n'a lieu que si l'archive contient rĂ©ellement une valeur en cache, et toute erreur retombe sur l'affichage formules seul. Info-bulle traduite FR/EN (xlsx.cached_value_title).

[2.29.0] — 2026-09-27

Correction

  • BUG-084 — l'index inversĂ© conservait des documents fantĂŽmes aprĂšs la suppression d'une vault. remove_vault_from_index() (backend/indexer.py) ne notifiait pas le hook incrĂ©mental : aprĂšs suppression d'une vault, ses documents restaient dans l'index inversĂ© (postings, doc_info, doc_vault, vault_docs) et continuaient de correspondre aux recherches pour une vault inexistante — seul un reindex manuel les effaçait. Le correctif dĂ©clenche _on_index_change('remove', 
) pour chaque fichier de la vault, et _remove_doc_internals() supprime dĂ©sormais la clĂ© vault_docs dont le set devient vide (c'est un defaultdict : une lecture la recrĂ©ait). Test : TestVaultRemovalPurgesInvertedIndex (contre-preuve : Ă©choue sans le correctif).

Maintenance

  • Index inversĂ© — is_stale() renommĂ© is_ready(). La relecture de plan.md a Ă©tabli que les Ă©tapes 6 et 7 (suppression du cooldown et du hack de coalescence) Ă©taient dĂ©jĂ  livrĂ©es : ni _last_rebuild, ni _rebuild_cooldown, ni _source_generation, ni _on_vault_change ne subsistent. is_stale() ne mesurait donc plus aucune staleness — il indiquait seulement si l'index initial Ă©tait construit, sous un nom trompeur. RenommĂ© is_ready(), cohĂ©rent avec le is_ready() de SemanticIndex ; l'alias is_stale() de SemanticIndex, sans appelant, est supprimĂ©. /api/diagnostics expose dĂ©sormais is_ready (libellĂ© « Index prĂȘt » cĂŽtĂ© frontend/js/config.js). Tests : test_is_ready_tracks_initial_build, test_is_ready_survives_incremental_updates.

  • plan.md recalibrĂ©. Le fichier est dĂ©sormais marquĂ© « livrĂ© » et suivi d'une section « État rĂ©el » : le code a divergĂ© du plan sur quatre points (pas de repli _needs_rebuild, _ready au lieu de doc_count == 0, rebuild() conservĂ© au dĂ©marrage, is_stale() repurposĂ©). Les extraits de code du plan sont explicitement signalĂ©s comme ne dĂ©crivant pas le code actuel.

  • Fixtures de test locales exclues du suivi Git. test_dir/music/, test_dir/video/, test_vault/images/, test_vault/markdown/ et test_vault/budget.xlsx (~200 Mo) sont ajoutĂ©s au .gitignore : aucun test ni job CI ne les rĂ©fĂ©rence — les tests unitaires gĂ©nĂšrent leurs fixtures dans tmp_path et l'E2E n'utilise que les fixtures committĂ©es (test_vault/sample-*.{mp3,png,svg,webm,pdf}, test_dir/*.md). Ils restaient non suivis et polluaient git status.

Sécurité

  • BUG-088 — plus d'injection de formule via la visionneuse Excel. Une saisie =cmd|'/c calc'!A1 (ou @
) Ă©tait stockĂ©e comme formule par openpyxl, donc exĂ©cutĂ©e par Excel Ă  la rĂ©ouverture du fichier (DDE). edit_xlsx_cells force maintenant le type texte (cell.data_type = "s") pour toute valeur commençant par = ou @ ; l'API accepte allow_formula: true et la visionneuse expose un bouton f(x) (opt-in, Ă©tat de session, jamais persistĂ©). +/- restent des nombres.
  • BUG-087 — Ă©criture concurrente d'un classeur. load_workbook() → save() n'Ă©tait pas sĂ©rialisĂ© : deux sauvegardes simultanĂ©es (deux onglets, l'agent IA et la visionneuse) faisaient gagner la derniĂšre, en silence. Verrou par chemin (backend/services/mutations.py::_xlsx_write_lock, timeout 15 s) autour du cycle lecture → Ă©dition → remplacement ; attente dĂ©passĂ©e → 409 conflict. L'endpoint PUT 
/xlsx/save est devenu synchrone pour que l'attente s'exĂ©cute dans le threadpool.

Corrigé

  • BUG-085 — la perte de donnĂ©es Ă  l'enregistrement d'un .xlsx est annoncĂ©e, plus silencieuse. GET /api/file/{vault} renvoie xlsx_lossy_features (Ă©lĂ©ments qu'un round-trip openpyxl perd) ; la visionneuse affiche un bandeau listant ces Ă©lĂ©ments et la premiĂšre sauvegarde demande confirmation avant de renvoyer force: true. Sans force, l'API rĂ©pond 409 xlsx_lossy_content avec details.features. PĂ©rimĂštre remesurĂ© sur openpyxl 3.1.5 : graphiques, images, dessins et tableaux croisĂ©s sont bien prĂ©servĂ©s ; sont perdus les valeurs calculĂ©es en cache, slicers/chronologies, contrĂŽles de formulaire, connexions/requĂȘtes, custom XML, signature numĂ©rique, commentaires enrichis et macros.
  • BUG-086 — Ă©criture atomique des classeurs. wb.save() Ă©crivait en place sur le fichier du vault : un plantage laissait un .xlsx tronquĂ©. L'Ă©criture passe dĂ©sormais par un .tmp puis os.replace() (le backup .bak est inchangĂ©, le .tmp est ignorĂ© par le watcher).
  • Le handler global ServiceError expose maintenant code et details dans la rĂ©ponse JSON, et api() (frontend) les propage sur l'Error — nĂ©cessaire pour que le client distingue un 409 de confirmation d'une autre erreur.

Ajouté

  • #153 (P0) — tests de la visionneuse Excel. tests/frontend/xlsx-viewer.test.mjs (10 tests JSDOM : banniĂšre, confirmation + reprise force, refus, toggle f(x), payload de sauvegarde) et tests/e2e/xlsx-viewer.spec.js (3 tests Playwright sur la fixture test_vault/sample-xlsx-lossy.xlsx) ; la suite JSDOM est branchĂ©e dans le CI.

Documentation

  • #153 — Visionneuse & Ă©dition XLSX : audit complet et backlog de complĂ©tude. La visionneuse .xlsx livrĂ©e par #152 a Ă©tĂ© auditĂ©e couche par couche (lecture backend/xlsx_reader.py, Ă©criture backend/services.mutations.edit_xlsx_cells, UI renderXlsxViewer, indexation, outils IA, tests). Bilan : la grille de valeurs est Ă©ditĂ©e correctement (sĂ©curitĂ©, backup, audit, Ă©chappement HTML), mais l'ensemble supportĂ© est Ă©troit, une partie du classeur est perdue Ă  l'enregistrement, les tableurs sont invisibles pour la recherche et l'IA ne sait que les crĂ©er. Ouverture de l'item #153 dans docs/ROADMAP.md (17 sous-tĂąches suivies A1 → A17 ; P0 livrĂ©, reste P1 recherche/IA/UX puis P2 Ă©tendu) et crĂ©ation de la fiche docs/features/xlsx-viewer.md : cartographie du code, limites par couche, tableau des risques R1-R5 et critĂšres d'acceptation par sous-tĂąche.

[2.28.16] — 2026-09-27


[2.28.15] — 2026-09-27

Sécurité

  • #87 T6 — dĂ©pendances qualifiĂ©es, pip-audit bloquant (0 vulnĂ©rabilitĂ©). mistune 3.0.2 → 3.3.3 (XSS/ReDoS/DoS dans le moteur de rendu), python-multipart 0.0.9 → 0.0.31, weasyprint 69 → 70, mcp 1.9.4 → 1.28.1, fastapi 0.110.3 → 0.141.1 + starlette 0.37.2 → 1.7.0, setuptools 84 ; cast(str, 
) aux 3 sites d'appel mistune (typage 3.3 resserrĂ©). Suite complĂšte 1359 passed, ruff/mypy 0. Seule exception : PYSEC-2026-1325 (ecdsa, Minerva) — aucun correctif upstream ET JWT exclusivement HS256 (backend/auth/jwt_handler.py), les chemins ECDSA P-256 ne s'exĂ©cutent jamais → --ignore-vuln documentĂ©.

  • #87 T7 — semgrep SAST local bloquant (8 rĂšgles, 0 finding). Ruleset semgrep-rules/ (eval/exec, shell=True, os.system, pickle, yaml.load sans Loader, verify=False, Markup, mktemp) — 100 % local, aucun registre rĂ©seau (runner au rĂ©seau fragile). Trivy Ă©cartĂ© : binaire + base de vulnĂ©rabilitĂ©s Ă  tĂ©lĂ©charger Ă  chaque run, couche Python dĂ©jĂ  couverte par pip-audit bloquant (image = slim + 4 libs).

  • #87 T8 — fin BUG-034 : cookies Secure auto, CORS same-origin explicite. OBSIGATE_SECURE_COOKIES=true|false|auto (dĂ©faut auto : Secure en https, sinon rien — logins http locaux prĂ©servĂ©s ; X-Forwarded-Proto honorĂ© sous TRUST_PROXY, avertissement dĂ©marrage affinĂ©, TRUST_PROXY=true dans le compose prod) ; CORSMiddleware same-origin explicite (sĂ»r : web et desktop Tauri same-origin, API directe hors navigateur) ; style-src 'unsafe-inline' conservĂ© et assumĂ© (189 attributs style= + 343 el.style — suppression = réécriture complĂšte, risque nul cĂŽtĂ© exĂ©cution une fois script-src verrouillĂ© en T5c).


[2.28.14] — 2026-09-27


[2.28.13] — 2026-09-27

Modifié

  • #87 (T5c) — script-src sans 'unsafe-inline'. Seuls les scripts avec nonce frais (backend/csp.py, T5b) ou servis par 'self'/CDN listĂ©s s'exĂ©cutent ; style-src garde 'unsafe-inline' (chantier sĂ©parĂ©). VĂ©rifiĂ© : test_csp_nonce.py 5/5, 0 handler inline restant dans les pages HTML (propriĂ©tĂ©s onXxx = fn en JS non concernĂ©es par la CSP).

[2.28.12] — 2026-09-27

Corrigé

  • BUG-081 — GET /api/auth/mfa/status → 500 quand l'auth est dĂ©sactivĂ©e. Le pseudo-user anonymous (auth dĂ©sactivĂ©e, mode E2E/CI) n'a aucune entrĂ©e en store : get_user(...) → None puis AttributeError sur user.get. Garde None → payload « MFA dĂ©sactivĂ© » (mfa_enabled: false, totp_enabled: false, webauthn_credentials: 0). Test : tests/test_mfa.py (TestMfaStatusAuthDisabled, Ă©choue en 500 sans le correctif).

[2.28.11] — 2026-09-27


[2.28.10] — 2026-09-27

Corrigé

  • BUG-083 — job CI security rouge : le runner tronquait le # du run: pip-audit. Le runner Gitea Act coupe naĂŻvement au premier # (mĂȘme entre guillemets) : echo "... see #87)" devenait une citation non fermĂ©e (unexpected EOF while looking for matching '"'"). Seul run: du workflow avec un # ; l'echo n'a plus de # (rĂ©f #87 en commentaire YAML, jamais vu par le shell). Garde-fou : tests/test_ci_workflow.py (aucun # dans le code des run:, upload.test.mjs verrouillĂ© dans l'Ă©tape JSDOM — BUG-082).

[2.28.9] — 2026-09-27

Corrigé

  • BUG-082 — CI lint rouge : suites frontend exigeant jsdom. tests/frontend/upload.test.mjs puis config-ai-keys.test.mjs (imports statiques jsdom, introduits par #89) Ă©taient exĂ©cutĂ©s dans l'Ă©tape frontend racine oĂč jsdom n'est jamais installĂ© (ERR_MODULE_NOT_FOUND, rouge depuis 7bee4a2). DĂ©placĂ©s dans l'Ă©tape JSDOM (les deux branches, aprĂšs install si besoin) ; garde-fou tests/test_ci_workflow.py : aucun fichier de l'Ă©tape racine ne doit importer jsdom statiquement.

[2.28.8] — 2026-09-27

Corrigé

  • BUG-080 — harnais E2E local anti-blocage (plus de run pendu toute la nuit). run-e2e-local.ps1 : Playwright lancĂ© via node direct sur la CLI locale (jamais de prompt interactif, Start-Process ne sachant pas exĂ©cuter npx ; paramĂštre $Arguments, $Args Ă©tant une variable automatique qui l'Ă©craserait), installation Chromium sautĂ©e si dĂ©jĂ  prĂ©sent (E2E_INSTALL_BROWSERS=1 pour forcer), Ă©tapes install/test bornĂ©es (E2E_TIMEOUT_SEC, dĂ©faut 1800 s / 600 s, exit 124 au dĂ©passement — au-delĂ  du globalTimeout pour un abandon propre avec rapport) ; run-e2e-local.sh : npx --yes + mĂȘmes bornes ; playwright.config.ts : globalTimeout (25 min en local, 30 min en CI, E2E_GLOBAL_TIMEOUT_MS pour surcharger) ; e2e-server.ps1 : pidfile resynchronisĂ© sur le vrai propriĂ©taire du port et stop qui tue l'arbre complet (fini les serveurs orphelins qui squattent le port 2029). Garde-fous : tests/test_e2e_harness.py (8 tests).

[2.28.7] — 2026-09-26

Ajouté

  • #87 (T5b) — nonces CSP prĂȘts pour la bascule (sans changement). Nonce frais par rĂ©ponse dans script-src (backend/csp.py), injectĂ© dans les 6 pages HTML servies (dont la nouvelle route /excalidraw-editor.html, utilisĂ©e par l'iframe du viewer au lieu de /static/) et la page de partage ; tests/test_csp_nonce.py (unicitĂ©, concordance en-tĂȘte/HTML). unsafe-inline conservĂ© jusqu'en T5c.

[2.28.6] — 2026-09-26

Modifié

  • #87 (T5a) — 16 handlers inline convertis en listeners (CSP inchangĂ©e). onclick/onerror de index.html et des vues JS (config, plugins, sync, viewer, auth) remplacĂ©s par addEventListener ; suites frontend vertes (unit, ai, config-mobile, pdf-viewer, mfa-settings, sidebar-filters).

[2.28.5] — 2026-09-26


[2.28.4] — 2026-09-26

Ajouté

  • #87 (T4) — E2E XSS et serveur E2E pilotĂ©. tests/e2e/xss.spec.js : page publique /s/{token} (titre/frontmatter hostile Ă©chappĂ©, JSON neutralisĂ©, aucun JS exĂ©cutĂ©) et lecteur markdown (sanitizer, aucun on*/javascript: vivant) — 2/2 verts en local. scripts/e2e-server.ps1 : start|stop|status|logs avec progression visible et fichier PID (fini les serveurs orphelins sur le port 2029).

[2.28.3] — 2026-09-26

Ajouté

  • #87 (T3) — cookies Secure et CORS explicites. Helper is_secure_cookies() centralisĂ© (dĂ©faut false conservĂ© pour ne pas casser les logins HTTP locaux) + avertissement au dĂ©marrage sur bind non-loopback sans Secure ; tests/test_security_headers.py atteste l'absence de CORS permissif (same-origin par dĂ©faut du navigateur) et les en-tĂȘtes de durcissement.

[2.28.2] — 2026-09-26

Ajouté

  • #87 (T2) — tests de durcissement : concurrence et regex. tests/test_hardening_concurrency.py : crĂ©ations/mises Ă  jour/login failures concurrents sur users.json (zĂ©ro mise Ă  jour perdue, JSON toujours rĂ©cupĂ©rable) et budget temps de la politique ReDoS (motifs catastrophiques rejetĂ©s en < 1 s, motifs acceptĂ©s < 5 s sur 200 Ko).

[2.28.1] — 2026-09-26

Modifié

  • #87 (T1) — CI sĂ©curitĂ© durcie. bandit passe en bloquant (# nosec justifiĂ©s : SHA1 non-crypto, subprocess git Ă  argv fixe, saxutils.escape sans parsing ; B105 exclu comme pyproject.toml) ; npm audit --omit=dev bloquant (0 faille) ; les 5 suites frontend hors CI (upload, pretty, media-viewer, mfa-settings, config-ai-keys, vertes en local) rejoignent le job lint. pip-audit reste consultatif (upgrades starlette/weasyprint Ă  qualifier, chantier dĂ©diĂ©).

[2.28.0] — 2026-09-26

Modifié

  • #85 (T10) — persistance d'Ă©tat et clĂŽture de la refonte architecturale. Verrous RLock sur les stores JSON sans protection (revoked_tokens, shares, webhooks + secrets, clĂ©s d'outils) avec tests de concurrence (tests/test_store_locks.py — pertes prouvĂ©es sans verrou) ; rate-limit auth persistĂ© en option (OBSIGATE_RATELIMIT_DB, SQLite WAL, sĂ©mantique identique, dĂ©faut mĂ©moire inchangĂ©, tests/test_ratelimit_store.py). Contrat tools/registry.py auditĂ© (permissions/quotas/redaction dĂ©jĂ  cĂąblĂ©s, rien Ă  coder). Index non persistĂ© : rebuild diffĂ©rentiel #86 suffisant (dĂ©cision documentĂ©e). Fiche docs/features/archi-refonte-85.md, #85 sorti du backlog (index roadmap).

[2.27.12] — 2026-09-26

Modifié

  • #85 (T9) — extraction realtime + render hors du monolithe backend/main.py. Le stream SSE /api/events et le WebSocket /ws/collab/* sont servis par backend/routers/realtime.py, le pipeline markdown (mistune, wikilinks, slugs, sanitizer) par backend/render.py (imports directs, plus de couplage diffĂ©rĂ©). main.py (4 827 → ~760 lignes) ne contient plus que l'assemblage : lifespan, middlewares, montage des 16 routers, racine /api, statique/SPA et cales de compatibilitĂ© testĂ©es.

[2.27.11] — 2026-09-26

Modifié

  • #85 (T8) — extraction vaults/history/conflicts hors du monolithe backend/main.py. 13 routes servies par backend/routers/vaults.py, history.py et conflicts.py ; VaultInfo/BookmarkToggleRequest dans schemas.py, handle watcher partagĂ© dans backend/watcher_state.py. tests/test_api_main.py importe humanize_mtime depuis son module canonique (services.recent).

[2.27.10] — 2026-09-26

Modifié

  • #85 (T7) — extraction du domaine config hors du monolithe backend/main.py. /api/config, ai-keys (get/post/delete/test), tool-keys (×3), ai-models, diagnostics et dashboard sont servis par backend/routers/config.py (_FALLBACK_MODELS, store clĂ©s et config dĂ©placĂ©s ; main rĂ©importe _load_config pour son lifespan, les fixtures de tests inchangĂ©es). tests/test_ai_models.py patch dĂ©sormais la rĂ©fĂ©rence du router.

[2.27.9] — 2026-09-26

Modifié

  • #85 (T6c) — extraction media/pdf/export/guide hors du monolithe backend/main.py. file/pdf, exports (html/md-bundle/epub), guide/download, pdf/stream|info, image, media+thumb, attachments (rescan/stats), vault settings (get/post/all) et vault files sont servis par backend/routers/files_media.py ; le helper Range partagĂ© vit dans backend/routers/helpers.py (tags OpenAPI inchangĂ©s, tests statiques frontend media-viewer/image-viewer rĂ©alignĂ©s).

[2.27.8] — 2026-09-26

Modifié

  • #85 (T6b) — extraction mutations fichiers/dossiers hors du monolithe backend/main.py. PUT .../save|xlsx/save, DELETE/POST/PATCH /api/file, POST/PATCH/DELETE /api/directory, POST /api/move, POST .../batch-upload sont servis par le nouveau backend/routers/files_write.py (effets de bord inchangĂ©s : audit, index, SSE, webhooks, plugins, historique) ; 15 modĂšles dans schemas.py.

[2.27.7] — 2026-09-26

Modifié

  • #85 (T6a) — extraction lecture fichiers hors du monolithe backend/main.py. /api/browse/{vault}, /api/file/{vault}/raw|download|backlinks et GET /api/file/{vault} (vue rendue tous formats) sont servis par le nouveau backend/routers/files_read.py ; modĂšles dans schemas.py, _content_disposition/_media_max_inline_bytes dans backend/routers/helpers.py (partagĂ©s avec les tranches suivantes). Correctif au passage : dĂ©corateur orphelin /s/{token} restĂ© en T3 et double-enregistrement de /api/conflicts supprimĂ©s.

[2.27.6] — 2026-09-26

Modifié

  • #85 (T5) — extraction du domaine search hors du monolithe backend/main.py. Les 11 routes (/api/search, /advanced, /replace, /tags, /tree-search, /vault/{vault}/paths, /suggest, /tags/suggest, /graph/{vault}, /index/reload, /index/reload/{vault}) sont servies par le nouveau backend/routers/search.py ; les modĂšles search dans schemas.py et le pool de threads dans backend/search_executor.py (mĂȘme dimensionnement, mĂȘme cycle de vie) — comportement inchangĂ©.

[2.27.5] — 2026-09-26

Modifié

  • #85 (T4) — extraction du domaine backups hors du monolithe backend/main.py. Les 9 routes (/api/file/{vault}/backups|diff|restore, /api/backups, /delete, /purge, /content, /compress, /auto) sont servies par le nouveau backend/routers/backups.py ; Diff/Restore* dĂ©mĂ©nagent dans schemas.py et le singleton SSE dans backend/sse.py (partagĂ© avec main) — comportement inchangĂ©, aucun impact utilisateur.

[2.27.4] — 2026-09-26

Modifié

  • #85 (T3) — extraction du domaine sharing hors du monolithe backend/main.py. POST /api/share/{vault}, GET /api/shares, DELETE /api/share/{share_id} et les pages publiques /s/{token}, /s/{token}/raw, /s/{token}/pdf sont servis par le nouveau backend/routers/sharing.py — chemins, rĂ©ponses, tags OpenAPI et authentification inchangĂ©s (aucun impact utilisateur).

[2.27.3] — 2026-09-26

Modifié

  • #85 (T2) — extraction du domaine webhooks hors du monolithe backend/main.py. Le CRUD GET/POST/PATCH/DELETE /api/webhooks (admin) est servi par le nouveau backend/routers/webhooks.py — chemins, rĂ©ponses, tags OpenAPI et authentification inchangĂ©s (aucun impact utilisateur).

[2.27.2] — 2026-09-26

Modifié

  • #85 (T1) — extraction du domaine health hors du monolithe backend/main.py. GET /api/health et GET /api/health/detailed (admin) sont servis par le nouveau backend/routers/health.py (montĂ© dans main.py) et le modĂšle HealthResponse dĂ©mĂ©nage dans backend/schemas.py — chemins, rĂ©ponses, tags OpenAPI et authentification inchangĂ©s (aucun impact utilisateur).

[2.27.1] — 2026-09-26

Modifié

  • Roadmap — priorisation dette & sĂ©curitĂ© (dĂ©cisions 2026-09-26). Items #85 (refonte architecturale) et #87 (CI/CD) dĂ©taillĂ©s et marquĂ©s prioritaires : backend/main.py mesurĂ© Ă  ~4 827 lignes, tools/registry.py Ă  crĂ©er, persistance SQLite/Redis, verrous asyncio, audit des except larges, CI sĂ©curitĂ© bloquante (bandit/semgrep/trivy, audits pip/npm), finition CSP nonce (BUG-034), cookies Secure par dĂ©faut, rotation clĂ© DeepSeek Ă  confirmer (BUG-006). #73 Sync reportĂ© (P4, hors chemin critique) ; desktop #77 confirmĂ© non signĂ© + doc SmartScreen, reste les 6 tests E2E manuels. Corrections : sections livrĂ©es #83/#84 retirĂ©es du backlog (dĂ©tail dans l'archive, index inchangĂ©), total restant recalculĂ© (~12-18 jours chemin critique : #77 fin + #85 + #87).

[2.27.0] — 2026-09-25

Ajouté

  • #152 — Viewer XLSX : affichage des fichiers .xlsx en tableaux multi-feuilles (onglets, en-tĂȘtes A1), Ă©dition inline des cellules avec PUT /api/file/{vault}/xlsx/save (backup avant Ă©criture, coercion numĂ©rique) et tĂ©lĂ©chargement du fichier d'origine.

[2.25.1] — 2026-09-24

Corrigé

  • /api/diagnostics — erreur 500 « dictionary changed size during iteration ». Le calcul des statistiques d'index itĂ©rait inv.word_index et index en direct, pendant que l'indexeur les modifiait depuis un autre thread (build au dĂ©marrage, hooks incrĂ©mentaux) : l'itĂ©rateur de dict levait RuntimeError et l'endpoint renvoyait 500. Les deux dicts sont dĂ©sormais copiĂ©s avant itĂ©ration (copie atomique sous le GIL), ce qui supprime la course.

[2.25.0] — 2026-09-24

Ajouté

  • #115 — barre d'outils de lecture toujours visible. La barre d'actions d'un document (pop-out, bookmark, Editer, Source, Copier, Partager
) est dĂ©sormais Ă©pinglĂ©e en haut de la zone de lecture : elle reste accessible en permanence, mĂȘme au bas d'un document long, au lieu de disparaĂźtre au dĂ©filement. Elle est rendue comme un enfant direct du conteneur de dĂ©filement (.file-toolbar), masquĂ©e en mode lecture, et alignĂ©e dans la fenĂȘtre pop-out.
  • #117 — avatars prĂ©dĂ©finis dans le profil. La section Profil propose une galerie de 12 avatars fournis avec l'application (frontend/icons/avatar/) : un clic charge l'image, la recadre au format carrĂ© 256 px (mĂȘme pipeline que l'import) et l'enregistre sur le compte. L'avatar actif est surlignĂ© ; l'import d'une photo personnalisĂ©e et la suppression restent disponibles.

Corrigé

  • BUG-078 — coloration syntaxique des fichiers de code perdue. Les feuilles de style highlight.js Ă©taient basculĂ©es Ă  partir de la clĂ© de thĂšme (defaut-obsigate, 
) au lieu du mode (dark/light) : les deux feuilles se retrouvaient dĂ©sactivĂ©es et les blocs de code (.py, .sh, .ps1, .yml, JSON, blocs Markdown
) s'affichaient en texte brut. Le basculement est dĂ©sormais pilotĂ© par le mode, de façon dĂ©terministe, dans le moteur de thĂšme (themes.js) comme au premier rendu (ui.js) ; sĂ©pia et contraste Ă©levĂ© rĂ©utilisent la palette claire.

[2.24.0] — 2026-09-24

Ajouté

  • BUG-077 — assistant IA : bouton « Stop ». Pendant qu'une rĂ©ponse se diffuse, le bouton d'envoi du composeur devient un bouton Stop (icĂŽne carrĂ©e, couleur d'alerte) : un clic interrompt immĂ©diatement l'exĂ©cution de l'agent (abort de la requĂȘte SSE, annulation de la tĂąche cĂŽtĂ© serveur Ă  la dĂ©connexion) et la rĂ©ponse partielle est conservĂ©e avec un marqueur « âč ExĂ©cution arrĂȘtĂ©e. ». Le bouton reste actif (il n'est plus dĂ©sactivĂ©) tant que l'agent travaille, y compris pendant la reprise d'une confirmation.

Modifié

  • BUG-075 — assistant IA : approbation globale des actions. Une mĂȘme rĂ©ponse du modĂšle peut demander plusieurs mutations (crĂ©er un dossier et les fichiers qu'il contient
). Elles sont dĂ©sormais regroupĂ©es en une seule confirmation (au lieu d'une action aprĂšs l'autre) : la carte liste chaque action avec son libellĂ© et son aperçu de diff, et un unique bouton « Tout approuver (N) » envoie confirm_all — les actions en attente sont appliquĂ©es d'un bloc, puis la suite de l'exĂ©cution est autorisĂ©e sans nouvelle carte. Les appels en lecture du mĂȘme lot s'exĂ©cutent immĂ©diatement (conversation valide). CĂŽtĂ© backend, pending.actions remplace le report deferred des mutations d'un lot (backend/agent/loop.py) et confirm_all arme ToolContext.confirmed pour le reste du run (backend/bookslm_routes.py).

Corrigé

  • BUG-074 — assistant IA : bloc d'Ă©tapes sans titre et compteur figĂ© Ă  1. Le rĂ©sumĂ© repliĂ© affiche dĂ©sormais un titre (le libellĂ© de la premiĂšre action, « N Ă©tapes — Fichier créé : notes/a.md ▶ »), le compteur ne compte plus les rĂ©flexions (seules les actions) et la reprise d'une confirmation continue de diffuser dans le mĂȘme message au lieu de crĂ©er un nouveau message « 1 Ă©tape » par approbation : le bloc d'Ă©tapes s'accumule sur tout l'Ă©change.
  • BUG-076 — assistant IA : arborescence et document ouvert non rafraĂźchis. AprĂšs une action mutatrice de l'agent (crĂ©ation/suppression/renommage de fichier ou dossier, Ă©criture), l'arborescence est rafraĂźchie immĂ©diatement (rafraĂźchissement dĂ©bouncĂ© sur les Ă©vĂ©nements tool mutateurs, sans attendre le watcher) et le document affichĂ© est rechargĂ© depuis le disque ; les outils de crĂ©ation de documents (xlsx/docx/csv/pdf) notifient dĂ©sormais aussi la visionneuse.

[2.23.0] — 2026-09-24

Ajouté

  • #114 — Configuration, refonte mobile-responsive. La page « Configurations » est dĂ©sormais pensĂ©e pour le tactile (≀ 768 px) : modale plein Ă©cran (100dvh, safe-area), sommaire en drawer coulissant gauche (position: fixed, largeur min(320px, 88vw)) avec fond assombri (#config-modal.config-toc-open::before) et bouton de fermeture #config-toc-close (tap sur le backdrop ou Échap ferme le drawer d'abord, puis la modale) ; formulaires sur une colonne ; tous les boutons (save/secondary/danger/add/sm/hamburger/fermer) et liens du sommaire en cibles ≄ 44 px ; champs et selects en 16 px + min-height 44 px (anti-zoom iOS) ; rangĂ©e « Sauvegarder / RĂ©indexer / RĂ©initialiser » sticky en bas de sa section avec safe-area ; MFA (codes de secours en 1 colonne, champ de code full-width et wrap des rangĂ©es d'action) ; dĂ©bordements webhook/token/share corrigĂ©s. Dettes HTML/i18n de l'audit incluses : .config-actions-row replacĂ©e dans #cfg-backend-settings (+ </section> orphelin supprimĂ©), id dupliquĂ© cfg-partages-publics retirĂ© du <h2>, conteneur mort #plugins-settings-container supprimĂ©, libellĂ© #mt-explorer i18n (settings.explorer), doublons .config-btn-add et rĂšgle morte .mfa-recovery-input purgĂ©s ; i18n : clĂ©s mortes settings.backend, settings.backend_hint, settings.restart_badge, settings.save, settings.plugins supprimĂ©es, config.toc_close ajoutĂ©e, settings.tabs FR corrigĂ© (« Onglets »). Tests : config-mobile.test.mjs Ă©tendu (27, au CI) + E2E config-mobile.spec.js (5, projet chromium-mobile). Fiche : docs/features/settings-mobile-114.md.

[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 de index.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) et body.np-active .content-area ramenĂ© Ă  76 px (le dĂ©gagement dock, sans double comptage avec .main-body). Tests : tests/frontend/mobile-toolbar.test.mjs (7, au CI) et E2E tests/e2e/mobile-toolbar.spec.js (2, projet chromium-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 (avatar dans data/users.json, exposĂ© par GET/PATCH /api/auth/me et 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-tags et cfg-partages-publics sont dĂ©sormais portĂ©es par leur <section> (cible de dĂ©filement = haut de section). Garde-fous : test statique tests/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 / total complĂšte la barre d'outils.

Modifié

  • #111 — navigation d'images fluide. Changer d'image se fait dĂ©sormais en place (showSibling remplace le src du <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 npm test:e2e:ps) reproduit localement le job CI e2e (uvicorn natif, auth dĂ©sactivĂ©e, fixtures TestVault/TestDir, port 2029, projet chromium-desktop) sans dĂ©pendre de bash — 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 script bash reste 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: hidden empĂȘ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.js qui 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-area viewport-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_EXTENSIONS via media_types.py). Nouvel endpoint GET /api/media/{vault}?path=
 avec support HTTP Range / 206 Partial Content (Content-Range, Accept-Ranges, 416 sur plage invalide, 413 au-delĂ  de OBSIGATE_MEDIA_MAX_INLINE_MB, dĂ©faut 500 Mo) — le helper Range de pdf/stream a Ă©tĂ© extrait en _stream_file_with_range() et est partagĂ©. api_file_view() expose is_audio/is_video/stream_url/media_mime avant toute lecture texte. Frontend : lecteur audio dĂ©diĂ© (artwork, durĂ©e via loadedmetadata) et lecteur vidĂ©o (playsinline, scĂšne noire letterboxĂ©e), icĂŽnes Lucide audio-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. Endpoint GET /api/media/{vault}/thumb (miniature WebP 256 px, cache disque invalidĂ© par mtime, repli sur l'original pour le SVG, pillow>=10.0). Filtre ext:png/ext:jpg opĂ©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Ă© par api_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 dans viewer.js (bouton Plein Ă©cran), avec encodage d'URL des chemins.
  • #108-B3 — XSS via SVG : /api/image (et le repli miniatures) pose Content-Security-Policy: sandbox sur 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Ă© dans docs/GUIDES/MCP.md ; docs/MCP_GUIDE.md devient 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, projet chromium-mobile, ignorĂ© en chromium-desktop comme mobile-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-nav qui 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-hamburger dans l'en-tĂȘte (mĂȘme traitement .help-hamburger que le Guide, libellĂ© traduit config.toc_toggle FR/EN). (2) Les liens du sommaire Ă©taient des ancres brutes sans JS : config.js les 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-attr accepte dĂ©sormais plusieurs paires attr: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, origines http://localhost sans 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/Proto si OBSIGATE_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.example documente 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 dans index.html (marquage rĂ©el : #login-screen > .login-card) → return silencieux. 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-danger dĂ©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, SVG data: via segno, 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 via POST /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_vault rĂ©utilise les entrĂ©es inchangĂ©es (size + modified) d'un snapshot prĂ©cĂ©dent — seuls os.walk + stat tournent Ă  chaque rebuild (build_index, reload_single_vault). Extraction excalidraw diffĂ©rĂ©e : le scan ne lit plus les .excalidraw / .excalidraw.md (flag excalidraw_text_pending), enrich_pdf_texts() extrait leur texte aprĂšs index comme pour les PDF. Garde-fou MAX_REPLACE_FILE_BYTES (5 Mio) sur replace_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.json ne contient que les mĂ©tadonnĂ©es) ; rĂ©vocation immĂ©diate des deux cĂŽtĂ©s, plafond 50 clĂ©s par utilisateur, isolation par utilisateur, audit config_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). Nouveau GET /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Ă© par diagram_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-emoji ajoutĂ© Ă  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-ai et un attribut data-i18n-placeholder dupliquĂ© 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 (đŸ—‚ïž) et config.section_shares (đŸ“€) sont alignĂ©s sur leurs titres de section, en FR et EN. Test de non-rĂ©gression ajoutĂ© dans tests/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 —
    1. 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 ».
    2. 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() dans frontend/js/ai.js, uniquement les capacités actives) au lieu des cases à cocher.
    3. Fournisseurs d'API en cartes dĂ©pliables (rendu dynamique depuis AI_PROVIDER_NAMES par _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 % (grille 3fr 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.
    4. 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).

[2.11.6] — 2026-09-18

Corrigé

  • BUG-064 - Éditeur Excalidraw : le diagramme ne s'affiche jamais (canvas vide) — deux causes cumulĂ©es :
    1. La feuille de style d'Excalidraw n'Ă©tait jamais chargĂ©e (@excalidraw/excalidraw exige un import CSS explicite). Sans elle, l'Ă©diteur est non stylisĂ© et .excalidraw n'a aucune hauteur fixe : la boucle de redimensionnement d'Excalidraw fait grossir le canvas jusqu'au plafond codĂ© en dur de 2^25 (33 554 432 px), que le navigateur ne peut pas dessiner → scĂšne blanche. Correctifs : <link> vers 
/@excalidraw/[email protected]/dist/prod/index.css dans frontend/excalidraw-editor.html et ajout de https://esm.sh Ă  style-src de la CSP (backend/main.py).
    2. appState.collaborators est une Map sérialisée en objet JSON ({}) par l'app Excalidraw / le plugin Obsidian ; réinjectée via initialData, Excalidraw 0.18 appelait .forEach() dessus et plantait (e.appState.collaborators.forEach is not a function). sanitizeAppState() la reconvertit en Map et écarte la géométrie de viewport importée (width, height, offsetLeft, offsetTop) pour les deux formats (.excalidraw et .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) + fixtures test_vault/diagram-app-export.excalidraw.
  • 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"]) en max-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 Ă©mettait index_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 » ou Ctrl+S. En complĂ©ment, reloadExternalWrite ne 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-fullscreen dans la barre d'outils de l'Ă©diteur (frontend/excalidraw-editor.html) qui bascule le mode plein Ă©cran natif ; l'iframe est créée avec allow="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 et aria-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.src avec 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=N qu'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ĂȘtre about:blank et l'affectation n'atteignait jamais le document. Nouveau helper navigatePdfToPage() qui recharge l'iframe avec le fragment #page=N ; les entrĂ©es portent un data-page et sont cĂąblĂ©es par des Ă©couteurs (plus d'onclick inline). 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, fixture test_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 HttpOnly access_token, envoyĂ© automatiquement par le navigateur lors du handshake same-origin, est le seul transport supportĂ© ; les trames brutes dĂ©passant MAX_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 explicite OBSIGATE_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_cost passe 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 invalides avec un temps Ă©quivalent (hachage factice), au lieu d'un 429/403 distinctif ; seul le rate-limit par IP (non liĂ© Ă  un compte) conserve le 429. Fichier : backend/auth/router.py.
  • BUG-040 — Extraction PDF diffĂ©rĂ©e au scan : _scan_vault ne 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 pose object-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 par frame-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, fixture test_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 endpoints GET/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 mousedown et 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_pdf de l'Assistant : tableaux mal formatĂ©s : l'outil create_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 + plugin table + 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 via OBSIGATE_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 i18n ai.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.

[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-secondary pour les numĂ©ros, --border pour 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 par postMessage({ type: 'parent-insert' }) → insertAtCursor dans editor-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, sur forge-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 SSE index_updated sur 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 Tab dĂ©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 de Tab est 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-complete est 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 (repli ollama si 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 (iframe allow="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-save est 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 (le catch ne restaurait ni l'icĂŽne ni l'Ă©tat), le spinner persistait jusqu'Ă  un rechargement complet de la page. Un helper resetSaveButton() 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 natif create_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 blocs obsigate-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Ă©sultat deferred pour 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-Agent navigateur 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Ă©sormais Accept-Language, Sec-Fetch-* et Upgrade-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_search renvoyait 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 (provider dans le rĂ©sultat, OBSIGATE_WEB_FALLBACK=0 pour 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 bloc COMMON_RULES commun (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 de COMMON_RULES pour 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_id sans rĂ©ponse) et la reprise Ă©chouait. Les appels non atteints reçoivent dĂ©sormais un rĂ©sultat deferred explicite (backend/agent/loop.py). En complĂ©ment, create_directory est idempotent cĂŽtĂ© outil IA (succĂšs si le dossier existe dĂ©jĂ , backend/services/mutations.py) et les consignes (create_file crĂ©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 de initSidebarFilter est 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 16px sur une largeur de 32 px ⇒ largeur de contenu nulle ⇒ SVG Ă  0 px). SĂ©lecteur portĂ© Ă  .bookslm-input-area button.bookslm-btn-plus, l'icĂŽne plus est 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Ă© dans initSidebarFilter (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-menu remontait 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 Lucide plus. Tests : tests/frontend/ai.test.mjs (+3), nouvelle suite tests/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 endpoints GET/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Ă©s bookslm-sessions-* / bookslm-history-* et notifie la sidebar via l'Ă©vĂ©nement bookslm:history-updated.
  • Assistant IA — accĂšs rapide Ă  l'historique dans la sidebar (#96) : cinquiĂšme onglet de navigation #sidebar-tab-ai (icĂŽne messages-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 _extensions simple Ă  Ă©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-send circulaire (40 px), icĂŽne Lucide arrow-up remplaç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 tag vX.Y.Z est 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 jetable tmp-verify-inline.cjs restĂ©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 SSE index_updated sur le fichier affichĂ© (un fichier modifiĂ© hors de l'app relançait openFile() et Ă©crasait le DOM de l'Ă©diteur). L'activation d'onglet appelle dĂ©sormais detachInlineEditor() avant le placeholder de chargement, et l'Ă©criture externe passe par reloadExternalWrite() (le tampon de l'Ă©diteur est rechargĂ© depuis le disque au lieu d'un re-rendu de la vue lecture). Nouveau helper queryEditor() (editor-inline.js) : l'en-tĂȘte, le pied et la marque voyagent avec le conteneur en mode inline, un modal.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.md documente 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 (scrollIntoView block: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 (SSE provider/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Ă©) et fetch_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 SSE tool, rĂ©sultats web_search plafonnĂ©s Ă  8 + page fetch_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_search signale 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-index conservĂ©) 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 SSE index_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.editing annonce 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çoit parent-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.1 alors que 66 commits avaient Ă©tĂ© livrĂ©s (et les numĂ©ros codĂ©s en dur divergeaient : package.json 1.0.0, desktop Tauri 2.0.0, Dockerfile 2.2.1, README 1.7.0). Le fichier VERSION (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-commit crĂ©e le tag vX.Y.Z et push.followTags le 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.md et rotation de CHANGELOG.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/health sur 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 gardait overflow:auto) et le scroller CodeMirror dĂ©bordaient simultanĂ©ment — la barre d'outils IA empilĂ©e sous un .cm-editor en height:100% faisait dĂ©border le corps, et un ancien override global .cm-scroller forç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 un detail tableau d'objets (format validation FastAPI) que new 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 outils edit_file / append_to_file / create_file de l'utiliser. Cache service worker SW_VERSION v20.

  • #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). _isLoading est 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 le padding-bottom du 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: none sur 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 (/chat et /agent) : le tag affiche « openrouter · openai/gpt-4o-mini ». Couvert par tests/test_bookslm.py::TestEffectiveModel et 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Ă©e e+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/status qu'une fois, Ă  sa construction, et le panneau de l'assistant est un singleton montĂ© pour toute la session : la liste restait figĂ©e. Le nouveau refreshAIPickers() (frontend/js/ai.js) reconstruit chaque picker montĂ© dans son emplacement .ai-picker-slot et 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 de obsigate_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/models en 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 (seul pixtral, retirĂ© de l'API, les matchait). ObsiGate lit dĂ©sormais les capacitĂ©s dĂ©clarĂ©es par le fournisseur (nouveau backend/provider_capabilities.py, snapshot mis en cache par GET /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 du title, 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/recovery et mfa/webauthn/verify (_enforce_mfa_rate_limit).
    • BUG-024 — traversal inter-vaults : resolve_safe_path compare dĂ©sormais les chemins par segment (Path.relative_to + repli casse-insensible), plus par prĂ©fixe de chaĂźne : vault ne peut plus lire vault-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 variable OBSIGATE_WEBHOOK_SECRET_<ID> (plus de secret en clair dans webhooks.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_at invalide tous les jetons Ă©mis avant un changement de mot de passe.
    • BUG-029 — race users.json : verrou threading.RLock autour des cycles lecture-modification-Ă©criture.
    • BUG-030 — audits : l'adresse IP rĂ©elle du client (X-Forwarded-For si OBSIGATE_TRUST_PROXY=true) est injectĂ©e dans current_user et 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_vault utilise os.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_fulltext utilisent 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 + cookie HttpOnly) ; 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).

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, persistance localStorage). i18n FR/EN. Helpers purs exportĂ©s (formatChange, normalizeRibbonCommands, moveRibbonCommand). Tests JSDOM tests/frontend/mobile-editor.test.mjs (+13) et E2E mobile.

  • #82 Assistant IA — menu @ instantanĂ© — nouvel endpoint GET /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-skill permet 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Ă©e backend/model_capabilities.py, endpoints GET /api/ai/model-capabilities et GET /api/ai/skills, support multimodal dans backend/ai_chat.py et backend/bookslm_routes.py. Tests : tests/test_model_capabilities.py (16), tests/test_skills.py (16), tests/test_ai_vision.py (12)

  • #77 Desktop — Signature des mises Ă  jour Tauri — la paire de clĂ©s minisign de l'updater est gĂ©nĂ©rĂ©e et sa clĂ© publique est embarquĂ©e dans desktop/tauri.conf.json (plugins.updater.pubkey, remplace le placeholder) ; bundle.createUpdaterArtifacts: true produit les fichiers .sig par artefact. Le workflow .gitea/workflows/desktop-build.yml expose les secrets TAURI_SIGNING_PRIVATE_KEY / TAURI_SIGNING_PRIVATE_KEY_PASSWORD aux 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 — nouveau scripts/updater_manifest.py qui construit le document consommĂ© par l'updater Tauri (version, notes, pub_date, platforms Windows/Linux avec signature .sig et URLs des assets). Il est gĂ©nĂ©rĂ© automatiquement par scripts/publish_release.py (Ă©crit desktop/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 automatiquement desktop/obsigate-updater.key pour 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 /embeddings compatible 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), avec semantic_score par rĂ©sultat. Indexation incrĂ©mentale branchĂ©e sur le watcher (un fichier modifiĂ© rĂ©gĂ©nĂšre son embedding). UI : toggle « Recherche sĂ©mantique » (~, raccourci Alt+S) dans la barre de rĂ©sultats + affichage du score de similaritĂ©, clĂ©s i18n FR/EN. Nouveau module backend/semantic_search.py, paramĂštre semantic sur /api/search/advanced, dĂ©pendances optionnelles dans backend/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 module frontend/js/mobile-editor.js, styles frontend/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 (cookie access_token ou paramĂštre token) + check_vault_access et resolve_safe_path par connexion. Yjs/CRDT cĂŽtĂ© client : Y.Doc/Y.Text fusionnent 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 frontend frontend/js/collab.js (provider + liaison Y.Text↔CodeMirror + curseurs distants) et backend/collab.py (CollabManager). Tests : tests/test_collab.py (17 tests, dont 5 clients simultanĂ©s) et tests/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 ; erreur rate_limited). Quotas BOOKSLM_MAX_* : BOOKSLM_MAX_TOOL_CALLS plafonne les appels d'outils par run d'agent (stopped="quota_exceeded") et BOOKSLM_MAX_TOOL_READ_BYTES plafonne read_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 : tag MCP + injection du path /mcp (Streamable HTTP) dans le schĂ©ma ; nouveau docs/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 MCP read→propose→apply→resources/read).

  • #79 Phase E — Serveur MCP (Streamable HTTP) — nouvel endpoint /mcp exposant ObsiGate Ă  des clients MCP externes (Claude Desktop, Cursor
) via le SDK Python mcp==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-step propose_<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> et vault://<name>/<path> (lecture seule, secrets redactĂ©s). Prompts : summarize-directory, generate-note, find-related. Auth Authorization: Bearer <JWT> → get_current_user ; permissions et toggle destructif par vault (aiDestructiveTools) appliquĂ©s. Nouveaux modules backend/mcp/server.py et backend/mcp/confirmations.py ; dĂ©pendances mcp==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Ă©utilisable backend/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}/restore et /api/search/replace dĂ©lĂšguent dĂ©sormais Ă  cette couche. backend/services/backups.py expose create_backup (source unique, rĂ©utilisĂ©e par main._backup_file). Confirmations two-step : tout outil WRITE/DANGEROUS dĂ©clenche ToolConfirmationRequired (carte Apply in-app, propose/apply cĂŽtĂ© MCP en phase E). Toggle par vault aiDestructiveTools (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Ă©utilisables backend/services/backups.py, graph.py, recent.py et extensions de vaults.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-search et /api/search/advanced dĂ©lĂšguent dĂ©sormais Ă  ces services (source unique de vĂ©ritĂ©). Filtrage systĂ©matique par permissions vault ; secrets redactĂ©s sur read_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çade backend/tools/api.py. Services lecture/recherche livrĂ©s (list_vaults, list_directory, read_file, search_fulltext, list_tags) ; permissions check_vault_access + resolve_safe_path appliquĂ©es par outil. Tests : tests/test_tools.py (30 tests).
  • #79 Phase B (partielle) — function calling in-app — abstraction tool-calling provider-agnostique backend/ai_chat.py (chat_completion, ToolCall, LLMResponse, OpenAI-compat

    • Gemini) ; agent loop backend/agent/loop.py (boucle tool→rĂ©sultat→tool, limite 10 itĂ©rations, truncation) ; endpoint opt-in POST /api/ai/bookslm/agent (events SSE tool/message/confirmation) ; fallback automatique en chat simple si le provider rejette les tools. Tests : tests/test_agent_loop.py, tests/test_ai_chat.py, tests/test_bookslm.py.
  • #79 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 ; ServiceError mappĂ©e vers HTTPException (routes) et ToolError (outils). /api/ai/bookslm/chat streame rĂ©ellement les tokens (stream_completion, OpenAI-compat + Gemini ; GZip ignorĂ© pour les SSE BooksLM). Mode agent cĂŽtĂ© UI (toggle, Ă©vĂ©nements tool/confirmation, carte Apply + aperçu diff LCS) et reprise confirm/confirm_messages de 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_models persistĂ©s dans data/config.json, rechargement Ă  chaud dans backend/ai.py, sĂ©lecteurs « Fournisseur par dĂ©faut » / « ModĂšle par dĂ©faut » dans #cfg-ai, i18n FR/EN.

  • #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_tree cĂŽ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 Ă  jour desktop/tauri.conf.json, desktop/Cargo.toml et desktop/Cargo.lock (obsigate-desktop) avant de crĂ©er un commit de release chore(release): vX.Y.Z puis le tag. --push pousse la branche et le tag ; --no-files conserve l'ancien comportement (tag seul) et --dry-run prĂ©visualise. Indispensable au bon fonctionnement de l'updater Tauri (dont le manifeste reprend la version de tauri.conf.json).

  • .gitea/workflows/desktop-build.yml — dĂ©clenchement manuel uniquement — le workflow Desktop Build ne s'exĂ©cute plus sur push (aucun runner self-hosted [windows/linux, desktop] enregistrĂ©) mais uniquement via workflow_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Ă©nement obsigate:open-file).

  • Assistant IA (frontend/js/bookslm.js) — bouton « mode agent » dans l'en-tĂȘte (persistĂ© en localStorage) : l'envoi bascule vers /api/ai/bookslm/agent, affiche la trace des appels d'outils et les cartes de confirmation (Apply + aperçu diff) ; /chat reste 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 de min-width: 0 et overflow: hidden sur .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 un padding-bottom pour ne pas cacher le curseur.

  • BUG-020 — Éditeur mobile : double barre de dĂ©filement — .editor-body passe en overflow: 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-area et sur les Ă©vĂ©nements bookslm: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 utilisait background: var(--surface2), or --surface2 est 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-hover avec 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 keydown du 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_VERSION portĂ© Ă  v6 et migration de purge Ă©largie Ă  tous les caches obsigate-* (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 sur vault=all pour 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-bulle title complĂš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 (_vault nul), la requĂȘte partait sans vault et le backend ignorait le contexte ad-hoc. La sĂ©lection capture maintenant le vault renvoyĂ© par /api/tree-search, _contextVault() le propage aux requĂȘtes /context et /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.json en cache HTTP affichait les clĂ©s brutes (ai.model_search, mobile_editor.*, 
) ; le chargement i18n utilise dĂ©sormais cache: 'no-store' et SW_VERSION est 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, options display: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_prompt ne 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 sur vault=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 localStorage de la webview et le paramĂštre hasVaultPath n'Ă©tait jamais renseignĂ©. Ajout d'un boolĂ©en persistant wizard_done dans la config desktop (desktop/src/main.rs, commandes get_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 inutilement libpango-1.0-0, libpangocairo-1.0-0 et shared-mime-info (libs runtime de WeasyPrint, sans usage Ă  la compilation), dupliquant les tĂ©lĂ©chargements apt. Ces paquets sont retirĂ©s du builder et apt-get clean est ajoutĂ© dans les deux stages pour purger les .deb conservĂ©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 plus Cache-Control: public, max-age=31536000, immutable sur des assets non fingerprintĂ©s. Correctifs : frontend/sw.js réécrit (network-first pour HTML/JS/CSS, caches versionnĂ©s SW_VERSION, prĂ©cache corrigĂ© /static/js/
, skipWaiting/clients.claim) ; migration ponctuelle localStorage (suppression des anciens caches) en remplacement du kill-switch de session ; en-tĂȘtes no-cache sur /static, index.html, manifest.json et pages HTML ; rechargement unique sur controllerchange. 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, gardes None sur get_user(), PdfReader: Any et import PROVIDERS manquant dans main.py (bug latent : le modĂšle par dĂ©faut n'Ă©tait jamais prĂ©pendĂ© Ă  la liste des modĂšles live). L'Ă©tape mypy du 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.md et documentĂ©es par feature dans docs/features/.

  • MĂ©thode de livraison unifiĂ©e — nouveau docs/DELIVERY_WORKFLOW.md (Definition of Done : tests, documentation, commit, push, CI) et AGENTS.md (instructions obligatoires lues Ă  chaque session). RĂ©fĂ©rencĂ©s depuis la Roadmap, CONTRIBUTING et ISSUES_TODOLIST.


[2.2.1] — 2026-09-11

Ajouté

  • Mobile — assistant AI central — bouton assistant dans la barre du bas (commit 50b823e).

Corrigé

  • Version affichĂ©e dans l'UI — affichait 2.1.0 au lieu de 2.2.0 (commit 84bda90).
  • Mobile — boĂźte d'Ă©dition de l'assistant relevĂ©e au-dessus de la barre du bas (commit f307ecb).

[2.2.0] — 2026-09-11

Ajouté

  • Assistant AI contextuel (sidebar) — l'assistant s'adapte dĂ©sormais au contexte d'ouverture :

    • RĂ©pertoire (directory) : ouvert depuis le menu contextuel d'un dossier — le contexte est le dossier et son contenu (mode historique BooksLM).
    • 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 bloc obsigate-action rendu 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/context et /chat acceptent mode (directory/documents/ general) et context_files ; collect_files_context(), empty_context() et build_general_system_prompt() dans backend/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) et tests/frontend/ai.test.mjs (+8 tests dĂ©tection de contexte, clavier, actions, header).
  • #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 et externalDocs.
    • backend/schemas.py : nouveaux response_model Pydantic 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 override app.openapi pour 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).
  • #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 manifest plugin.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, protocole postMessage structurĂ©, 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 sans importScripts, 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.mjs aux tests JSDOM.
    • Docs : docs/PLUGINS.md (manifest, hooks, permissions, modĂšle de sĂ©curitĂ©, API, guide).
  • #78 Excalidraw — finitions : dĂ©compression lz-string du format plugin Obsidian, crĂ©ation depuis la modale et le menu contextuel, extraction du texte pour la recherche (B5), support .excalidraw.md (commit ac16fc1 + correctifs associĂ©s).

  • #67 Notifications web (Push API) : backend/push.py, POST /api/push/subscribe, clĂ©s VAPID dans config.json, toggle par vault, payload (fichier/vault/action) et ouverture du fichier au clic (commit ac16fc1).

  • #68 Health check enrichi : GET /api/health/detailed (admin-gated) exposant index, mĂ©moire (RSS/heap), uptime, connexions SSE, backups et espace disque (commit ac16fc1).

  • #77 Desktop — jumplist vaults rĂ©cents dans le menu DĂ©marrer (commit ac16fc1).

  • #75 Split View — matrice E2E complĂšte (37 tests) : 16 tests split-view.spec.js (raccourcis, drag & drop, persistance, navigation clavier) + 21 tests split-view-matrix.spec.js couvrant 100% des cas d'ouverture/fermeture.

Documentation

  • #78 Excalidraw — doc utilisateur finalisĂ©e : .excalidraw / .excalidraw.md ajoutĂ©s aux 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=true persistĂ© 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Ă©e envoie la requĂȘte, Ctrl+EntrĂ©e insĂš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/chat n'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 lisait data.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 via AuthManager (retry 401), parsing SSE event:/data: avec support token/error, historique construit avant l'ajout du message courant, barre de progression basĂ©e sur max_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 utilisait window.prompt() → remplacĂ©e par une modale accessible (Échap annule, Ctrl/Cmd+EntrĂ©e valide).
  • Recherche cassĂ©e (rĂ©gression plugins) : onSearchFilter() Ă©tait appelĂ©e avec 2 arguments au lieu de 3 (frontend/js/search.js), renvoyait undefined, 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.md corrompu Ă  l'ouverture : Excalidraw Ă©met onChange au 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 format frontmatter + compressed-json est prĂ©servĂ© Ă  la sauvegarde.

  • Splash / tests E2E : window.__OBSIGATE_BOOTED Ă©tait posĂ© avant la fin de init() — 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 de init() (frontend/js/app.js), et goHome()/login() attendent ce signal.

  • Tests E2E Excalidraw : réécriture de tests/e2e/excalidraw.spec.js avec 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), config OBSIGATE_PDF_MAX_SIZE_MB (50) + OBSIGATE_PDF_EXTRACT_TIMEOUT (30s). 16 tests supplĂ©mentaires (26 au total dans test_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 (lib webauthn==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 tests split-view-matrix.spec.js couvrant 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 via cygpath (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_stream plantait systĂ©matiquement (NameError: current_user non 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 en read_text() → contenu garbage indexĂ©. Utilise maintenant extract_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 psutil
    • GET /api/admin/audit — 500 derniĂšres entrĂ©es d'audit avec filtres user/action/limit/offset
    • GET /api/admin/backup-stats — compte + taille + age par vault
    • GET /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Ăšmes
  • backend/ — Endpoints backups, Excalidraw, multi-format, versioning git
  • docs/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+P fichiers, Ctrl+Alt+Espace commandes, Ctrl+Shift+Espace
    • Navigateur de dossiers : crĂ©er/supprimer fichiers/dossiers par sĂ©lection
    • OpĂ©rateurs +include / -exclude dans la recherche globale
    • 6 nouvelles commandes : crĂ©er/supprimer fichiers/dossiers, Ă©diter, pop-out
  • 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 home
  • backend/ — Indexation non-bloquante, statut IA, diff/restauration backups
  • docs/ — 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.11 sur 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)
  • Stemming français — snowballstemmer intĂ©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 modules
  • backend/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 erreurs assignment 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 DashboardStatsWidget avec 4 cartes (fichiers, tags, taille, vaults)
    • Widget "Derniers fichiers ouverts" avec timestamps
  • Documentation OpenAPI enrichie — Tous les modĂšles Pydantic documentĂ©s (Field(description=...))
    • Visible dans /docs (Swagger UI) et /redoc
  • Documentation complĂšte — ANALYSE_REVIEW.md, AUDIT_TECHNIQUE.md, ROADMAP.md

Modifié

  • frontend/js/app.js — Nouveau systĂšme d'onglets (TabManager), dashboard
  • frontend/style.css — Dashboard, share view, webhooks UI
  • backend/main.py — Nouveaux endpoints share/webhooks/dashboard
  • backend/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 dans data/audit.log, rotation 10MB
  • Backup automatique avant Ă©criture
    • Sauvegarde du contenu original dans .obsigate-backup/ avant PUT/DELETE
  • Backlinks panel — Fichiers avec wikilinks pointant vers le fichier courant
    • GET /api/file/{vault}/backlinks + index inversĂ© dans indexer.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
  • 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 endpoint
  • backend/indexer.py — +backlink index, +IGNORED_DIRS
  • backend/auth/router.py — +IP rate limiting
  • backend/auth/jwt_handler.py — +configurable TTL
  • backend/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, sync
  • frontend/style.css — CodeMirror themes, auth forms, PWA UI, responsive
  • docker-compose.yml — auth env vars, data volume, non-root user
  • backend/main.py — auth middleware, file CRUD, code editor API, SSE, PWA
  • requirements.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 (![alt](path))
    • 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'images
  • backend/image_processor.py — PrĂ©traitement Markdown pour les syntaxes d'images
  • GET /api/image/{vault}?path= — Serve images avec MIME types
  • POST /api/attachments/rescan/{vault} — Rescan manuel
  • HIDDEN_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Ă©)
  • 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, autocomplete
  • frontend/app.js — Refonte recherche : nouveau systĂšme de requĂȘtes, facettes, pagination, autocomplete
  • frontend/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