feat: preload the whole current chapter in continuous mode

Eager-load every page of the current chapter rather than a bounded
rolling window, so pages are fetched up front (the browser paces them
over its per-origin connection limit, top-to-bottom = reading order)
instead of filling in as they're scrolled onto. This drops the
scrollLeadIdx/eagerThrough machinery in favour of a plain
`loading="eager"`.

The first few pages of the *next* chapter are already warmed when the
reader nears the end of the current one (the next-chapter preloader), so
flipping over stays instant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-07 19:47:48 +02:00
parent c69ff502f7
commit 987d1ba235
6 changed files with 34 additions and 98 deletions

View File

@@ -170,13 +170,10 @@ test.describe('reader ?page=N deep link', () => {
await expect(page.getByTestId('reader-continuous')).toBeVisible();
// Pages 1..=4 (1-indexed in the testid, 0-indexed in the
// template; initialIndex = 3 means we eager-load at least 0..=3,
// i.e. testids 1..4). Without this guard, page 2+ would be lazy
// and their 0×0 placeholders would let the scroll target
// appear far above its final position. The rolling preload window
// (PRELOAD_AHEAD) extends the eager set a few pages past the
// target, so on this 6-page chapter every page is eager.
// The whole chapter is eager, so the scroll target (page 4) and
// every page before it have settled heights before the
// scroll-to-`?page=N` effect runs — the target can't be pushed
// past the viewport by later pages loading in.
for (const n of [1, 2, 3, 4, 5, 6]) {
await expect(page.getByTestId(`reader-page-${n}`)).toHaveAttribute(
'loading',
@@ -185,28 +182,21 @@ test.describe('reader ?page=N deep link', () => {
}
});
test('continuous mode: no ?page= eager-loads a leading preload window, not the whole chapter', async ({
test('continuous mode: eager-loads the whole chapter up front', async ({
page
}) => {
await mockReader(page, 'continuous');
await page.goto(`/manga/${mangaId}/chapter/${chapterId}`);
await expect(page.getByTestId('reader-continuous')).toBeVisible();
// initialIndex = 0; the rolling window (PRELOAD_AHEAD = 3) eager-loads
// the first few pages ahead of the reader so they don't pop in on
// scroll ...
for (const n of [1, 2, 3]) {
// Every page is eager — the whole current chapter is preloaded so
// pages don't pop in / reflow as the reader scrolls onto them.
for (const n of [1, 2, 3, 4, 5, 6]) {
await expect(page.getByTestId(`reader-page-${n}`)).toHaveAttribute(
'loading',
'eager'
);
}
// ... while the tail stays lazy so a long chapter doesn't fetch every
// page at once on open.
await expect(page.getByTestId('reader-page-6')).toHaveAttribute(
'loading',
'lazy'
);
});
test('single mode: ?page=N opens at the requested page', async ({ page }) => {