Les IntersectionObserver utilisaient root: null (viewport) au lieu du
conteneur de scroll réel (.infinite-reader). Le rootMargin de 1000px
était donc calculé par rapport au viewport, causant un montage/démontage
des pages à des moments imprécis et des sauts de layout lors du scroll
vers le haut.
Supprime également scroll-behavior: smooth sur le conteneur, qui entrait
en conflit avec le scroll anchoring du navigateur lors des corrections de
position, donnant l'impression que le scroll redescendait tout seul.
Remplace le rendu de tous les composants ReaderPage par un système de
virtual rendering : seules les pages dans la zone ±1000px du viewport
sont montées, les autres sont remplacées par un placeholder dimensionné.
- InfiniteReader : ajout visibilityObserver + mountedPageIndices (Set
réactif), helper getPlaceholderHeight(), suppression de 5 console.log
- ReaderPage : prop windowWidth injectable depuis le parent, listener
resize conditionnel, suppression de 3 console.log de debug
- Windowing côté rendu : seules les pages dans une fenêtre de ±3 autour
de la page visible sont montées en tant que ReaderPage ; les autres
sont remplacées par des placeholders dimensionnés via aspect-ratio CSS
pour maintenir la hauteur de scroll sans saut
- IntersectionObserver utilise le minimum des indices intersectants pour
éviter que les entrées simultanées au chargement ne décalent la fenêtre
- Prop initialPage passé depuis ChapterReader pour ancrer la fenêtre sur
la page courante dès le montage
- loading="eager" sur les ReaderPage montés (le windowing est le mécanisme
de lazy-loading, pas l'attribut HTML natif)
- Prop loading bindé sur les 3 balises <img> de ReaderPage
- Corriger la troncature de la toolbar (max-height 4rem → 5rem)
- Animer la toolbar en translateY pour un effet "bloc uni" avec le header
- Corriger le bug d'auto-hide du header après switch simple → scroll
- Augmenter la taille du titre de chapitre dans la toolbar (text-sm font-medium)
- Harmoniser le bouton scroll-to-top avec le style des ToolbarButtons
- Ajouter support de prop `class` sur les labels de ToolbarSection
Replace the per-page API call (base64 payload) with static image URLs
served directly by Caddy from public/images/pages/{chapterId}/.
- LocalImageStorage now stores to public/images/ (was MANGA_DATA_PATH)
- LegacyChapterRepository returns /images/pages/{id}/{file} URLs,
uses getimagesize() instead of loading file content into memory
- Delete GetChapterPage query/handler/response, ChapterPageResource,
ChapterPageProvider, PageContent model
- Remove getPageContent() from ChapterRepositoryInterface
- Frontend: loadChapter() fetches chapter + all pages in parallel,
ReaderPage uses URL instead of base64 data URI, InfiniteReader drops
lazy-load observer side effect, readerStore drops loadedPages/preload
- GetChapterPagesTest: extract fixture images from CBZ at runtime,
ignore tests/Fixtures/pages/ in .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>