import { Command, Home, Search, X } from "lucide-react"; import { useEffect, useMemo, useRef, useState, type ComponentType } from "react"; import { useNavigate } from "react-router"; import { navigationTools } from "@/constant/navigation-tools"; import { cn } from "@/lib/utils"; import { useUserStore } from "@/stores/use-user-store"; type PaletteEntry = { id: string; title: string; icon: ComponentType<{ className?: string; strokeWidth?: number }>; to: string; }; /** 顶栏搜索 / ⌘K 命令面板:按功能开关过滤当前可用页面入口。 */ export function WorkspaceCommandPalette({ open, onClose }: { open: boolean; onClose: () => void }) { const navigate = useNavigate(); const features = useUserStore((state) => state.features); const [query, setQuery] = useState(""); const [highlight, setHighlight] = useState(0); const inputRef = useRef(null); const entries = useMemo(() => { const toolEntry = (slug: string, to: string): PaletteEntry => { const tool = navigationTools.find((item) => item.slug === slug); return { id: slug, title: tool?.label ?? slug, icon: tool?.icon ?? Home, to }; }; return [ { id: "home", title: "首页", icon: Home, to: "/" }, toolEntry("projects", "/projects"), toolEntry("canvas", "/canvas"), ...(features.taskCenterEnabled ? [toolEntry("tasks", "/tasks")] : []), toolEntry("assets", "/assets"), toolEntry("skills", "/skills"), ...(features.creditsEnabled ? [toolEntry("wallet", "/wallet")] : []), toolEntry("settings", "/settings"), ]; }, [features]); const filtered = useMemo(() => { const keyword = query.trim().toLowerCase(); if (!keyword) return entries; return entries.filter((entry) => entry.title.toLowerCase().includes(keyword)); }, [entries, query]); useEffect(() => { if (open) { setQuery(""); window.setTimeout(() => inputRef.current?.focus(), 20); } }, [open]); useEffect(() => { setHighlight(0); }, [filtered.length, open]); useEffect(() => { if (!open) return; const handler = (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); onClose(); return; } if (event.key === "ArrowDown") { event.preventDefault(); setHighlight((index) => Math.min(index + 1, Math.max(filtered.length - 1, 0))); return; } if (event.key === "ArrowUp") { event.preventDefault(); setHighlight((index) => Math.max(index - 1, 0)); return; } if (event.key === "Enter" && filtered[highlight]) { event.preventDefault(); const target = filtered[highlight]; onClose(); navigate(target.to); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [open, filtered, highlight, navigate, onClose]); if (!open) return null; return (
setQuery(event.target.value)} className="min-w-0 flex-1 bg-transparent text-[var(--fs-body)] outline-none placeholder:text-foreground/45" placeholder="搜索页面或操作…" /> ⌘K
{filtered.length ? (
    {filtered.map((entry, index) => { const Icon = entry.icon; return (
  • ); })}
) : (

未找到「{query}」相关页面

)}
); }