BUG-012: le menu @ est toujours rendu et _mentionVault() retombe sur le vault de contexte puis le premier vault disponible. #82: suppression des intitules Fournisseur & modele / Fournisseur:, description du contexte General en info-bulle, placeholder retire, bouton Envoyer en emoji, indicateur d'activite (envoi/reception/outils/confirmation/succes/echec), SW_VERSION v5.
BUG-011: popover des modeles aligne a droite (right:0), largeur 340px bornee, noms sur plusieurs lignes + title. BUG-010: la selection @ capture le vault renvoye par tree-search et _contextVault() le propage aux requetes /context et /chat, corrigeant le contexte ad-hoc en mode General.
Locales chargees en cache no-store + SW_VERSION v4 pour eviter les cles brutes (fr.json obsolete). Picker : styles critiques en ligne (popover/liste/options), plafond de 200 modeles avec indicateur, et non-chevauchement entre la liste de modeles et la bulle de capacites.
Refonte de la section Fournisseur & modele de l'assistant : capacites regroupees dans une bulle d'information (survol/clic/appui long) au lieu d'une liste permanente, et liste de modeles avec recherche. Correctifs : detection Unicode + filtrage insensible aux accents des commandes / et mentions @, navigation clavier fiable, et prise en compte du contexte ad-hoc en mode General cote backend.
- Signature des mises a jour Tauri : paire de cles minisign generee, cle publique dans tauri.conf.json, createUpdaterArtifacts actif, secrets CI exposes (repli build non signe si secret absent).
- Wizard 1er lancement : etat persistant wizard_done cote Rust (get_wizard_state/complete_wizard) pour ne plus reafficher la banniere, retro-compatible.
- Protocole des 6 tests E2E manuels : docs/DESKTOP_E2E_CHECKLIST.md.
- CI : desktop.test.mjs ajoute au job lint.
- Docs : ROADMAP, fiche desktop-tauri, CHANGELOG, README desktop, guide releases.
- fix(build): retirer les libs WeasyPrint inutiles du stage builder Docker.
Le .gitignore exclut _*.py, donc backend/tools/__init__.py n'etait pas versionne et
la CI echouait a l'import (unknown location). Remplacement par une facade api.py
qui enregistre les outils et reexporte l'API publique. Tests adaptes.
Contexte:
- Repertoire (mode historique BooksLM, via menu contextuel dossier)
- Documents (bouton flottant quand des documents sont ouverts)
- General (bouton flottant sans document: aide app + creation de fichiers)
- Le contexte est affiche dans le header; suggestions adaptees au mode
- Blocs obsigate-action rendus en carte avec bouton Appliquer (pas d'execution auto)
Backend:
- /api/ai/bookslm/context et /chat acceptent mode + context_files
- collect_files_context(), empty_context(), build_general_system_prompt()
- mode documents degrade vers general si aucun fichier lisible
Corrige:
- bouton flottant passait le chemin d'un fichier comme repertoire -> erreur
'Aucun fichier markdown trouve dans ce dossier'
- panneau precedent masque (localStorage) ne se rouvrait plus
- selecteur fournisseur/modele deplace sur une ligne dediee (header actions visibles)
- Entree envoie, Ctrl+Entree insere un saut de ligne
Tests: test_bookslm.py (+13) et tests/frontend/ai.test.mjs (+8)
BUG-001 (PDF stream 500) : le nom Unicode du fichier etait injecte brut
dans Content-Disposition -> en-tete HTTP invalide -> 500. Nouveau helper
_content_disposition (RFC 5987 : filename ASCII + filename*=UTF-8'').
Applique aux 2 branches /pdf/stream. Test: tests/test_pdf_stream.py.
BUG-002 (Excalidraw 'Loading...' infini) : deux causes.
1) L'import esm.sh avec ?alias=react:... etait propage a chaque dep
transitive; esm.sh renvoie 408 sur les builds a gamme semver (jotai@>=2.9.2)
-> import entier echoue. On retire l'alias et on utilise React 19 coherent
(spec a gamme + target identique a celle d'Excalidraw) -> plus de 408.
2) L'editeur passait la prop legacy 'excalidrawRef', inoperante en 0.18
(la prop reelle est 'excalidrawAPI') -> l'API n'etait jamais recue,
le 'Loading' ne se masquait pas, save/export morts.
Verifie : 534 tests backend verts (dont 3 nouveaux PDF) + E2E navigateur
(loading masque + cycle requestSave->save OK).
Corrige l'incoherence d'affichage (page principale 0.0.0-dev vs a propos
v2.0.0-dev) et unifie la gestion de version sur une seule source de verite.
Backend:
- version.py: get_version() retourne toujours le tag x.y.z propre du dernier
tag (plus de 0.0.0-dev / -N-gHASH dans l'UI). Ajout get_git_describe() et
get_git_commit() pour le detail.
- main.py: /api/health expose git_describe + git_commit (nouveaux champs).
Frontend:
- modale A propos (populateAbout): version + commit lus depuis /api/health,
suppression du hardcode v2.0.0-dev dans index.html.
- section config A propos (loadAbout): lit health.version, plus de state.
APP_VERSION obsolete.
- badge header: fetch /api/health (fallback neutre '...').
- state.js: suppression de APP_VERSION (1.5.0) devenu mort; imports nettoyes.
Script:
- scripts/bump_version.sh: incrementation SemVer selon les commits
(breaking->major, feat->minor, sinon patch), creer/pousser le tag vX.Y.Z.
Tests: 507 passed, frontend validators OK.
Le menu Admin redirigeait vers /admin.html mais aucune route backend ne le
servait : le catch-all SPA /{full_path:path} renvoyait index.html, d'où le
retour à la page principale.
- backend/main.py: route GET /admin.html (gate require_admin) déclarée avant
le catch-all SPA + correction des imports JS /frontend/js -> /static/js
- frontend/admin.html: chemins d'assets alignés sur le mount /static
- tests/test_admin.py: 3 tests de régression (page servie admin, refus 401/403)
1. **fix(xiaomi): la clé Xiaomi échouait avec 'Name or service not known'**
- URL précédente api.xiaomi.com n'existe pas (DNS fail)
- Vraie URL: https://api.xiaomimimo.com/v1/models
- Xiaomi MiMo utilise un header custom 'api-key:' (PAS 'Authorization: Bearer')
- Modèles par défaut mis à jour: mimo-v2.5-pro, mimo-v2.5, mimo-v2.5-asr, etc.
- Le chat dans ai.py supporte maintenant un header custom via auth_header_name
2. **fix(admin): EventSource 503 → fallback polling**
- Ajout d'un fallback: si EventSource ne reçoit jamais le premier event
(cookie expiré, réseau bloqué, proxy timeout), on bascule sur du polling
/api/admin/stats toutes les 5s. Le UI continue de se mettre à jour.
- Cleanup correct du timer dans disconnectSSE
3. **fix(test_ai_models)**: 2 nouveaux tests de régression
- test_xiaomi_uses_api_key_header_not_bearer: vérifie URL + header
- test_xiaomi_test_endpoint_uses_real_url: vérifie /api/config/ai-keys/test
- Patche backend.ai ET backend.main (import local)
- Header case-insensitive (urllib normalise à 'Api-key', HTTP est insensible)
Vérifié :
- pytest : 504 passed (502 + 2 nouveaux Xiaomi)
- frontend unit : 7 passed
- validate-imports : 30 modules / 204 exports
- pane-manager JSDOM : 9/9
- ruff check backend/ : All checks passed
Trois bugs corrigés + une amélioration demandée :
1. **fix(admin): /admin.html redirigeait toujours vers /**
- admin.js _gateAdmin() lisait /api/auth/status qui ne contient PAS le rôle user
- Remplacé par /api/auth/me (retourne username, role, vaults)
- Le code distingue maintenant le cas 'auth désactivé' (admin anonyme) du
cas 'auth requise non admin' (affiche écran forbidden)
2. **fix(ai): les modèles Nvidia/Xiaomi ne se chargeaient pas dans les dropdowns**
- Xiaomi : l'endpoint public /v1/models est instable, échec réseau fréquent
- Toutes les erreurs réseau/d'API renvoyaient models=[] → dropdown vide
- Ajout d'un fallback curé : _FALLBACK_MODELS dict avec 4-8 modèles populaires
par provider, TOUJOURS retourné si la clé manque OU si l'API distante échoue
- Le frontend voit désormais 'fallback' vs 'live' comme hint pour l'utilisateur
- Gemini parsing : strip du préfixe 'models/' retourné par l'API Gemini
- 7 nouveaux tests pytest pour _FALLBACK_MODELS + endpoint /api/config/ai-models
3. **feat(ai): picker provider/model dans la toolbar AI + BooksLM**
- Plusieurs providers peuvent maintenant être activés simultanément
- Sélection provider+model par section (Forge, BooksLM, etc.) via dropdown
- État persisté en localStorage (le choix suit l'utilisateur entre sections)
- Chaque appel AI passe maintenant {provider, model} au backend
- ai.js : aiAction() lit le picker et l'injecte dans le body
- bookslm.js : envoie provider+model à /api/ai/bookslm/chat
- ai_routes.py + bookslm_routes.py : AIRequest et BooksLMChatRequest
acceptent provider+model, avec save/restore du modèle original
pour ne pas affecter les autres requêtes concurrentes
- 7 nouvelles clés i18n (ai.provider, ai.model, ai.model_loading, etc.)
- 1 nouveau test bookslm vérifie que le schema accepte provider+model
- validate-imports.mjs : fix faux positif sur 'export { X as Y }'
Vérifié :
- pytest : 502 passed, 5 skipped (494 baseline + 7 AI models + 1 BooksLM)
- frontend unit : 7 passed
- validate-imports : 30 modules / 204 exports / 0 erreur
- pane-manager JSDOM : 9/9
- ruff check backend/ : All checks passed
- tests/test_pdf.py : 13 tests (100% verts) couvrant :
- extract_pdf_text/metadata/toc avec edge cases (corrupt, missing, truncation)
- .pdf dans SUPPORTED_EXTENSIONS
- _scan_vault() extrait le texte des PDFs (vérifié avec fixture reportlab)
- parseur du filtre ext:pdf
- backend/pdf_reader.py : fix NameError quand pymupdf est installé
(PdfReader n'était déclaré que dans la branche except ImportError)
- backend/requirements-test.txt : reportlab pour générer des PDFs de test (devDep only)
- README.md + README.fr.md : section 'PDF support' documentée
- docs/ROADMAP.md : #74 marqué 'pratiquement complet' avec détail honnête des items livrés vs non
- CHANGELOG.md : entrées pour #71 admin (déjà dans commit précédent), #75 I2 et #74
inotify ne voit pas les ecritures faites depuis d'autres clients NFS
(Obsidian sur Windows), donc index_updated n'etait jamais emis et la
section Recent du sidebar ne se rafraichissait pas. Detection du fstype
par vault via /proc/mounts -> PollingObserver (stat-based) sur mounts
reseau, Observer natif conserve sur les mounts locaux.
L'input de la palette porte la classe .cp-input et non l'id #cp-input. Le fallback de dispatch manuel du raccourci est supprime : la cause racine est traitee.
- search.js : suppression par flag (jusqu a la prochaine frappe) au lieu d un timeout 500ms qui expirait avant le clic sur le bouton Forge
- split-view.spec.js : le test reset panes attend l apparition de la liste de commandes (debounce 150ms du palette) avant Entree