mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-16 19:59:56 +02:00
Interface - Un seul theme, en deux variantes claire et sombre. Les themes Classique, Doux, ChatGPT et AJEAN sont retires, ainsi que le reglage de police et ses quatre variantes (plus aucun telechargement Google Fonts). - La disposition en colonne centree devient celle de l'application : reponses sans bulle, question en pastille a droite, composer en pilule. - Menu lateral refondu sur un gabarit unique repris des listes de la modale de preset : cartes, lignes de 34 px, intitule a gauche et controle a droite. Menu replie = 462 px, plus de defilement au chargement. - GPU et RAM fusionnes en une section Machine, avec un vrai bloc de statistique (nom, valeur alignee, jauge, detail). - Palette sombre plus contrastee : gris de texte a 7,6:1 (contre 5,6), bordures et surfaces enfin distinctes du fond. - Le logo affiche toujours AJEAN (le A n'apparaissait qu'en mode agent) et passe en couleur d'accent quand l'agent est actif. - Les garde-fous du serveur ([stop: ...]) s'affichent en encart discret. Corrections - Fond d'accent + texte blanc en dur : illisible en mode sombre (bouton enregistrer de l'editeur de preset, boutons accoles, bulle utilisateur). - Pave des interrupteurs invisible sur piste claire, piste trop proche du fond. - Valeurs cle/valeur inversees (« Go 24 / 18,2 ») : direction:rtl sans unicode-bidi:plaintext. - Jauge de VRAM invisible (fond de la jauge = couleur de sa carte). - Badges d'etat flottants dans les en-tetes (deux margin-left:auto). - Ombres et voiles calibres pour un fond sombre, trop lourds sur du blanc. Preferences - /api/prefs n'accepte plus que theme=light|dark et les quatre drapeaux d'affichage ; les cles obsoletes (police, anciens themes) sont purgees a la premiere ecriture, et le client normalise un theme herite.
98 lines
3.1 KiB
Go
98 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},
|
|
}
|
|
|
|
// 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()})
|
|
}
|