4.7 KiB
#113 — Ordre naturel des sections Configurations & avatar utilisateur
Version livrée : 2.22.0 · Statut : 🟢 · Impact : 🟡 Zone : frontend (
index.html,frontend/js/config.js,frontend/js/auth.js,frontend/style.css, i18n FR/EN) + backend (backend/auth/router.py,backend/auth/user_store.py) + CI (.gitea/workflows/ci.yml).
Contexte
Deux irritants sur la page Configurations :
- l'ordre des sections était historique et peu naturel (Recherche en tête, Profil noyé en position 11, À propos au milieu) ;
- la section Profil ne permettait pas de personnaliser l'image affichée dans le cercle du compte en bas de la sidebar (initiales uniquement).
Ce qui a été livré
A. Ordre naturel des sections (TOC = page)
Nouvel ordre, appliqué à la liste <ul class="help-nav-list"> (TOC) et aux
<section> de la page, dans le même ordre :
- Profil (
#cfg-profile) — en premier - Sécurité du compte (
#cfg-security) - Thèmes (
#cfg-themes) - Paramètres de recherche (
#cfg-search) - Historique récent (
#cfg-recent) - Filtrage de tags (
#cfg-tags) - Fichiers cachés (
#cfg-hidden-files) - Synchronisation (
#cfg-sync) - Paramètres backend (
#cfg-backend-settings) - Diagnostics (
#cfg-diags) - Clés API IA (
#cfg-ai) - Sources connectées (
#cfg-sources) - Clés API & MCP (
#cfg-tokens) - Notifications push (
#cfg-push) - Webhooks (
#cfg-webhooks) - Partages publics (
#cfg-partages-publics) - Plugins (
#cfg-plugins) - À propos (
#cfg-about) — en dernier
Regroupement retenu : Compte & apparence → Navigation & contenu → Système → Intégrations & notifications → À propos.
Les ancres cfg-tags et cfg-partages-publics étaient posées sur un <h2> à
l'intérieur d'une <section> sans id : elles sont désormais portées par la
<section> elle-même, pour que le scroll vise le haut de la section (comme toutes
les autres).
B. Avatar utilisateur ( Profil )
- Import d'image : bouton « Choisir une image » + overlay caméra au survol de
l'aperçu circulaire (88 px) →
<input type="file" accept="image/png,image/jpeg,image/webp">. - Traitement client (
config.js) : garde type (PNG/JPEG/WEBP) et taille brute (8 Mo), recadrage carré central et redimensionnement à 256 px via canvas, export JPEG qualitée 0,85 (fond blanc pour les PNG transparents). - Persistance serveur :
PATCH /api/auth/meavec{"avatar": "<data-url>"};""supprime. Validation stricte dansbackend/auth/router.py(_validate_avatar) : data-URL PNG/JPEG/WebP uniquement (SVG refusé — surface XSS), plafond 400 000 caractères, base64 valide et octets magiques contrôlés. Champavatarajouté àdata/users.json(create_user), renvoyé parGET/PATCH /api/auth/meet par le payloaduserde login. - Affichage sidebar (
auth.js) :renderUserSection()insère un<img class="sidebar-user-avatar-img">dans#sidebar-user-avatarquanduser.avatarest défini, sinon les initiales (repli inchangé). HelpersAuthManager.updateCachedUser()etAuthManager.isAuthEnabled(). - Suppression : bouton « Supprimer la photo » (stylisté danger), revenu aux initiales.
- Auth désactivée : le bloc avatar est masqué (pas de compte, sidebar masquée).
- Feedback : toasts
config.avatar_updated/config.avatar_removed, erreurs en ligne (config.avatar_invalid_type,config.avatar_too_large,config.avatar_upload_failed).
Tests
tests/test_auth_api.py— classeTestAvatar(+8) : GET/PATCH exposent l'avatar, data-URL PNG acceptée,""efface, SVG refusé, payload non-image refusé, trop grand refusé, base64 invalide refusé, avatar présent dans le payload de login.tests/frontend/settings-order-avatar.test.mjs(nouveau, ajouté au joblint) — 9 tests : ordre TOC (Profil 1er, À propos dernier), ordre de page strictement identique à la TOC, aucune ancre morte / section orpheline, présence de l'UI avatar dans#cfg-profile, fluxconfig.js(types, taille, resize, PATCH, rafraîchissement sidebar), renduauth.js, règles CSS, clés i18n FR/EN, validation backend.- Vérifications locales : pytest 1302 passed, ruff/mypy 0 erreur, tests frontend statiques + JSDOM verts.
Limitations connues
- L'avatar est stocké en data-URL dans
data/users.json(adéquat pour un usage personnel ; un stockage fichier dédié restera possible si les comptes se multiplient). - La conversion GIF/animé n'est pas prise en charge (types PNG/JPEG/WEBP uniquement).
- Le nom d'affichage du profil reste local (
localStorage) et n'est pas poussé au serveur (comportement antérieur conservé).