sunnymh-manga-site/app/layout.tsx
yiekheng 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

59 lines
1.4 KiB
TypeScript

import type { Metadata, Viewport } from "next";
import { Geist } from "next/font/google";
import { Header } from "@/components/Header";
import { BottomNav } from "@/components/BottomNav";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: {
default: "SunnyMH — Free Manga Reader",
template: "%s | SunnyMH",
},
description:
"Read free public domain manga online. Beautiful reader optimized for mobile.",
metadataBase: new URL("https://www.04080616.xyz"),
openGraph: {
type: "website",
siteName: "SunnyMH",
},
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 1,
viewportFit: "cover",
interactiveWidget: "overlays-content",
colorScheme: "light",
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
{ media: "(prefers-color-scheme: dark)", color: "#ffffff" },
],
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${geistSans.variable} antialiased`}
data-scroll-behavior="smooth"
suppressHydrationWarning
>
<body className="min-h-dvh flex flex-col bg-background text-foreground">
<Header />
<main className="flex-1 bg-background">{children}</main>
<BottomNav />
</body>
</html>
);
}