Historique : plusieurs discussions, et mise à jour par l'image en conteneur

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.
This commit is contained in:
Loki committed 2026-08-15 11:07:45 +00:00
1 parent 1f24d8ccf5
commit 2fbf86cdde
12 files changed
+586 -17

No files matched your search

+54
View File
@@ -120,6 +120,60 @@ func handleChatReset(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, map[string]any{"ok": true})
}
// --- Discussions (plusieurs fils) ------------------------------------------
// Le fil actif est PARTAGÉ par tous les appareils, comme la conversation unique
// de l'amont : basculer sur le téléphone bascule aussi le PC (l'epoch fait
// rejouer le nouveau fil à tous les abonnés SSE).
func handleConvList(w http.ResponseWriter, r *http.Request) {
list, active := ConvList()
if list == nil {
list = []convMeta{}
}
sendJSON(w, 200, map[string]any{"conversations": list, "active": active})
}
func handleConvNew(w http.ResponseWriter, r *http.Request) {
sendJSON(w, 200, map[string]any{"ok": true, "id": convNew()})
}
func handleConvSwitch(w http.ResponseWriter, r *http.Request) {
var req struct {
ID string `json:"id"`
}
_ = json.NewDecoder(r.Body).Decode(&req)
if err := convSwitch(req.ID); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
sendJSON(w, 200, map[string]any{"ok": true})
}
func handleConvRename(w http.ResponseWriter, r *http.Request) {
var req struct {
ID string `json:"id"`
Title string `json:"title"`
}
_ = json.NewDecoder(r.Body).Decode(&req)
if err := convRename(req.ID, req.Title); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
sendJSON(w, 200, map[string]any{"ok": true})
}
func handleConvDelete(w http.ResponseWriter, r *http.Request) {
var req struct {
ID string `json:"id"`
}
_ = json.NewDecoder(r.Body).Decode(&req)
if err := convDelete(req.ID); err != nil {
sendJSON(w, 400, map[string]any{"ok": false, "error": err.Error()})
return
}
sendJSON(w, 200, map[string]any{"ok": true})
}
// handleChatCompact lance une compaction manuelle du contexte (bouton UI). La
// progression est diffusée via le flux d'abonnement (compacting/compacted).
func handleChatCompact(w http.ResponseWriter, r *http.Request) {