feat: launcher-style search UI (Raycast/Spotlight)

- Replace Pagefind UI with cmdk + Pagefind low-level API
- Quick actions when empty: nav (home, blog, tags) + recent posts
- Debounced full-text search with keyboard navigation
- Pass recent posts from layout to SearchModal
- Extract cn utility to lib/utils.ts
- Remove Pagefind UI styles, add Radix overlay styling
- Align blog search bar styling with launcher

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-13 23:41:10 +08:00
parent 7d85446ac5
commit fe28262ef4
10 changed files with 738 additions and 217 deletions

View File

@@ -4,18 +4,13 @@ import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { FiList, FiX } from 'react-icons/fi';
import dynamic from 'next/dynamic';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { cn } from '@/lib/utils';
// Lazy load PostToc since it's not critical for initial render
const PostToc = dynamic(() => import('./post-toc').then(mod => ({ default: mod.PostToc })), {
ssr: false,
});
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function PostLayout({ children, hasToc = true, contentKey }: { children: React.ReactNode; hasToc?: boolean; contentKey?: string }) {
const [isTocOpen, setIsTocOpen] = useState(false); // Default closed on mobile
const [isDesktopTocOpen, setIsDesktopTocOpen] = useState(hasToc); // Separate state for desktop