Replaces the prepend/flushSync/scrollBy gymnastics with placeholder divs
sized by each page's width/height. Document height is correct from the
first paint, so resume + backward scroll just work — no scroll
compensation, no gesture fights, no forced aspect ratio distorting images.
- New /api/chapters/[id]/meta returns the dim skeleton for any chapter.
- Chapter page pre-fetches the starting chapter's meta server-side and
parallelizes the two Prisma queries via Promise.all.
- Reader renders placeholders with aspectRatio: w/h, lazy-loads image
URLs in batches via IntersectionObserver, and prefetches the next
chapter's meta ~3 pages from the end.
- Scroll tracker walks only the intersecting-pages set (~3–5 elements)
instead of every loaded page per rAF.
- scroll={false} on all Links into the reader + { scroll: false } on
double-tap router.push, plus a belt-and-suspenders rAF re-scroll, so
resume survives soft navigation and browser scroll-restoration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20 lines
550 B
TypeScript
20 lines
550 B
TypeScript
import { prisma } from "@/lib/db";
|
|
|
|
type Params = { params: Promise<{ chapterId: string }> };
|
|
|
|
export async function GET(_request: Request, { params }: Params) {
|
|
const { chapterId: raw } = await params;
|
|
const chapterId = parseInt(raw, 10);
|
|
if (isNaN(chapterId)) {
|
|
return Response.json({ error: "Invalid chapterId" }, { status: 400 });
|
|
}
|
|
|
|
const pages = await prisma.page.findMany({
|
|
where: { chapterId },
|
|
orderBy: { number: "asc" },
|
|
select: { number: true, width: true, height: true },
|
|
});
|
|
|
|
return Response.json(pages);
|
|
}
|