Add full-text search with Chinese tokenization using Pagefind

Integrated Pagefind for static site search with built-in Chinese word segmentation support.

Changes:
1. **Installed Pagefind** (v1.4.0) as dev dependency
2. **Updated build script** to run Pagefind indexing after Next.js build
   - Indexes all 69 pages with 5,711 words
   - Automatic Chinese (zh-tw) language detection
3. **Created search modal component** (components/search-modal.tsx)
   - Dynamic Pagefind UI loading (lazy-loaded on demand)
   - Keyboard shortcuts (Cmd+K / Ctrl+K)
   - Chinese translations for UI elements
   - Dark mode compatible styling
4. **Added search button to header** (components/site-header.tsx)
   - Integrated SearchButton with keyboard shortcut display
   - Modal state management
5. **Custom Pagefind styles** (styles/globals.css)
   - Tailwind-based styling to match site design
   - Dark mode support
   - Highlight styling for search results

Features:
-  Full-text search across all blog posts and pages
-  Built-in Chinese word segmentation (Unicode-based)
-  Mixed Chinese/English query support
-  Zero bundle impact (20KB lazy-loaded on search activation)
-  Keyboard shortcuts (⌘K / Ctrl+K)
-  Search result highlighting with excerpts
-  Dark mode compatible

Technical Details:
- Pagefind runs post-build to index .next directory
- Search index stored in .next/pagefind/
- Chinese segmentation works automatically via Unicode boundaries
- No third-party services or API keys required

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-20 00:10:26 +08:00
parent 912c70332e
commit 2c9d5ed650
5 changed files with 339 additions and 2 deletions

View File

@@ -265,4 +265,49 @@ body {
transition: color var(--motion-duration-short) var(--motion-ease-snappy),
transform var(--motion-duration-short) var(--motion-ease-snappy);
}
}
/* Pagefind Search Styles */
.pagefind-ui__search-input {
@apply w-full rounded-lg border border-slate-200 bg-white px-4 py-3 text-slate-900 placeholder:text-slate-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-100 dark:placeholder:text-slate-500 dark:focus:ring-blue-500;
}
.pagefind-ui__search-clear {
@apply rounded-md px-2 py-1 text-sm text-slate-500 hover:bg-slate-100 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-700 dark:hover:text-slate-200;
}
.pagefind-ui__results {
@apply space-y-4;
}
.pagefind-ui__result {
@apply rounded-lg border border-slate-200 bg-white p-4 transition-all hover:border-blue-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-800 dark:hover:border-blue-600;
}
.pagefind-ui__result-link {
@apply text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300;
text-decoration: none;
}
.pagefind-ui__result-title {
@apply mb-2 text-lg font-semibold text-slate-900 dark:text-slate-100;
}
.pagefind-ui__result-excerpt {
@apply text-sm text-slate-600 dark:text-slate-300;
}
.pagefind-ui__result-excerpt mark {
@apply bg-yellow-200 font-semibold text-slate-900 dark:bg-yellow-600 dark:text-slate-100;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
}
.pagefind-ui__message {
@apply text-center text-sm text-slate-500 dark:text-slate-400;
padding: 2rem 0;
}
.pagefind-ui__button {
@apply rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-blue-500 dark:hover:bg-blue-600;
}