18 Commits

Author SHA1 Message Date
33087cc5b3 Make list pages fully dynamic; cache only at query level
Docker production build runs without DATABASE_URL, so any page Next
tries to prerender at build (force-static / revalidate without a
dynamic segment) fails on the Prisma call. Homepage, genre page, and
sitemap previously had page-level revalidate, forcing prerender. Add
force-dynamic to each and move the revalidation inside an
unstable_cache wrapper around the Prisma query — result still cached
5m / 1h at runtime, but build no longer touches the DB.

Detail page (/manga/[slug]) keeps page-level revalidate since its
dynamic segment without generateStaticParams was never prerendered at
build anyway.

Update CLAUDE.md caching section to reflect the new strategy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:56:26 +08:00
90f8f50166 Enable ISR on list pages; cache reader + search DB queries
Stable image URLs (post-signing removal) make page-level caching
safe again. Homepage, genre page, sitemap, and detail page now
revalidate on an interval instead of running Prisma on every hit.
Reader and search keep dynamic rendering (searchParams) but wrap
their Prisma queries in unstable_cache.

TTLs: home/genre/detail 5m, reader manga 5m, reader page meta 1h
(immutable), search 1m, sitemap 1h.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:12:51 +08:00
dea57e6b28 Drop R2 URL signing; serve images via custom domain
Images now load direct from images.04080616.xyz. Removes read-side
signing (signUrl/signCoverUrls + callers), unlocking browser and
edge caching since URLs are stable. Presigned upload kept for /api/upload.
PageReader retries failed loads via onError as a safety net.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 19:29:11 +08:00
9681dddc2e Fix iOS Safari zoom-in on lock/unlock
Adds a tiny client component mounted in the root layout that listens for
pageshow with persisted=true (bfcache resume) and nudges scroll + forces
a reflow. Resolves the zoom-desync bug where viewportFit=cover +
maximumScale=1 leaves the visual viewport stale after device lock.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:11:59 +08:00
b993de43bc Reader: fix resume bug, add loading skeleton, scraping protection, bounded image cache
- Resume scroll position only when arriving via 继续阅读 (?resume=1).
  Plain chapter-list / drawer clicks now actively scroll to top on mount.
- Progress format extended to {chapter, page, ratio} for within-page
  precision; legacy bare-number and {chapter, page} still read correctly.
- Tappable skeleton logo (sunflower outline, spins) while a page loads;
  tap force-fetches a fresh signed URL.
- Viewport-priority image loading: second IntersectionObserver at margin 0
  marks truly-visible pages, drives <img fetchpriority="high"> and fires
  immediate single-page fetches that cut the batch queue.
- Bounded image cache: unmount previous chapter's <img> elements when
  currentPage > 5 into the new chapter; placeholders stay for layout.
  One AbortController per live chapter; unmount aborts in-flight batches.
- Hashed chapter IDs on the wire via hashids; DB PKs unchanged.
- Origin/Referer allowlist + rate limiting on all /api/* routes via a
  withGuards(opts, handler) wrapper (eliminates 6-line boilerplate x5).
- robots.txt allows Googlebot/Bingbot/Slurp/DuckDuckBot/Baiduspider/
  YandexBot only; disallows /api/ for all UAs.
- Extract pure helpers for future TDD: lib/scroll-ratio.ts (calcScrollRatio,
  scrollOffsetFromRatio), lib/progress.ts (parseProgress + injectable
  StorageLike), lib/rate-limit.ts (optional { now, store, ipOf } deps),
  lib/api-guards.ts.
- New env keys: HASHIDS_SALT, ALLOWED_ORIGINS (wired into docker-compose).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:48:15 +08:00
6c750e70ce Update site msg 2026-04-12 15:51:57 +08:00
3e4b87329a Added logo and changed theme color from light blue to green 2026-04-12 15:45:46 +08:00
1c74348fae Sync URL and prev/next nav with the chapter being viewed
Once continuous scroll crosses a chapter boundary, the URL was stuck at
the originally-opened chapter so browser back / reload would jump the
user back there. Double-tap left/right also walked off the wrong chapter
since prevChapter/nextChapter were frozen at mount time.

- replaceState the URL as currentChapterNum changes (no server refetch).
- Derive prevChapter/nextChapter dynamically via useMemo on
  currentChapterNum, dropping the now-redundant server-computed props.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:40:13 +08:00
43a2a6d3f8 Rewrite reader around known-dimension page placeholders
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>
2026-04-12 13:01:41 +08:00
26b620de2f Add reading-progress resume + multi-genre support
Reading progress
- New ReadingProgressButton client component that reads/writes
  localStorage key "sunnymh:last-read:{slug}"
- PageReader writes the currently-visible chapter as the user scrolls
  through continuous chapters
- Manga detail CTA now reads: "开始阅读" on first visit, or
  "继续阅读 · #{N} {title}" when a prior chapter is stored (with
  spacing and truncation for long titles)

Multiple genres
- lib/genres.ts with parseGenres() and collectGenres() helpers to
  split "冒险, 恋爱, 魔幻" into a list and aggregate across a collection
- Manga detail renders one pill per genre
- GenreTabs filters via parseGenres(...).includes(activeGenre) so a
  multi-genre manga appears under each of its genres
- Home / Genre pages compute the tab list from collectGenres(signedManga)
- Card captions (GenreTabs + TrendingCarousel) show "冒险 · 恋爱 · 魔幻"
  with truncation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:27:28 +08:00
06dcf0a649 Rewrite reader for iOS Safari with continuous multi-chapter flow
Replaces the fixed-position reader with a sticky layout that works
correctly on iPhone Safari and Edge, while also auto-appending the
next chapter's pages when the current one finishes.

Layout
- Swap all position:fixed for sticky (Header, BottomNav, reader top nav)
  — fixed-positioning quirks broke the bottom nav in Edge and
  prevented Safari's URL bar from collapsing on scroll
- Viewport: viewport-fit=cover + interactiveWidget=overlays-content
  so manga extends edge-to-edge and the URL bar overlays content
  without resizing the viewport
- Add pt-safe / pb-safe utilities; apply on nav bars so chrome
  respects the notch and home-indicator
- Drop fixed-positioning bottom padding now that BottomNav is in flow

Continuous reading
- PageReader now receives the full chapter manifest (id + totalPages)
  and auto-fetches the next chapter when the current one is done
- Subtle chapter divider strip appears between chapters in the scroll
- Top nav chapter title updates as the user scrolls into a new chapter
  (rAF-throttled scroll listener, cached offsetTop)
- Double-tap on left/right viewport half navigates prev/next chapter
- End-of-manga footer fills the viewport with a Back-to-Manga action

Theme polish
- Light theme: white body/background, blue accent preserved for
  chapter numbers, badges, active states
- Modern chapter drawer: white sheet, rounded-t-3xl, two-column
  rows with chapter-number badge, blue highlight for current chapter
- Suppress hydration warnings for extension-injected attributes on
  <html> and the search input
- Manga detail CTA localized to 开始阅读

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:17:54 +08:00
57255e2624 Add signed R2 URLs, batched page fetching, and 3D chapter badges
- Sign all image URLs server-side with 60s expiry presigned URLs
- Add /api/pages endpoint for batched page fetching (7 per batch)
- PageReader prefetches next batch when user scrolls to 3rd page
- Move chapter count badge outside overflow-hidden for 3D effect
- Fix missing URL signing on search and genre pages
- Extract signCoverUrls helper to reduce duplication
- Clamp API limit param to prevent abuse

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:15:01 +08:00
e7dc39738c Add auto-hide nav bars on scroll and chapter selector bottom sheet
Nav bars hide when scrolling down and reappear on scroll up.
Replace static page count with a chapter picker that opens as a
bottom sheet drawer for quick chapter navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:38:54 +08:00
6257f6c79d Switch domain from manga.04080616.xyz to www.04080616.xyz
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:48:07 +08:00
09fb507e05 Second Commit 2026-04-11 16:59:44 +08:00
f1c649429e First Commit 2026-03-25 22:20:37 +08:00
9bd75381a7 Add hello world landing page and Docker deployment config
Simple landing page for initial Portainer GitOps deployment.
Includes Dockerfile, docker-compose.yml with pull_policy: build,
and project documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 22:37:08 +08:00
c7374493c5 Initial commit from Create Next App 2026-03-24 22:20:52 +08:00