IO with a shadow-host root never delivered intersections in Chromium
(verified live: pages stayed blank while the section.load→fetch→img
pipeline itself worked fine when probed from outside). Loading is now
driven from the rAF-throttled scroll handler that already tracked the
reading position: a window of [index-2, index+4] loads around the
viewport-center page, with a 'loading' guard against duplicate
in-flight loads. connectedCallback kicks the initial load window.
New <foliate-webtoon> custom element: every page stacked in one
scrolling column (max-width 900px centered on wide screens), the
standard long-strip manga reading mode.
- Pages lazy-load through an IntersectionObserver with a 150% root
margin so they're ready before scrolling into view; placeholders
carry an estimated min-height until the image loads, then the real
aspect-ratio — set on the placeholder element — so geometry stays
stable across unloads and the scrollbar never jumps.
- Pages more than 12 positions from the reading point are unloaded
(image removed, section blob URL revoked) to bound memory on long
webtoons; they simply reload if the user scrolls back.
- Relocate events ({reason:'scroll', index, fraction, size:1}) fire on
rAF-throttled scroll with the viewport-center page as index and the
visible position within it as fraction — compatible with view.js's
SectionProgress enrichment, so progress saving/restoring, the
slider, and the back-stack all work unchanged.
- Renderer contract mirrors fixed-layout: goTo(await {index}) scrolls
the page into view; next/prev page by ~one screen (view.init()'s
fresh-start probe is a no-op that reports location); goLeft/goRight
map to prev/next (vertical flow is direction-independent).
No zoom/magnifier APIs — the host already gates those on the flow.
- Page images honor --fx-filter from the host for the new
brightness/contrast/invert controls.
view.js picks the renderer when book.format === 'comic' (now tagged
by makeComicBook) and options.comic.flow === 'webtoon'.