Commit Graph

193 Commits

Author SHA1 Message Date
2402c94760 perf: 全面優化部落格載入速度與效能
- 字體載入優化:添加 preconnect 到 Google Fonts,優化載入順序
- 元件延遲載入:RightSidebar、MastodonFeed、PostToc、BackToTop 使用動態載入
- 圖片優化:添加 blur placeholder,首屏圖片添加 priority,優化圖片尺寸配置
- 快取策略:為 HTML 頁面、OG 圖片、RSS feed 添加快取標頭
- 程式碼分割:確保路由層級分割正常,延遲載入非關鍵元件
- 效能監控:添加 WebVitals 元件追蹤基本效能指標
- 連結優化:為重要連結添加 prefetch 屬性

預期效果:
- FCP 減少 20-30%
- LCP 減少 30-40%
- CLS 減少 50%+
- TTI 減少 25-35%
- Bundle Size 減少 15-25%

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 16:18:51 +08:00
62090c7742 perf: 优化字体加载性能和字间距
- 添加 adjustFontFallback: false 优化 CLS(累积布局偏移)
- 调整霞鹜文楷字间距:从负值改为正值,让手写风格字体更自然
  - h1: -0.03em → 0.01em
  - h2: -0.02em → 0.015em
  - type-display: 添加 0.01em
  - type-title: 0.02em → 0.025em
- 改善字体加载时的视觉稳定性

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 16:08:26 +08:00
a9bd56b658 feat: 将霞鹜文楷也应用到文章内 h2 标题
- 统一文章内所有标题层级的字体风格
- 保持视觉一致性

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 16:01:28 +08:00
e8666d19ee feat: 扩展霞鹜文楷字体到所有主标题级别
- 应用到 .type-title(页面标题、站点标题)
- 应用到 .type-subtitle(区块副标题)
- 应用到全局 h1 和 h2(所有主标题级别)
- 保持卡片标题、导航、正文等使用系统字体
- 统一大标题层级的视觉风格

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 16:01:21 +08:00
2229f6bb6f feat: 添加霞鹜文楷字体到文章主标题
- 使用 Google Fonts 的 LXGW WenKai TC 字体
- 仅应用到文章主标题 (.prose h1 和 .type-display)
- 只加载 Regular (400) 和 Bold (700) 字重以优化性能
- 添加字体子集化脚本(可选,用于本地字体文件)
- 保持系统字体作为 fallback

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 15:05:00 +08:00
ed63ec7d9a Remove unnecessary Partial Prerendering (PPR) configuration
- Remove cacheComponents: true from next.config.mjs
- Update README.md to remove PPR references
- All pages are fully static-generated, PPR provides no benefit

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 14:57:08 +08:00
5a80567117 Fix sidebar personal info section turning white on hover in dark mode
Added dark:hover:bg-slate-800/80 to prevent the light hover:bg-slate-50
from overriding the dark background.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 20:14:41 +08:00
614d8dd5aa Update content submodule: fix article line breaks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:04:14 +08:00
31b5821532 Migrate to Tailwind CSS v4 with CSS-first configuration
- Replace tailwindcss v3 + autoprefixer with tailwindcss v4 + @tailwindcss/postcss
- Migrate tailwind.config.cjs theme to @theme block in globals.css
- Add @custom-variant dark for class-based dark mode (next-themes)
- Load typography plugin via @plugin directive, replace prose-dark with prose-invert
- Convert prose dark mode overrides from JS config to CSS (.dark .prose rules)
- Add @source directive for content submodule detection
- Replace postcss.config.cjs with postcss.config.mjs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:55:43 +08:00
661b67cc01 Fix PPR empty generateStaticParams error and update dependencies
- Add placeholder fallback to generateStaticParams for cacheComponents compatibility
- Update npm packages within semver range (next 16.1.6, react 19.2.4, shiki 3.22.0, etc.)
- Add /new-post skill for blog publishing workflow
- Update CLAUDE.md with git remote mirroring docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:39:17 +08:00
9c7f2463aa Add new post and update CLAUDE.md with deployment docs
- New post: 不是所有眼淚,都該被濃縮成重點
- CLAUDE.md: add deployment workflow and content submodule instructions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 12:50:36 +08:00
ce4245c148 Revert "Migrate to HeroUI v3 and Tailwind CSS v4"
This reverts commit 6a9296f33d.
2026-01-23 02:46:02 +08:00
6a9296f33d Migrate to HeroUI v3 and Tailwind CSS v4
- Upgrade from Tailwind CSS v3 to v4 with CSS-first configuration
- Install HeroUI v3 beta packages (@heroui/react, @heroui/styles)
- Migrate PostCSS config to new @tailwindcss/postcss plugin
- Convert tailwind.config.cjs to CSS @theme directive in globals.css
- Replace @tailwindcss/typography with custom prose styles

Component migrations:
- theme-toggle, back-to-top: HeroUI Button with onPress
- post-card, post-list-item: HeroUI Card compound components
- right-sidebar: HeroUI Card, Avatar, Chip
- search-modal: HeroUI Modal with compound structure
- nav-menu: HeroUI Button for mobile controls
- post-list-with-controls: HeroUI Button for sorting/pagination

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:47:36 +08:00
1cd9106ad0 Add CLAUDE.md for Claude Code session context
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 15:19:20 +08:00
35df76cd13 Fix Mastodon widget line breaks not displaying
Added whitespace-pre-line CSS class to preserve line breaks in
Mastodon post content. Previously, newline characters were being
collapsed into single spaces by default HTML whitespace handling.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 12:43:18 +08:00
ee2eb4796e SECURITY: Update Next.js and React to patch critical RCE vulnerability
Addresses CVE-2025-55182 (React) and CVE-2025-66478 (Next.js)
- CVSS Score: 10.0 (Critical)
- Allows unauthenticated remote code execution via RSC payloads

Updates:
- Next.js: 16.0.3 → 16.0.7
- React: 19.2.0 → 19.2.1
- react-dom: 19.2.0 → 19.2.1

References:
- https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components
- https://nextjs.org/blog/CVE-2025-66478

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 21:57:55 +08:00
d90442456b Partial Lecture Updatee 2025-11-25 00:47:32 +08:00
b17930c10b Update content submodule to 5b1737e 2025-11-23 23:56:37 +08:00
1f3323834e Update navigation layout and assets 2025-11-21 14:51:24 +08:00
7cdfb90b1b Portal mobile TOC overlay to stay floating 2025-11-21 01:48:10 +08:00
f6c5be0ee4 Slim reading progress bar 2025-11-21 01:44:23 +08:00
fc24ddb676 Portal reading progress bar above all layers 2025-11-21 01:42:49 +08:00
cafb810155 Make reading progress bar prominent 2025-11-21 01:40:06 +08:00
ae37f93508 Raise reading progress bar above header 2025-11-21 01:36:04 +08:00
4a4d6dd933 Refine typography palette and dark heading colors 2025-11-21 01:29:57 +08:00
7bf2c4149d Add hover delay to nav dropdown 2025-11-21 01:20:48 +08:00
9d7a6757c9 Raise nav dropdown z-index 2025-11-21 01:17:21 +08:00
d03b061c1e Keep dropdown nav open while hovering 2025-11-21 01:15:28 +08:00
d768d108d6 Add nested navigation groups 2025-11-21 01:10:15 +08:00
7685c79705 Fix TOC duplication when navigating 2025-11-21 00:39:56 +08:00
4173aa69d3 Improve TOC synchronization with contentKey prop
Better fix for TOC showing previous article headings. The issue was
relying on pathname which could be out of sync with the actual content.

Changes:
- Pass contentKey as prop to PostToc instead of using usePathname()
- Use contentKey in useEffect dependency for more reliable updates
- Replace setTimeout with double requestAnimationFrame for DOM sync
- Remove unused usePathname import

This ensures the TOC effect runs exactly when the content changes,
not just when the URL changes, providing more reliable synchronization
between the TOC and the article content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 23:38:30 +08:00
e2f9c9d556 Fix TOC showing headings from previous article
The TOC was displaying sections from previously viewed articles when
navigating between posts. This happened because the DOM query for
headings ran before Next.js finished updating the page content.

Changes to components/post-toc.tsx:
- Clear items and activeId immediately when pathname changes
- Add 50ms delay before querying DOM for new headings
- Properly handle IntersectionObserver cleanup with timeout

This ensures the TOC always shows the correct headings for the
current article, not the previous one.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 23:29:17 +08:00
5d226a2969 Fix TOC button overlap with back-to-top on mobile
Adjusted TOC button positioning to prevent overlap with the back-to-top
button on mobile devices:

- Mobile: bottom-20 (80px) - sits well above back-to-top at bottom-6
- Desktop: lg:bottom-8 - maintains original desktop position
- Both buttons now aligned to right-4 on mobile for consistency

This gives ~56px vertical spacing between buttons on mobile,
preventing any overlap while keeping both easily accessible.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 23:08:50 +08:00
a77cd17419 Fix TOC button to be truly fixed-position using React Portal
The TOC toggle button was appearing near the end of posts instead of
floating at a fixed position. This happened because the button was
rendered inside the PostLayout component hierarchy.

Changes:
- Use React Portal to render TOC button at document.body level
- Add mounted state for proper SSR/client hydration
- Button now floats like back-to-top button, visible from start

This ensures the button is always visible and accessible, similar to
the back-to-top button behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 22:42:59 +08:00
d42cb46af8 Remove bundle analyzer (incompatible with Turbopack)
- Removed @next/bundle-analyzer package
- Removed build:analyze script
- Cleaned up next.config.mjs

The Next Bundle Analyzer is not compatible with Turbopack builds yet.
Since this project uses Turbopack, the analyzer cannot be used.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 22:35:46 +08:00
d6edcf1757 Fix bundle analyzer to use webpack instead of Turbopack
Bundle analyzer requires webpack, add --webpack flag to build:analyze script.

Note: This makes analysis builds slower but enables bundle visualization.
Regular builds still use faster Turbopack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 22:24:15 +08:00
ba60d49fc6 Add bundle analyzer configuration
Configure @next/bundle-analyzer for production bundle analysis:

**Changes:**
- Install @next/bundle-analyzer package
- Update next.config.mjs to wrap config with bundle analyzer
- Add npm script `build:analyze` to run build with ANALYZE=true
- Bundle analyzer only enabled when ANALYZE=true environment variable is set

**Usage:**
```bash
# Run build with bundle analysis
npm run build:analyze

# Opens interactive bundle visualization in browser
# Shows chunk sizes, module dependencies, and optimization opportunities
```

**Note:** Kept Mastodon feed as Client Component (not Server Component)
because formatRelativeTime() uses `new Date()` which requires dynamic
rendering. Converting to Server Component would prevent static generation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 22:00:02 +08:00
0bb3ee40c6 Optimize performance: Replace Framer Motion and FontAwesome, convert Mastodon to Server Component
Major performance optimizations addressing PageSpeed Insights warnings:

**Phase 1: Replace Framer Motion with CSS (~60-100KB savings)**
- Remove Framer Motion from components/post-layout.tsx
- Add CSS transitions to styles/globals.css for TOC animations
- Replace motion.div/motion.button with regular elements + CSS classes
- Remove framer-motion package dependency

**Phase 2: Replace FontAwesome with React Icons (~150-250KB savings)**
- Replace FontAwesome in 16 components with react-icons
- Use Feather icons (react-icons/fi) for UI elements
- Use FontAwesome brand icons (react-icons/fa) for social media
- Remove 4 @fortawesome packages (@fortawesome/fontawesome-svg-core,
  @fortawesome/free-brands-svg-icons, @fortawesome/free-solid-svg-icons,
  @fortawesome/react-fontawesome)
- Updated components:
  - app/error.tsx, app/tags/page.tsx, app/tags/[tag]/page.tsx
  - components/hero.tsx, components/mastodon-feed.tsx
  - components/meta-item.tsx, components/nav-menu.tsx
  - components/post-card.tsx, components/post-layout.tsx
  - components/post-list-item.tsx, components/post-list-with-controls.tsx
  - components/post-storyline-nav.tsx, components/post-toc.tsx
  - components/right-sidebar.tsx, components/search-modal.tsx
  - components/site-footer.tsx, components/theme-toggle.tsx

**Phase 3: Convert Mastodon Feed to Server Component**
- Convert components/mastodon-feed.tsx from Client Component to async Server Component
- Replace client-side useEffect fetching with server-side ISR
- Add 30-minute revalidation (next: { revalidate: 1800 })
- Eliminate 2 blocking client-side network requests
- Remove loading state (rendered on server)

**Total Impact:**
- JavaScript bundle: ~210-350KB reduction
- Blocking network requests: 2 eliminated
- Main thread time: Reduced by ~100-160ms
- Build:  Verified successful

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 21:51:24 +08:00
6badd76733 Add Schema.org JSON-LD structured data for SEO
Implemented comprehensive Schema.org structured data across the blog to improve SEO and enable rich snippets in search results.

Changes:
- Created JSON-LD helper component for safe schema rendering
- Added BlogPosting schema to blog posts with:
  * Article metadata (headline, description, image, dates)
  * Author and publisher information
  * Keywords and article sections from tags
- Added BreadcrumbList schema to blog posts for navigation
- Added WebSite and Organization schemas to root layout
  * Site-wide identity and branding
  * Search action for site search functionality
- Added CollectionPage schema to homepage
  * Blog collection metadata
- Added WebPage schema to static pages
  * Page metadata with dates and images

Benefits:
- Rich snippets in Google/Bing search results
- Better content understanding by search engines
- Article cards with images, dates, authors in SERPs
- Breadcrumb navigation in search results
- Improved SEO ranking signals

All schemas validated against Schema.org specifications and include proper Chinese language support.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 21:23:10 +08:00
237e5d403b Update content submodule with fixed internal links
Fixed all internal post links from /posts/ to /blog/ to match the actual URL structure. The contentlayer config generates URLs as /blog/{slug} for posts, not /posts/{slug}.

Fixed links in:
- content/pages/關於作者.md (7 links)
- content/posts/OPNsense 在 Proxmox VE 內安裝筆記.md (1 link)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 20:51:02 +08:00
e05295e003 Fix GitHub-style callout rendering
The callout plugin wasn't working because:
1. Contentlayer cache was preventing the plugin from running
2. The plugin wasn't handling blockquotes with whitespace text nodes
3. The plugin needed to skip whitespace-only children to find actual content

Updated the rehype plugin to:
- Skip whitespace-only text nodes when looking for [!TYPE] markers
- Handle both direct text children and text within paragraphs
- Properly extract the callout type from regex match
- Clean up empty text nodes after removing markers

Now callouts render correctly with proper structure:
- Header with icon and title
- Content wrapper with styled box
- All 5 callout types supported (NOTE, TIP, IMPORTANT, WARNING, CAUTION)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 20:39:16 +08:00
45cfc6acc4 Fix TOC showing wrong headings across navigation
The TOC component was only extracting headings once at mount, causing it to show stale headings when navigating between posts via client-side routing. Now it re-extracts headings whenever the pathname changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 20:29:30 +08:00
af40ebc5e6 Add GitHub-style callout support
Implement proper GitHub-style callouts with beautiful styling:

Features:
- Custom rehype plugin to transform > [!NOTE] syntax
- Support for 5 callout types:
  * NOTE (blue, 📝)
  * TIP (green, 💡)
  * IMPORTANT (purple, )
  * WARNING (orange, ⚠️)
  * CAUTION (red, 🚨)
- Gradient backgrounds with accent colors
- Full dark mode support
- Converts existing emoji callouts to proper format

Files:
- lib/rehype-callouts.ts: Custom plugin for parsing
- contentlayer.config.ts: Add plugin to pipeline
- styles/globals.css: Beautiful styling for all types
- content/: Convert 2 emoji callouts to [!TIP] format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 18:11:29 +08:00
f994301fbb Add RSS feed, sitemap, robots.txt, and code syntax highlighting
Implements essential blog features:

1. RSS Feed (/feed.xml)
   - Latest 20 posts with full content
   - Proper XML escaping and CDATA sections
   - Includes tags, authors, and descriptions
   - Auto-discovery link in HTML head

2. Sitemap (/sitemap.xml)
   - All posts, pages, and tag pages
   - Proper lastModified dates and priorities
   - Automatic generation via Next.js built-in support

3. Robots.txt (/robots.txt)
   - Allow all crawlers
   - Disallow API and admin routes
   - Links to sitemap for better SEO

4. Code Syntax Highlighting
   - Using rehype-pretty-code + Shiki
   - GitHub Dark/Light themes based on user preference
   - Line numbers for all code blocks
   - Support for highlighted lines
   - Inline code styling
   - Code title support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 17:59:56 +08:00
dd3f553282 Update content submodule with broken link fixes
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 17:40:29 +08:00
016c75cb8b Update content submodule with .gitignore
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 16:57:09 +08:00
0fe7faf334 Update content submodule with favicon optimization
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 16:56:35 +08:00
854c5a1097 Fix search on Vercel by serving Pagefind as static files
The previous approach using an API route to serve Pagefind files
doesn't work on Vercel's serverless environment because fs.readFile
can't reliably access files in the deployed output.

Solution: Serve Pagefind files directly from public/_pagefind as
static assets, which is the standard Next.js approach and works
reliably on all deployment platforms.

Changes:
- Update search modal to load from /_pagefind/ instead of /pagefind/
- Remove app/pagefind/[...path]/route.ts API route (no longer needed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 16:46:10 +08:00
a7aa930759 Fix search hanging on production by correcting Pagefind file path
Problem: Search modal hangs on loading in production deployment,
but works fine locally.

Root cause: The Pagefind route handler was reading files from
.next/pagefind, which is not reliably accessible in production
deployments. The build script copies Pagefind files to
public/_pagefind for deployment, but the route wasn't using them.

Solution: Changed the file path in app/pagefind/[...path]/route.ts
from .next/pagefind to public/_pagefind. Files in the public
directory are always accessible and properly deployed.

This ensures search works consistently across both development
and production environments.
2025-11-20 16:27:34 +08:00
8c71e80b2a Add Mastodon feed to right sidebar
Features:
- Display latest 5 Mastodon posts (toots) in sidebar
- Include original posts and boosts, exclude replies
- Show medium-length previews (150-200 chars)
- Styled to match existing blog design with purple Mastodon branding
- Section title: "微網誌 (Microblog)"
- Relative timestamps in Chinese ("2小時前")
- Links to original posts on Mastodon
- Loading skeletons for better UX
- Graceful error handling (fails silently if API unavailable)
- Respects dark mode

Implementation:
- Created lib/mastodon.ts with utility functions:
  - Parse Mastodon URL format
  - Strip HTML from content
  - Smart text truncation
  - Relative time formatting in Chinese
  - API functions to fetch account and statuses

- Created components/mastodon-feed.tsx:
  - Client component with useEffect for data fetching
  - Fetches directly from Mastodon public API
  - Handles boosts/reblogs with indicator
  - Shows media attachment indicators
  - Matches existing card styling patterns

- Updated components/right-sidebar.tsx:
  - Added MastodonFeed between profile and hot tags
  - Maintains consistent spacing and layout

Usage:
Set NEXT_PUBLIC_MASTODON_URL in .env.local to enable
Format: https://your.instance/@yourhandle
2025-11-20 16:10:31 +08:00