123 lines
5.5 KiB
Markdown
123 lines
5.5 KiB
Markdown
# #115 / BUG-078 / #117 — Barre d'outils épinglée, coloration syntaxique des fichiers de code & avatars prédéfinis
|
|
|
|
> **Statut :** 🟢 livré (en attente vérification utilisateur)
|
|
> **Impact :** 🟡 (#115, BUG-078) · 🟢 (#117)
|
|
> **Zone :** frontend (`frontend/js/viewer.js`, `frontend/js/themes.js`,
|
|
> `frontend/js/ui.js`, `frontend/js/config.js`, `frontend/index.html`,
|
|
> `frontend/style.css`, `frontend/popout.html`, `frontend/locales/fr.json`,
|
|
> `frontend/locales/en.json`, `frontend/icons/avatar/*`)
|
|
|
|
Trois demandes traitées dans la même livraison, toutes côté frontend.
|
|
|
|
## #115 — Barre d'outils de lecture toujours visible
|
|
|
|
### Problème
|
|
|
|
La barre d'actions d'un document (pop-out, bookmark, Editer, Source, Copier, PDF, Export,
|
|
Partager…) était rendue **dans** `.file-header`, un conteneur court placé en haut de la
|
|
zone de lecture. Or un élément `position: sticky` reste borné par son parent : dès que
|
|
`.file-header` sortait de l'écran au défilement d'un document long, la barre disparaissait.
|
|
|
|
### Correctif
|
|
|
|
- `frontend/js/viewer.js` et `frontend/popout.html` sortent la barre d'actions de
|
|
`.file-header` et la placent dans un nouveau conteneur `.file-toolbar`, **enfant direct
|
|
de `.content-area`** (le conteneur de défilement). La barre peut donc se coller au haut
|
|
de la zone de lecture pour toute la hauteur du document.
|
|
- `frontend/style.css` :
|
|
|
|
```css
|
|
.file-toolbar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 30;
|
|
margin: 0 0 16px;
|
|
padding: 8px 0;
|
|
background: var(--bg-primary);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
```
|
|
|
|
Le fond est opaque pour qu'aucun texte ne transparaisse dessous.
|
|
- `body.reading-mode .file-toolbar { display: none }` : la barre reste masquée en mode
|
|
lecture, comme les autres actions.
|
|
|
|
## BUG-078 — Coloration syntaxique des fichiers de code
|
|
|
|
### Problème
|
|
|
|
Les fichiers `.py`, `.sh`, `.ps1`, `.yml`, `.json`… (et les blocs de code Markdown)
|
|
s'affichaient en **texte brut**, sans couleurs. Le code était pourtant correctement
|
|
généré côté backend (`<pre><code class="language-python">…`) et `safeHighlight()` appelait
|
|
bien `hljs.highlightElement()`.
|
|
|
|
La cause était ailleurs : les deux feuilles de style de highlight.js (`#hljs-theme-dark`
|
|
et `#hljs-theme-light`, chargées depuis le CDN dans `index.html`) étaient basculées à
|
|
partir de la **clé de thème** persistée (`obsigate-theme` = `defaut-obsigate`, …) :
|
|
|
|
```js
|
|
darkSheet.disabled = theme !== "dark"; // "defaut-obsigate" !== "dark" → true
|
|
lightSheet.disabled = theme !== "light"; // "defaut-obsigate" !== "light" → true
|
|
```
|
|
|
|
Les deux feuilles finissaient donc désactivées, privant tous les tokens de leurs couleurs.
|
|
Le résultat dépendait de l'ordre de deux initialisations concurrentes — `UI.initTheme()`
|
|
(clé de thème) au démarrage et `themes.initThemes()` (mode) via `Sync.init()` — d'où un
|
|
comportement **non déterministe** (parfois coloré, le plus souvent non).
|
|
|
|
### Correctif
|
|
|
|
- `frontend/js/themes.js` — `applyTheme(themeKey, mode)` bascule désormais les feuilles
|
|
highlight.js selon le **mode** :
|
|
|
|
```js
|
|
var isDark = mode === 'dark';
|
|
darkSheet.disabled = !isDark;
|
|
lightSheet.disabled = isDark;
|
|
```
|
|
|
|
(`sepia` et `high-contrast` réutilisent la palette claire.)
|
|
- `frontend/js/ui.js` — `initTheme()` lit le **mode** persisté (`obsigate-theme-mode`) et
|
|
`applyTheme()` résout un mode avant de fixer `data-theme` et de basculer les feuilles,
|
|
au lieu de comparer la clé de thème à `"dark"`/`"light"`.
|
|
|
|
Le basculement est ainsi **déterministe** dès le premier rendu et à chaque changement de
|
|
mode.
|
|
|
|
## #117 — Avatars prédéfinis dans le profil
|
|
|
|
### Ce qui a été livré
|
|
|
|
- **Galerie de 12 avatars** dans la section `Profil` (`#cfg-profile`), servis depuis
|
|
`frontend/icons/avatar/` (`/static/icons/avatar/<fichier>.jpg`) : Chat, chien, elephan,
|
|
hibou, koala, lapin, lion, ours, penda, pingouin, raton, tigre.
|
|
- Un clic charge l'image, la fait passer par le **même pipeline que l'import** (recadrage
|
|
carré central, redimensionnement 256 px, export JPEG 0,85) et l'enregistre via
|
|
`PATCH /api/auth/me` — aucune modification backend n'a été nécessaire, la validation
|
|
data-URL PNG/JPEG/WebP existante (BUG-113) s'applique telle quelle.
|
|
- L'avatar actif est **surligné** ; le choix est mémorisé dans `localStorage`
|
|
(`obsigate-avatar-preset`) et purgé dès qu'on importe une photo personnalisée ou qu'on
|
|
supprime l'avatar.
|
|
- L'import d'une photo et la suppression restent disponibles.
|
|
- i18n : nouvelle clé `config.avatar_presets_label` (FR/EN).
|
|
|
|
## Tests
|
|
|
|
- `tests/frontend/unit.test.mjs` — `syntax highlight theme` (BUG-078) : `themes.applyTheme`
|
|
bascule les feuilles selon le mode et `ui.js` ne compare plus la clé au mode.
|
|
- `tests/frontend/toolbar-order.test.mjs` — #115 : `.file-toolbar` dans `viewer.js` et
|
|
`popout.html`, règle CSS `position: sticky; top: 0`, masquage en mode lecture.
|
|
- `tests/frontend/settings-order-avatar.test.mjs` — #117 : 12 avatars présents dans
|
|
`#cfg-profile`, fichiers d'images existants, pipeline `config.js`, règles CSS, clé i18n
|
|
FR/EN.
|
|
- Vérification Playwright (instance locale, auth désactivée) : coloration déterministe sur
|
|
5 chargements successifs d'un `.py` ; `.file-toolbar` dont le `top` ne bouge plus après
|
|
défilement (épinglage effectif).
|
|
|
|
## Limitations
|
|
|
|
- L'avatar reste stocké en data-URL 256 px dans `data/users.json` (comportement #113
|
|
conservé).
|
|
- Le mode `high-contrast`/`sepia` utilise le thème highlight.js **clair** (pas de palette
|
|
dédiée).
|