4.0 KiB
4.0 KiB
#111 — Visionneuse d'images : navigation fluide
Version livrée : 2.20.0 · Statut : 🟢 · Impact : 🟡 Zone : frontend (
frontend/js/viewer.js,frontend/style.css, i18n FR/EN). Améliore : #108 (visionneuse livrée en 2.17.0).
Contexte
La visionneuse d'images de #108 fonctionnait mais restait perfectible sur l'usage quotidien :
- certaines images s'affichaient plus grandes que le cadre de présentation ;
- changer d'image (←/→, flèches, vignette) appelait
openFile→renderFile→renderImageViewer, soit un rechargement complet de la vue et un nouvel appel/api/browseà chaque image — sensible dès qu'un dossier en contient beaucoup ; - la pellicule de miniatures disparaissait le temps du re-rendu ;
- aucune zone de clic latérale ne permettait de changer d'image.
Ce qui a été livré
A. Ajustement au cadre
- La zone de contenu devient un conteneur flex dédié à la visionneuse
(
.content-area:has(> .image-viewer-container) { padding: 0; overflow: hidden }) : plus de défilement de page, la visionneuse occupe tout l'espace disponible. .image-stagegagnemin-height: 0, unpaddingde 16 px (cadre) etbox-sizing: border-box;.image-mainconservemax-width/height: 100%avecwidth/height: auto. L'image est donc toujours redimensionnée dans l'espace disponible, y compris quand le panneau « Métadonnées » s'ouvre (la scène se réduit, l'image suit).
B. Navigation « en place » (performance)
renderImageViewerne se contente plus de rendre une image : il gère un état mutable (currentPath,currentTitle,imgUrl,currentMeta) et exposeshowSibling(index)qui remplace lesrcdu<img>sans reconstruire le DOM. Les flèches, le clavier ←/→ et les miniatures passent tous par là.- Conséquences : plus de
openFile, plus de re-rendu, plus de refetch du fichier ni de/api/browseà chaque image. - Cache annuaire
_imageDirCache(Map, TTL 15 s) : la liste des images d'un dossier n'est récupérée qu'une fois par courte fenêtre. - Préchargement des images voisines (
new Image()), avec unSetpour éviter les doublons.
C. Pellicule persistante
- La pellicule de miniatures n'est plus recréée à chaque navigation ; la
vignette active est simplement re-marquée (
.active) et amenée dans la vue par défilement horizontal du film uniquement (jamais la page). - Miniatures en
loading="lazy"+decoding="async".
D. Flèches latérales translucides
- Deux boutons superposés
.image-nav-arrow(prev/next) longent les bords du cadre, avec une icônechevronet une ombre portée pour rester lisibles sur toute image. - Opacité quasi nulle au repos, révélée au survol du cadre (
0.4) puis du bouton (1, avec dégradé sombre). Toujours visibles (opacité moyenne) sur les appareils tactiles (@media (hover: none)). - Le
pointerdown/dblclickdes flèches n'est pas propagé à la scène : le pan (glisser) et le double-clic de réinitialisation du zoom restent intacts. - Un compteur
n / totalest ajouté à la barre d'outils.
Tests
tests/frontend/image-viewer.test.mjs: helpers purs inchangés + vérifications statiques de la navigation en place (showSibling, absence de_imageViewerNavPending, cache annuaire), des flèches et du CSS (:has(> .image-viewer-container),max-width/height,opacity).tests/e2e/image-viewer.spec.js(+1) : image contenue dans le cadre, flèche superposée révélée au survol, titre mis à jour sans recréer le conteneur (marqueurdata-inplace), pellicule toujours visible et compteur affiché.
Limitations connues
- Le cache annuaire a un TTL court : une image ajoutée puis ouverte dans les quelques secondes peut ne pas apparaître tout de suite dans la pellicule.
- Les flèches latérales n'apparaissent pas en mode lightbox plein écran (navigation clavier ←/→ et pellicule masquée conservées).