Skip to content

Latest commit

 

History

History
233 lines (175 loc) · 9.59 KB

File metadata and controls

233 lines (175 loc) · 9.59 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Lemon Blog is a modern, static blog platform built with Next.js App Router and Tailwind CSS. It features:

  • Static Site Generation: All blog posts are generated as static pages at build time
  • Markdown Content: Articles are stored as Markdown files in /content/articles
  • Syntax Highlighting: Code blocks with Prism.js syntax highlighting (rehype-prism-plus)
  • Dark Mode: Full dark/light mode support with system preference detection
  • Responsive Design: Mobile-first design with Tailwind CSS
  • SEO Optimized: Built-in metadata and OpenGraph support
  • Image Service: Unified image service supporting Unsplash, GIPHY, and Storyset
  • Cover Images: Automatic cover image extraction from article content

Development Commands

# Install dependencies
npm install

# Start development server (also copies article images)
npm run dev

# Build for production (also copies article images)
npm run build

# Start production server
npm run start

# Lint code
npm run lint

# Fix linting issues
npm run lint:fix

# Type checking
npm run type-check

# Bundle analysis
npm run analyze

Important: The dev and build commands automatically run scripts/copy-images.js before starting. This script copies images from content/articles/{slug}/img/ and content/articles/img/ to public/articles/ for static serving.

Project Architecture

Key Architectural Patterns

Image Path Resolution: The blog supports two image storage patterns:

  1. Per-article: content/articles/{slug}/img/*/articles/{slug}/img/*
  2. Shared: content/articles/img/*/articles/img/*

Relative image paths in markdown (e.g., img/photo.jpg) are automatically converted to absolute paths during build time via fixImagePaths() in src/lib/posts.ts.

Image Service Architecture (src/lib/images/):

  • index.ts: Unified imageService class with caching
  • unsplash.ts: Unsplash photo integration
  • giphy.ts: GIPHY GIF integration
  • storyset.ts: Storyset illustration integration
  • All services follow a consistent pattern: search, random, and optimized URL generation

Cover Image Extraction: The first image in markdown content is automatically extracted as the cover image. This supports both markdown syntax ![alt](url) and HTML <img> tags. The extracted image is removed from the content to avoid duplication.

Directory Structure

src/
├── app/                    # Next.js App Router pages
│   ├── blog/              # Blog-related pages
│   │   ├── [slug]/        # Dynamic blog post pages
│   │   └── page.tsx       # Blog listing page
│   ├── layout.tsx         # Root layout with Navbar and Footer
│   └── page.tsx           # Homepage
├── components/            # Reusable React components
│   ├── Navbar.tsx         # Navigation with dark mode toggle
│   ├── Footer.tsx         # Site footer
│   ├── BannerImage.tsx    # Smart image component with Unsplash/GIPHY/Storyset support
│   ├── ArticleCard.tsx    # Blog post card component
│   └── ImageOptimizer.tsx # Image optimization utilities
├── lib/                   # Utility functions and configurations
│   ├── posts.ts           # Markdown processing, image path fixing, cover extraction
│   ├── images/            # Image service (Unsplash, GIPHY, Storyset)
│   └── styles.css         # Custom styles for syntax highlighting
└── types/
    └── post.ts            # Post type definition

content/articles/          # Markdown blog posts
├── {slug}.md              # Article markdown files
├── {slug}/img/            # Per-article images (copied to public/articles/{slug}/img/)
└── img/                   # Shared article images (copied to public/articles/img/)

scripts/
└── copy-images.js         # Copies article images to public directory

public/articles/           # Generated by copy-images.js (do not edit manually)

Key Files and Their Purpose

  • src/lib/posts.ts: Core utilities including fixImagePaths(), extractCoverImageSync(), getAllPosts(), getPostBySlug()
  • src/lib/images/index.ts: Unified image service with getArticleImage(), searchImages(), getRandomImage()
  • src/app/blog/[slug]/page.tsx: Dynamic route with generateStaticParams() for static generation
  • src/components/BannerImage.tsx: Smart image component supporting multiple sources with fallback
  • scripts/copy-images.js: Build-time script that copies images from content to public
  • next.config.ts: Static export configuration with output: 'export'

Content Management

Adding New Blog Posts

  1. Create a new .md file in /content/articles/
  2. Include required frontmatter:
    ---
    title: "Article Title"
    date: "YYYY-MM-DD"
    description: "Brief description"
    author: "Author Name"
    tags: ["tag1", "tag2"]
    ---
  3. Add images to either content/articles/{slug}/img/ (per-article) or content/articles/img/ (shared)
  4. Reference images in markdown as img/filename.jpg - paths will be auto-converted
  5. The first image in content becomes the cover image automatically

Frontmatter Fields

  • title: Required. The article title
  • date: Required. Publication date (YYYY-MM-DD format)
  • description: Required. Brief description for SEO and previews
  • author: Required. Author name (defaults to "Anonymous")
  • tags: Optional. Array of string tags for categorization (also used for image matching)

Adding New Thoughts

Short thoughts/ideas live as individual Markdown files in content/thoughts/ and are rendered on the /thoughts page (timeline style, newest first). Each thought also has its own detail page at /thoughts/{slug}.

  1. Create a new file content/thoughts/YYYY-MM-DD.md (or YYYY-MM-DD-some-title.md to disambiguate). The filename (without .md) becomes the slug / URL.
  2. Use frontmatter for metadata and the rest as plain Markdown:
    ---
    date: 2026-07-31
    title: 独立判断的代价    # optional
    tags: [随笔, 思辨]       # optional
    ---
    
    第一段…
    
    第二段(段间空一行即可)…
  3. Both the list page and the detail page render the body through the same Markdown pipeline (remark + remark-gfm), so list and detail stay in sync. The list page shows up to 10 thoughts per page with URL pagination (/thoughts?page=2); sort and excerpt are derived automatically.

Supported Markdown features: paragraphs, headings, lists, blockquotes, fenced code, inline emphasis, links, images, and raw HTML (passes through). Plain paragraph text (the common case) just needs blank lines between paragraphs.

Image Handling

For article images (displayed in content):

  • Place in content/articles/{slug}/img/ or content/articles/img/
  • Reference as ![alt](img/filename.jpg) in markdown
  • Paths are automatically converted to /articles/... at build time

For cover images:

  • First image in markdown is auto-extracted as cover
  • Supports ![alt](url) and <img src="url"> syntax
  • Removed from content after extraction to avoid duplication
  • Can be overridden via coverImage property in Post type

For BannerImage component:

  • Accepts customImage prop to override automatic selection
  • Falls back to Unsplash/GIPHY/Storyset based on tags if no custom image
  • Supports preferredSource prop ('unsplash', 'giphy', 'storyset')

Styling and Design

  • Tailwind CSS v4: Utility-first CSS framework with new PostCSS-based architecture
  • Dark Mode: Class-based dark mode with system preference detection
  • Typography: Custom prose styles using @tailwindcss/typography plugin
  • Responsive: Mobile-first responsive design
  • Components: Consistent design system with reusable components

Deployment

The project is configured for one-click deployment to Vercel:

  1. Push to GitHub
  2. Connect repository to Vercel
  3. Vercel automatically builds and deploys

Build Configuration

  • Static Export: output: 'export' in next.config.ts
  • Image Optimization: unoptimized: true required for static export
  • Turbopack: Enabled for faster builds
  • Trailing Slash: Enabled for consistent URL structure
  • Remote Patterns: Configured for Unsplash, via.placeholder, and wildcard HTTPS

Development Notes

Working with Markdown

  • Uses gray-matter for frontmatter parsing
  • Uses remark and rehype for Markdown processing pipeline
  • rehype-prism-plus provides syntax highlighting with line numbers
  • Image paths are fixed before markdown processing via fixImagePaths()
  • First image is extracted as cover, then removed from content

State Management

  • Dark mode preference stored in localStorage
  • Theme state managed in Navbar component (client component)
  • System preference detection on first load
  • Image service uses in-memory Map for caching

Type Safety

  • All types defined in src/types/post.ts
  • Client-safe type exports (no server-only types in client components)
  • TypeScript strict mode enabled

Dependencies Key

  • Next.js 16: React framework with App Router
  • React 19: Latest React with concurrent features
  • Tailwind CSS v4: Latest utility-first CSS with PostCSS integration
  • gray-matter: Frontmatter parser for Markdown
  • remark/rehype: Markdown processor ecosystem
  • rehype-prism-plus: Syntax highlighting with Prism.js
  • date-fns: Date formatting utilities
  • @tailwindcss/typography: Prose styles for article content
  • @tailwindcss/aspect-ratio: Aspect ratio utilities