feat(assistant): #91 fenêtre de résultats — post épinglé en haut, fournisseur/modèle discret & bouton copier
CI / lint (push) Successful in 1m20s
CI / security (push) Successful in 57s
CI / test (push) Successful in 2m41s
CI / build (push) Successful in 55s
CI / e2e (push) Successful in 10m40s

This commit is contained in:
2026-09-15 12:04:38 -04:00
parent b69cb9b0f8
commit 256f5a4a03
9 changed files with 275 additions and 37 deletions
@@ -0,0 +1,57 @@
# #91 — Assistant IA — Fenêtre de résultats : post en haut, fournisseur/modèle & bouton copier
> **Statut :** ✅ Livré
> **Effort :** 0,5-1 jour | **Impact :** 🟡
> **Références :** [Roadmap](../ROADMAP.md) · [BooksLM #76](./bookslm.md) · [Assistant UX #80](./ai-assistant-ux.md) · [Changelog](../../CHANGELOG.md)
- **Description :** Trois améliorations d'expérience dans la fenêtre de résultats de
l'assistant IA (`frontend/js/bookslm.js`, `frontend/style.css`) :
1. **Post toujours en haut** : dès qu'un message est soumis, ce tour (le post
utilisateur **et** la réponse de l'assistant en cours d'écriture) est épinglé en
haut de la fenêtre de messages, afin que le résultat écrit par l'assistant reste
toujours visible — même après re-render successifs et auto-scroll.
2. **Fournisseur & modèle discrets** : juste au-dessus du bloc de texte rédigé par
l'assistant, un libellé discret rapporte le fournisseur et le modèle réellement
utilisés (fournis par le backend via le flux SSE `{token, provider, model}`).
3. **Bouton « Copier »** : sous le bloc soumis par l'utilisateur ainsi que sous le
bloc rédigé par l'assistant, un bouton copie le texte brut de ce bloc dans le
presse-papiers (avec toast de confirmation).
## A. Post toujours en haut — ✅ livré
- [x] **A1.** Chaque message est enveloppé dans un bloc `.bookslm-msg` ; le conteneur
`.bookslm-messages` est un flex column et les blocs du **dernier tour** (depuis le
dernier message utilisateur inclus) portent `.latest` → `order: -1`, les épinglant
en haut de la fenêtre.
- [x] **A2.** La détection du dernier tour se fait au rendu (`_renderMessages()`) en
recherchant le dernier message `role === 'user'` ; tout ce qui suit (sa réponse, ses
actions/cartes) reste groupé au-dessus de l'historique antérieur.
- [x] **A3.** L'auto-scroll vers le bas (`scrollTop = scrollHeight`) conserve le dernier
tour en haut, hors d'atteinte du contenu précédent.
## B. Fournisseur & modèle discrets — ✅ livré
- [x] **B1.** `_streamResponse()` capture `data.provider` et `data.model` du flux SSE
(déjà émis par `/chat` et `/agent`) sur le message assistant.
- [x] **B2.** Au rendu d'un message assistant, `.bookslm-msg-meta` affiche
« `fournisseur · modèle` » au-dessus du bloc (visible uniquement si l'info est reçue).
- [x] **B3.** Style discret (10px, `var(--text-secondary)`), non sélectionnable.
## C. Bouton « Copier » — ✅ livré
- [x] **C1.** `_appendCopyButton()` ajoute un bouton `.bookslm-copy-btn` sous chaque
bloc non vide (utilisateur et assistant) avec le texte i18n `bookslm.copy`.
- [x] **C2.** `_copyText()` copie le **texte brut** du message (`navigator.clipboard`,
repli `_fallbackCopy`) et affiche le toast `bookslm.copied`.
- [x] **C3.** Le bouton est masqué par défaut (opacité 0) et révélé au survol du bloc
`.bookslm-msg`.
## D. Tests & documentation — ✅ livré
- [x] **D1.** `tests/frontend/ai.test.mjs` : dernier tour épinglé, tag fournisseur/modèle
rendu, boutons copier présents sur les deux blocs, copie du texte brut, absence de
bouton pour un contenu vide.
- [x] **D2.** i18n FR/EN : `bookslm.copied`.
- [x] **D3.** CHANGELOG + Roadmap.
## E. Points d'attention
- Le tag provient du **backend réel** (`provider`/`model` du SSE) et reflète donc le
modèle effectif même lorsque l'utilisateur conserve la sélection « par défaut ».
- Le copier utilise le texte brut (pas le HTML rendu) — cohérent avec l'export de
conversation et évite d'embarquer des liens/balises de rendu.