diff --git a/next.config.mjs b/next.config.mjs index ac46786..d730e0c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,33 +1,34 @@ -import createMDX from '@next/mdx' -import bundleAnalyzer from '@next/bundle-analyzer' +import createMDX from "@next/mdx"; +import bundleAnalyzer from "@next/bundle-analyzer"; /** @type {import('next').NextConfig} */ const nextConfig = { - output: 'export', + output: "export", reactStrictMode: true, compress: true, poweredByHeader: false, optimizeFonts: true, swcMinify: true, images: { - unoptimized: true + unoptimized: true, }, - pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], + pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"], webpack: (config, { isServer }) => { if (!isServer) { config.externals = { // 添加其他需要从 CDN 加载的依赖 - } + }; } - return config + return config; }, + // experimental: { turbo: {} }, }; -const withMDX = createMDX({}) +const withMDX = createMDX({}); // 修改 bundle-analyzer 配置 const withBundleAnalyzer = bundleAnalyzer({ - enabled: process.env.ANALYZE === 'true', -}) + enabled: process.env.ANALYZE === "true", +}); -export default withBundleAnalyzer(withMDX(nextConfig)); \ No newline at end of file +export default withBundleAnalyzer(withMDX(nextConfig)); diff --git a/package.json b/package.json index 8136e06..23b1929 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "my-app", + "name": "next-blog", "version": "0.1.0", "private": true, "scripts": { @@ -8,7 +8,6 @@ "analyze": "pnpm clean && ANALYZE=true next build", "start": "next start", "lint": "next lint", - "export": "next export", "clean": "rm -rf .next" }, "dependencies": { diff --git a/public/svg/check-dark.svg b/public/svg/check-dark.svg index d4c5fa8..5b00714 100644 --- a/public/svg/check-dark.svg +++ b/public/svg/check-dark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/svg/copy-dark.svg b/public/svg/copy-dark.svg index f0c4615..ee08e0f 100644 --- a/public/svg/copy-dark.svg +++ b/public/svg/copy-dark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/blog/BlogClientComponent.tsx b/src/app/blog/BlogClientComponent.tsx index bca9bcb..f7608f3 100644 --- a/src/app/blog/BlogClientComponent.tsx +++ b/src/app/blog/BlogClientComponent.tsx @@ -24,8 +24,8 @@ import { SelectTrigger, SelectValue, } from "@/components/ui"; -import { ChevronDown, ChevronUp } from "lucide-react"; -import { AlertCircle } from "lucide-react"; +import { ChevronDown, ChevronUp, AlertCircle } from "lucide-react"; +import { motion, AnimatePresence } from "framer-motion"; const POSTS_PER_PAGE = 6; const INITIAL_TAG_COUNT = 10; @@ -33,6 +33,12 @@ const INITIAL_TAG_COUNT = 10; // 添加新的排序选项类型 type SortOption = "date" | "title"; +const cardVariants = { + hidden: { opacity: 0, y: -20 }, + visible: { opacity: 1, y: 0 }, + exit: { opacity: 0, y: 20 }, +}; + export default function BlogList() { const router = useRouter(); const searchParams = useSearchParams(); @@ -218,35 +224,52 @@ export default function BlogList() { )) ) : currentPosts.length > 0 ? ( - currentPosts.map((post) => ( - - - - - {post.title} - - - - {post.date} | {post.author} - - - -

{post.excerpt}

-
- - {post.tags.map((tag) => ( - handleTagClick(tag)} - > - {tag} - - ))} - -
- )) + + {currentPosts.map((post, index) => ( + + + + + + {post.title} + + + + {post.date} | {post.author} + + + +

+ {post.excerpt} +

+
+ + {post.tags.map((tag) => ( + handleTagClick(tag)} + > + {tag} + + ))} + +
+
+ ))} +
) : (
@@ -266,21 +289,29 @@ export default function BlogList() { )}
{filteredPostsMemo.length > 0 && ( -
- {Array.from( - { length: Math.ceil(filteredPostsMemo.length / POSTS_PER_PAGE) }, - (_, i) => ( - - ) - )} -
+ +
+ {Array.from( + { length: Math.ceil(filteredPostsMemo.length / POSTS_PER_PAGE) }, + (_, i) => ( + + ) + )} +
+
)} ); diff --git a/src/app/blog/[id]/AnimatedBlogPost.tsx b/src/app/blog/[id]/AnimatedBlogPost.tsx new file mode 100644 index 0000000..be4b406 --- /dev/null +++ b/src/app/blog/[id]/AnimatedBlogPost.tsx @@ -0,0 +1,60 @@ +'use client'; + +import { motion } from 'framer-motion'; +import BlogHeader from "./BlogHeader"; +import BlogContent from "./BlogContent"; +import BlogFooter from "./BlogFooter"; +import RelatedPosts from "./RelatedPosts"; +import ShareButtons from "./ShareButtons"; +import TableOfContents from "./TableOfContents"; +import Comments from "@/components/Comments"; +import { mdxComponents } from "./MdxComponents"; +import type { BlogPost, Heading } from "./types"; + +interface AnimatedBlogPostProps { + post: BlogPost; + headings: Heading[]; + readingTime: number; + relatedPosts: BlogPost[]; +} + +export default function AnimatedBlogPost({ post, headings, readingTime, relatedPosts }: AnimatedBlogPostProps) { + return ( + +
+ + + + + + + +
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/blog/[id]/MdxComponents.tsx b/src/app/blog/[id]/MdxComponents.tsx index 4b9978b..e480024 100644 --- a/src/app/blog/[id]/MdxComponents.tsx +++ b/src/app/blog/[id]/MdxComponents.tsx @@ -68,6 +68,7 @@ export const mdxComponents: MDXComponents = { customStyle={{ backgroundColor: "var(--code-bg)", color: "var(--code-text)", + paddingRight: "7rem", }} {...props} /> diff --git a/src/app/blog/[id]/page.tsx b/src/app/blog/[id]/page.tsx index a585f10..4180c32 100644 --- a/src/app/blog/[id]/page.tsx +++ b/src/app/blog/[id]/page.tsx @@ -1,17 +1,13 @@ -import { serialize } from "next-mdx-remote/serialize"; import { notFound } from "next/navigation"; import { blogPosts } from "@/data/blogPosts"; -import Comments from "@/components/Comments"; import ReadingProgress from "@/components/ReadingProgress"; import { extractHeadings, estimateReadingTime } from "@/utils/blogHelpers"; -import BlogHeader from "./BlogHeader"; -import BlogContent from "./BlogContent"; -import BlogFooter from "./BlogFooter"; -import RelatedPosts from "./RelatedPosts"; -import ShareButtons from "./ShareButtons"; -import TableOfContents from "./TableOfContents"; -import { mdxComponents } from "./MdxComponents"; import type { BlogPost, Heading } from "./types"; +import dynamic from "next/dynamic"; + +const AnimatedBlogPost = dynamic(() => import("./AnimatedBlogPost"), { + ssr: false, +}); interface BlogPostParams { params: { id: string }; @@ -42,29 +38,14 @@ export default async function BlogPost({ params }: BlogPostParams) { .slice(0, 3); return ( -
+ <> -
- -
- - - - - -
- -
-
-
-
+ + ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 21c8a55..82a93fd 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,6 +7,7 @@ import Footer from "@/components/Footer"; import { cn } from "@/lib/utils"; import { ThemeProvider } from "next-themes"; +import AnimatedLayout from "@/components/AnimatedLayout"; const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }); @@ -31,7 +32,11 @@ export default function RootLayout({ >
-
{children}
+ +
+ {children} +
+