Files
Loki/internal/jean/web_prefs.go
T
nathaninline e2475c6688 v0.5.10 : le produit s'appelle AJEAN + diff des ecritures dans le chat + variante AJEAN sombre
- renommage : « Jean » devient « AJEAN » dans tous les textes visibles (titre de
  l'UI, proprietes du binaire Windows, barre de menus macOS / zone de
  notification, messages du terminal, README). La commande, les services, les
  chemins et le depot ne changent pas.
- nouveau thème « AJEAN sombre » (même habillage pixel, palette nuit) ; les
  regles de mise en page sont partagees via [data-theme^="ajean"].
- l'IA montre ce qu'elle ecrit : les outils edit / mem_add / mem_edit renvoient
  un diff ligne a ligne, affiche en vert (+) et rouge (-) dans la bulle d'outil
  et persiste avec la conversation (chat_diff.go).
- ecritures idempotentes : si la modification est deja en place, edit et
  mem_edit repondent « deja a jour » au lieu d'une erreur trompeuse ; un appel
  d'outil rigoureusement identique n'est plus rejoue dans le meme tour.
- saisie du chat auto-agrandissante (une ligne au repos en thème ChatGPT, deux
  ailleurs), bouton d'envoi enfin aligne, astuce clavier detachee de la carte.
- editeur de preset relu pour le thème AJEAN (plus de double encadrement), champ
  + bouton de telechargement recolles dans tous les thèmes, interrupteurs carres.
- emojis retires de l'interface (les glyphes monochromes restent).
2026-07-28 10:20:19 +02:00

88 lines
2.8 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{
// ⚠️ Tout thème ajouté à l'UI (registre THEMES, js/01-theme.js) doit être
// listé ICI : sinon POST /api/prefs le rejette silencieusement, le serveur
// conserve l'ancienne valeur et loadPrefs() la réapplique à chaque
// rafraîchissement — le thème « saute » alors qu'il est bien en localStorage.
"theme": {"ajean": true, "ajean-dark": true, "dark": true, "light": true, "soft": true, "soft-dark": true, "gpt": true},
"display": {"full": true, "simple": 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()
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,display}}
// POST {theme?, display?} → 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()})
}