# #94–#97 — Assistant IA : historique permanent, accès sidebar, bouton rond et panneau « + » > **Statut :** ✅ Livré (en attente de validation utilisateur) > **Effort :** ~7 jours | **Impact :** 🟡 > **Références :** [Roadmap](../ROADMAP.md) · [Changelog](../../CHANGELOG.md) - **Description :** quatre améliorations de l'Assistant IA livrées ensemble : 1. **#94** — bouton de soumission **circulaire** avec icône Lucide `arrow-up` (remplace l'avion ✈️). 2. **#95** — **historique permanent** des conversations **persisté côté backend** (les échanges survivent aux rechargements de page et aux navigateurs). 3. **#96** — accès rapide à l'**historique depuis la sidebar de navigation** (onglet dédié). 4. **#97** — panneau **« + » extensible** remplaçant « Attach an image » (modules : fichiers, image, contextes, skills, Deep Research, web, Canva). ## A. Backend — persistance des conversations (#95) — ✅ livré - [x] **`backend/ai_history.py`** (nouveau) : stockage JSON par utilisateur (`data/ai_history/{username}.json`), écriture atomique (tmp + `shutil.move`), plafond `MAX_SESSIONS = 200` (purge des plus anciennes), tri par `updatedAt` desc. - [x] `list_sessions`, `get_session`, `upsert_session`, `delete_session` ; la liste renvoie des **résumés sans messages** (`_summary` : titre, mode, vault, contexte, `message_count`, `preview`) pour rester légère. - [x] **`backend/bookslm_routes.py`** : modèle Pydantic `BookslmSession` et 4 endpoints : - `GET /api/ai/bookslm/history` — liste des conversations (résumés). - `GET /api/ai/bookslm/history/{session_id}` — conversation complète (404 si absente). - `PUT /api/ai/bookslm/history/{session_id}` — création/mise à jour (l'`id` du corps est forcé à l'`id` du path → jamais d'écriture sous une autre clé). - `DELETE /api/ai/bookslm/history/{session_id}` — suppression (booléen `ok`). - [x] Isolation par utilisateur (`require_auth`) ; saisies défensives (username/id vides) → `[]` ou `None`. ## B. Frontend — sync serveur + cache local (#95) — ✅ livré - [x] `frontend/js/bookslm.js` : clé local globale `bookslm-sessions-all-{username}` ; **migration** des anciennes clés périmées `bookslm-sessions-` et `bookslm-history-` à la première ouverture. - [x] `_loadHistory(preferredSessionId)` **async** au chargement du panneau : lecture serveur (`GET /history`), hydratation du localStorage, repli hors-ligne silencieux si le serveur ne répond pas. - [x] Synchronisation **debounced 600 ms** (`_syncHistoryToServer` → `_flushHistoryToServer`) via `_dirtySessionIds` / `_deletedIds` : `PUT`/`DELETE` seulement pour les sessions modifiées ; pas d'appel réseau à la simple ouverture. - [x] `_positionSession` : nouvelle session insérée en tête, session rechargée remise à jour. - [x] `openContext(opts, preferredSessionId)` **async** ; `openWithSession(sessionOrId)` public pour ouvrir une conversation connue (utilisé par la sidebar #96). - [x] `_notifyHistoryChanged()` : événement `bookslm:history-updated` pour rafraîchir la sidebar #96 sans rechargement. ## C. Frontend — sidebar & historique (#96) — ✅ livré - [x] `frontend/index.html` : cinquième onglet `#sidebar-tab-ai` (icône Lucide `messages-square`) et panneau `#sidebar-panel-ai` (`#ai-history-list`, `#ai-history-empty`). - [x] `frontend/js/config.js` : `loadAISessionList()` / `renderAIHistoryList()` (réutilise les classes `.recent-*` de l'UI), placeholder « Aucune conversation », listener `bookslm:history-updated` monté par `initSidebarTabs()` et actif quand l'onglet `ai` est affiché. - [x] Le clic sur une conversation l'ouvre dans le panneau Assistant IA (via `openWithSession`) et bascule la sidebar. ## D. Frontend — panneau « + » extensible (#97) — ✅ livré - [x] Bouton « Attach an image » remplacé par un bouton **« + »** circulaire (`frontend/js/bookslm.js`, `.bookslm-btn-plus`). - [x] Panneau overlay `.bookslm-ext-menu` (dropdown au-dessus de la zone de saisie, fermeture au clic extérieur / Échap) listant les modules. - [x] Architecture **modulaire** : registre `_extensions` (objet d'enregistrement simple) — chaque entrée : `id`, icône, libellé i18n, action ; `renderExtMenu()` construit la liste ; un nouveau module s'ajoute en une entrée. - [x] Modules livrés : **Fichiers** (sélecteur général `.bookslm-files-input`), **Image** (joindre une image), **Contextes/fichiers**, **Skills**, **Deep Research** (mode agent + prompt pré-rempli), **Recherche sur Internet**, **Canva**. - [x] `web`/`canva` affichés **désactivés** avec badge « Bientôt » (`.bookslm-ext-soon`) — le catalogue d'outils #92 les alimentera. - [x] `_startDeepResearch()` : active le mode agent, injecte le prompt Deep Research et déclenche l'envoi. ## E. UI — bouton rond & icône (#94) — ✅ livré - [x] `.bookslm-btn-send` circulaire (40 px, `border-radius: 50%`), icône Lucide `arrow-up` à la place de l'emoji ✈️, aligné en bas à droite de la zone de saisie. - [x] i18n FR/EN : `bookslm.add_options`, `bookslm.ext_files`, `bookslm.ext_image_hint`, `bookslm.ext_contexts`, `bookslm.ext_skills`, `bookslm.ext_deep_research`, `bookslm.ext_web`, `bookslm.ext_canva`, `bookslm.coming_soon`, `bookslm.soon`, `bookslm.ext_more_coming`, `bookslm.deep_research_prompt`, `bookslm.deep_research_started`, `bookslm.mode_*`. ## F. Tests — ✅ livré - [x] `tests/test_bookslm.py` : `TestBooksLMSessionHistoryEndpoints` (7) + `TestAIGatewayHistoryStore` (4) — liste résumée sans messages, full 404, upsert qui force l'id et isole par utilisateur, delete, cap à 200, purge. - [x] `tests/frontend/ai.test.mjs` : persistance/reouverture/suppression de sessions, migration de l'historique legacy, menu d'historique. - [x] Vérifs : pytest **1088 passed / 6 skipped**, ruff 0, mypy 0 (71 fichiers), frontend `ai.test.mjs` **84/84**, `unit.test.mjs` 9/9, `validate-imports` 38 modules. ## G. Points d'attention - Le localStorage reste un **cache** : le serveur est la source de vérité (`data/ai_history/`). En cas de données locales corrompues, un `localStorage.clear()` réinitialise proprement. - `MAX_SESSIONS = 200` : le comportement de purge est testé (`TestAIGatewayHistoryStore`) ; la rétention configurable (item #95 du backlog) pourra s'appuyer sur ce plafond. - Les modules web/Canva du panneau « + » sont volontairement désactivés tant que l'écosystème d'outils phase 2 (#92) n'est pas livré.