Quatre retouches d'interface :
- Le sélecteur de l'en-tête liste maintenant les MODÈLES du disque (groupe
« Modèles (.gguf) ») en plus des presets : en choisir un le charge —
POST /api/models/use écrit MODEL seul (contexte, NGL et échantillonnage
conservés) et redémarre le service en arrière-plan. Sans preset créé, le
sélecteur n'offrait aucun choix.
- Pourcentage de chargement : sur un redémarrage à chaud, le modèle est déjà
dans le cache disque — llama-server ne lit rien (read_bytes reste à 0) et
la pastille passait de « 0 % » à « prêt » sans jamais monter. On prend le
plus avancé de read_bytes et de la mémoire résidente (VmRSS), qui grandit
cache ou pas.
- Discussions triées par date de CRÉATION (récentes en tête) : une
discussion garde sa place, écrire dans un vieux fil ne le fait plus
remonter.
- Bouton Réglages ancré en pied de barre latérale, juste au-dessus du
moniteur Performance — toujours au même endroit, quel que soit le nombre
de discussions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
La barre latérale ne garde que les discussions et le moniteur machine : tout
le bloc « Réglages » (11 sections empilées en details) devient une modale
avec sa nav de sections à gauche et LE panneau choisi à droite — chaque
section a enfin toute la largeur, et la barre latérale respire.
Les panneaux réutilisent tels quels les gabarits existants (.slist,
.switchrow, .subhead) ; les ids historiques (svc-log-box, lc-details,
tasks-details…) sont conservés sur les panneaux, et openDetails() les résout
vers openSettings(panneau) : pastille d'état du moteur, pastille
d'installation et jobs llama.cpp rouvrent la modale au bon endroit sans
changer leurs appelants. Le chargement paresseux du journal du moteur
(ancien ontoggle) devient un hook d'affichage du panneau.
Discussions : la plus récente en tête. Le serveur triait déjà par date
d'activité, mais son tri stable laissait une discussion toute neuve SOUS
celle qu'on venait de quitter (touchées dans la même seconde) : l'UI
départage par date de création puis par ordre d'index.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deux manques dans la présentation d'un tour :
1. Rien ne disait sur QUELLE discussion un agent était en train de
travailler. La ligne concernée porte maintenant un anneau qui tourne,
à gauche du titre — et l'en-tête aussi, puisque la barre latérale est
escamotée sur téléphone. L'état vient de deux sources : le flux SSE
(instantané) et /api/conversations, qui expose désormais `busy` pour
que la page ouverte en cours de génération s'anime sans attendre le
rejeu du journal. L'indicateur est posé et retiré SANS redessiner la
liste : un redessin relancerait l'animation à chaque événement.
2. Le pied des réponses ne montrait que la vitesse du moteur, jamais le
temps que le tour avait pris. Il porte maintenant « travail 1 min 04 s »
— de la question envoyée à la fin du tour, raisonnement, appels
d'outils et attentes compris. La durée avance à la seconde pendant le
tour et se fige au turn_done ; elle apparaît aussi sur l'indicateur
« … », là où il n'y a pas encore de réponse sous laquelle écrire.
La bulle de raisonnement gagne au passage sa propre durée.
La mesure est prise sur les HORODATAGES SERVEUR (événements `user` et
`turn_done`) : elle est donc juste en direct comme au rejeu, où tout
arrive d'un bloc côté client. Le compteur vivant se recale sur l'écart
entre l'horloge du navigateur et celle du serveur, réévalué à chaque
événement reçu en direct — un téléphone n'est pas à la même heure que la
machine.
« Masquer la vitesse de génération » ne masque plus que la vitesse : la
durée n'est pas une mesure de moteur, c'est ce que la réponse a coûté.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5UndZ9DedRXPuAoRXbmDb
Les deux icônes de la carte de saisie venaient de la maquette ; tout le reste
était encore des GLYPHES DE TEXTE — ↻ × ☰ ⌫ 🗑 ✎ ↓ ▸ ▣ ·. C'est le système
d'exploitation qui les dessinait : le même bouton sortait fin sur macOS, gras et
bleuté sur Windows (émojis en couleur), et absent d'une machine sans police
d'émojis. Aucune cohérence possible, et rien à régler depuis la feuille de style.
Elles passent en SVG, tracées comme celles de la maquette : boîte de 24, trait de
1,8, extrémités arrondies, couleur héritée. Un seul fichier les décrit
(03-icons.js) — menu, fermer, rafraîchir, crayon, corbeille, téléchargement,
flèche, dossier, fichier, image, plus, chevron.
Touchées : le ☰ du téléphone, les croix de modale, les deux rafraîchissements,
le « + » des sections, la flèche de retour en bas, le téléchargement Hugging
Face, « vider la discussion », les lignes du panneau Fichiers (dossier, image,
fichier, télécharger, supprimer), renommer/supprimer une discussion, éditer un
serveur MCP.
Deux pièges rencontrés, notés dans la feuille :
- Une règle de centrage qui pose `display:inline-flex` sur ces boutons les rend
VISIBLES en permanence : plusieurs se masquent justement par `display:none`
(le ☰ hors téléphone, le « + » d'une section fermée, la flèche de retour en
bas). Le ☰ s'est ainsi retrouvé par-dessus l'en-tête sur grand écran. Le
centrage passe donc par le rembourrage pour ceux-là.
- Les titres de panneau (.stitle) et l'intertitre « Paramètres » gardaient le
monospace en capitales de la charte précédente ; ils suivent maintenant le
seul style de titre de la maquette, celui de « Performance ».
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
Reprise du design d'après la maquette fournie, sans changer d'architecture :
l'UI reste du HTML/CSS/JS assemblé dans le binaire (voir plus bas).
Charte
- Palette ardoise + sauge en remplacement des bruns « Terre ». Deux variantes :
claire par défaut (celle de la maquette) et « Deep Dark » (fond #0F172A,
cartes #1E293B), à un clic depuis l'en-tête. Une variable --on-accent porte ce
qui s'écrit sur les aplats de sauge, pour que le contraste tienne dans les
deux variantes.
- Typographie Inter (interface) + JetBrains Mono (code, chiffres, chemins), en
sous-ensemble latin, embarquées comme l'étaient Bricolage et IBM Plex Mono —
toujours aucune requête vers un service de polices.
- Boutons sans contour, fond au survol, léger enfoncement au clic.
- Coque à plat : plus de cartes flottantes séparées par une gouttière, la barre
latérale est collée au bord et le fil occupe tout le reste.
Coque
- Barre d'en-tête : titre de la discussion + sélecteur de modèle. Changer de
preset demandait d'ouvrir la barre latérale et de descendre jusqu'aux
presets ; c'est désormais une liste déroulante, toujours visible.
- Barre latérale en trois zones — marque, contenu défilant, moniteur machine —
et escamotable d'un clic sur grand écran. Les discussions y passent au
premier plan, avec une recherche textuelle (filtrage côté client : la liste
est déjà chargée, sans les messages) ; les réglages descendent sous un repli
unique, d'où openDetails(), qui déplie aussi les parents.
- Moniteur machine en pied de colonne : une jauge par ressource (charge GPU,
puis VRAM et température en détail, mémoire vive), sauge jusqu'à 75 %, ambre
puis rouille quand la machine sature. Il remplace la section « Machine », qui
disait la même chose en plus verbeux et en plus loin.
- L'option « barre latérale escamotable » disparaît d'Apparence : elle faisait
de la barre un tiroir posé SUR la conversation, là où le bouton d'en-tête
l'escamote pour de bon. Deux mécanismes concurrents pour la même intention.
Fil et saisie
- La réponse de l'IA redevient une carte, la question un aplat de sauge : sur un
fond de fil coloré, du texte nu flottait sans ancrage.
- Saisie en pilule, ses outils (joindre, fichiers, envoyer) dans la barre
elle-même. L'envoi est une pastille ronde à flèche ; le mot « Envoyer » reste
porté par aria-label et l'infobulle.
Corrections croisées
- L'heure d'une discussion prenait la moitié de la ligne (flex:1 hérité de
`.preset>span`), le titre était tronqué au premier mot.
- Une liste déroulante posée directement dans une ligne de modale débordait
sous son intitulé quand sa valeur était longue (chemin de destination).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
Les fichiers du chat vivaient dans un pot commun : on changeait de discussion
et on revoyait les mêmes pièces jointes, et supprimer une discussion laissait
derrière elle tout ce qu'on y avait déposé ou fait écrire à l'agent (seules ses
captures, déjà rangées par identifiant, partaient avec).
Chaque discussion a désormais son dossier, <workspace>/discussions/<id>/ :
- les dépôts (uploads/), les captures (captures/) et ce que l'agent écrit y
atterrissent ; le shell et les chemins relatifs du modèle y sont résolus ;
- le panneau Fichiers s'ouvre sur ce dossier et n'en sort pas, et se redessine
quand la discussion change (bascule ou vidage, signalés par le flux SSE) ;
- supprimer une discussion — ou la vider — emporte ses fichiers. Les deux gestes
le disent maintenant avant de demander confirmation ; « clear chat » en
demandait aucune.
La racine du dossier de travail reste la borne de sécurité : les liens des
anciens messages (uploads/x.pdf, captures/<id>/y.jpg) continuent d'ouvrir leur
fichier par un chemin de repli. Au démarrage, une migration range les captures
dans le dossier de leur discussion et rend chaque dépôt à la discussion qui le
mentionne dans son journal ; ce que personne ne réclame reste à la racine,
atteignable par le bouton « hors discussion » du panneau, qui disparaît une fois
le ménage fait.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
L'amont ne connaît qu'un fil unique (bkChat/conversation) que « clear chat »
effaçait définitivement. On garde toute la machinerie (un seul conv en
mémoire, mêmes flux SSE, même compactage) mais rangée par discussion :
bkChat/index liste des discussions (métadonnées seules)
bkChat/active discussion ouverte, partagée par tous les appareils
bkChat/conv:<id> état complet d'une discussion
Basculer réutilise le mécanisme d'epoch du reset : les abonnés SSE reçoivent
{reset:true} et rejouent le nouveau fil — aucun code de rendu à toucher. Le
fil unique existant est repris comme première discussion au premier
démarrage, et sa clé d'origine est laissée intacte.
- routes /api/conversations (liste, new, switch, rename, delete)
- barre latérale : liste (titre déduit du 1er message, date, nb d'échanges),
bouton +, renommer, supprimer ; lignes construites en DOM et non en
innerHTML, les titres venant de messages utilisateur
- suppression de la dernière discussion : convCreate et non convNew, qui
aurait réenregistré celle qu'on vient d'effacer
Le bouton « Vérifier les mises à jour » répondait « GitHub a répondu 404 » :
il interrogeait les releases du dépôt du fork, qui n'en publie aucune. En
conteneur, remplacer le binaire n'a de toute façon pas de sens — l'UI, l'API
et la CLI renvoient désormais « docker compose pull ».
Vérifié en conteneur : création, bascule, renommage (titre accentué avec
< > &), suppression de l'active puis de la dernière ; /api/update et
loki update renvoient la note Docker.