Files
Loki/internal/jean/web_prefs.go
T
nathaninline 1360eb1943 v0.6.4 : serveurs MCP connectes en parallele et prechauffes, vitesse de generation de retour, retouches du menu
MCP : les serveurs actifs se connectent en parallele au lieu de la file
indienne (quatre serveurs npx n'additionnent plus leurs demarrages) et sont
prechauffes au lancement du service, donc le premier message ne paie plus le
handshake. Une session est publiee dans le pool avant d'aboutir : deux
appelants simultanes partagent la meme tentative au lieu de lancer deux
process.

Interface : la vitesse de generation (prefill/decode) reapparait sous chaque
reponse, en fondu, avec un interrupteur pour la masquer ; infobulles " ? "
qui n'etaient plus rognees ; espace superflu en haut des cartes du mode
agent ; pastilles d'etat redessinees et plaquees a droite ; moteurs renommes
en llama.cpp precompile / compile / personnalise ; badge conseille lisible en
sombre ; section Service fondue dans Moteur ; plus qu'un enregistrement
automatique pour l'acces internet.

Documentation : le README dit desormais que Crawl4AI est un serveur a
heberger soi-meme, avec la commande Docker.
2026-07-30 10:21:09 +02:00

99 lines
3.1 KiB
Go

package jean
import (
"encoding/json"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
)
// Préférences d'apparence de l'UI web (thème, mode d'affichage). Stockées côté
// serveur — sur la machine jean — pour être partagées entre tous les appareils
// qui pilotent la même instance (téléphone, laptop, accès distant ajean.link).
// Avant ça, elles ne vivaient qu'en localStorage, donc par navigateur/appareil.
// webPrefsPath est le fichier JSON des préférences d'apparence.
func webPrefsPath() string { return filepath.Join(JeanHome(), "webprefs.json") }
var webPrefsMu sync.Mutex
// webPrefsAllowed liste les clés de préférence acceptées et, pour chacune, les
// valeurs valides. On ne stocke que ce qui est connu (pas de champ libre).
var webPrefsAllowed = map[string]map[string]bool{
// Un seul thème, deux variantes. Les anciennes valeurs (ajean, gpt-dark…)
// ne sont plus acceptées : le client les normalise en clair/sombre au
// chargement puis réenregistre la valeur normalisée.
"theme": {"light": true, "dark": true},
"hide_reasoning": {"0": true, "1": true},
"hide_tools": {"0": true, "1": true},
"fold_tools": {"0": true, "1": true},
"hide_side": {"0": true, "1": true},
"hide_stats": {"0": true, "1": true},
}
// loadWebPrefs lit les préférences enregistrées (map vide si absent/illisible).
func loadWebPrefs() map[string]string {
out := map[string]string{}
b, err := os.ReadFile(webPrefsPath())
if err != nil {
return out
}
_ = json.Unmarshal(b, &out)
return out
}
// saveWebPrefs fusionne les valeurs valides de `in` dans le fichier et l'écrit.
func saveWebPrefs(in map[string]string) (map[string]string, error) {
webPrefsMu.Lock()
defer webPrefsMu.Unlock()
prefs := loadWebPrefs()
// Purge les clés/valeurs devenues invalides (anciens thèmes, réglage de
// police retiré) : sans ça, un fichier écrit par une version précédente
// renverrait indéfiniment des valeurs que le client ne sait plus traiter.
for k, v := range prefs {
if allowed, ok := webPrefsAllowed[k]; !ok || !allowed[v] {
delete(prefs, k)
}
}
for k, v := range in {
allowed, ok := webPrefsAllowed[k]
if !ok {
continue
}
v = strings.ToLower(strings.TrimSpace(v))
if !allowed[v] {
continue
}
prefs[k] = v
}
b, err := json.MarshalIndent(prefs, "", " ")
if err != nil {
return prefs, err
}
if err := os.WriteFile(webPrefsPath(), b, 0o600); err != nil {
return prefs, err
}
return prefs, nil
}
// handleWebPrefs expose les préférences d'apparence partagées entre appareils.
//
// GET → {ok, prefs:{theme, hide_*, fold_*}}
// POST {theme?, hide_*?, fold_*?} → fusionne puis renvoie {ok, prefs}
func handleWebPrefs(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
var req map[string]string
_ = json.NewDecoder(r.Body).Decode(&req)
prefs, err := saveWebPrefs(req)
if err != nil {
sendJSON(w, 500, map[string]any{"ok": false, "error": err.Error()})
return
}
sendJSON(w, 200, map[string]any{"ok": true, "prefs": prefs})
return
}
sendJSON(w, 200, map[string]any{"ok": true, "prefs": loadWebPrefs()})
}