Files
ObsiGate/docs/features/image-navigation-111.md
T
bruno b926f01b85
CI / lint (push) Successful in 2m2s
CI / security (push) Successful in 1m23s
CI / test (push) Successful in 4m23s
CI / build (push) Successful in 1m19s
CI / e2e (push) Successful in 13m27s
feat: visionneuse d'images - navigation fluide, image ajustee au cadre, fleches laterales et pellicule persistante #111
2026-09-23 14:37:41 -04:00

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-stage gagne min-height: 0, un padding de 16 px (cadre) et box-sizing: border-box ; .image-main conserve max-width/height: 100% avec width/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)

  • renderImageViewer ne se contente plus de rendre une image : il gère un état mutable (currentPath, currentTitle, imgUrl, currentMeta) et expose showSibling(index) qui remplace le src du <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 un Set pour é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ône chevron et 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/dblclick des 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 / total est 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 (marqueur data-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).