feat: 实现命令面板、颜色取色、JSON格式化和系统信息功能

- 重构项目架构,采用四层架构模式 (Command → Service → Platform → Utils)
  - 实现命令面板功能,支持快捷搜索和特征分类
  - 添加颜色取色功能,支持屏幕像素颜色获取
  - 添加JSON格式化功能,支持JSON格式化和压缩
  - 添加系统信息功能,显示操作系统和硬件信息
  - 移除旧的状态文档和无用配置文件
This commit is contained in:
2026-02-10 18:46:11 +08:00
parent db4978e349
commit 927eaa1e03
62 changed files with 7536 additions and 1958 deletions

156
src/pages/Search.tsx Normal file
View File

@@ -0,0 +1,156 @@
import { useState, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import Fuse from "fuse.js";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Keyboard } from "lucide-react";
import { SearchResult } from "@/components/search/SearchResult";
import { features } from "@/features/registry";
export function Search() {
const navigate = useNavigate();
const [query, setQuery] = useState("");
const [selectedIndex, setSelectedIndex] = useState(0);
// 使用 Fuse.js 进行模糊搜索
const fuse = useMemo(() => {
return new Fuse(features, {
keys: [
{ name: 'name', weight: 2 },
{ name: 'description', weight: 1.5 },
{ name: 'tags', weight: 1 },
],
threshold: 0.4,
ignoreLocation: true,
});
}, []);
// 搜索结果
const searchResults = useMemo(() => {
if (!query.trim()) {
return features;
}
return fuse.search(query).map(result => result.item);
}, [query, fuse]);
// 键盘导航
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'ArrowDown') {
e.preventDefault();
setSelectedIndex((prev) =>
prev < searchResults.length - 1 ? prev + 1 : prev
);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : 0));
} else if (e.key === 'Enter' && searchResults.length > 0) {
e.preventDefault();
const selected = searchResults[selectedIndex];
if (selected.implemented) {
navigate(selected.route);
}
} else if (e.key === 'Escape') {
navigate('/');
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [searchResults, selectedIndex, navigate]);
// 重置选中索引
useEffect(() => {
setSelectedIndex(0);
}, [query]);
const handleResultClick = useCallback((index: number) => {
setSelectedIndex(index);
}, []);
return (
<div className="min-h-screen bg-background">
{/* 顶部导航栏 */}
<header className="border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center gap-3 mb-4">
<Button
variant="ghost"
size="sm"
onClick={() => navigate('/')}
>
<ArrowLeft className="w-4 h-4" />
</Button>
<h1 className="text-xl font-bold"></h1>
</div>
{/* 大搜索框 */}
<div className="relative">
<Keyboard className="absolute left-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-muted-foreground" />
<Input
type="text"
placeholder="输入关键词搜索功能..."
value={query}
onChange={(e) => setQuery(e.target.value)}
className="pl-12 h-14 text-lg"
autoFocus
/>
</div>
</div>
</header>
{/* 主内容区 */}
<main className="container mx-auto px-4 py-6">
{/* 结果统计 */}
<div className="mb-4">
<p className="text-sm text-muted-foreground">
{query.trim() ? (
<>
<span className="font-semibold text-foreground">{searchResults.length}</span>
</>
) : (
<>
<span className="font-semibold text-foreground">{features.length}</span>
</>
)}
</p>
</div>
{/* 搜索结果列表 */}
<div className="space-y-3 max-h-[calc(100vh-250px)] overflow-y-auto">
{searchResults.length > 0 ? (
searchResults.map((feature, index) => (
<SearchResult
key={feature.id}
feature={feature}
isHighlighted={index === selectedIndex}
onClick={() => handleResultClick(index)}
/>
))
) : (
<div className="flex flex-col items-center justify-center py-16 text-center">
<div className="w-16 h-16 bg-muted rounded-full flex items-center justify-center mb-4">
<span className="text-3xl">🔍</span>
</div>
<h3 className="text-lg font-semibold mb-2"></h3>
<p className="text-sm text-muted-foreground">
使
</p>
</div>
)}
</div>
{/* 键盘提示 */}
{searchResults.length > 0 && (
<div className="mt-4 p-3 bg-muted/50 rounded-lg">
<p className="text-xs text-muted-foreground text-center">
<span className="font-semibold"></span>
<span className="font-semibold ml-2">Enter</span>
<span className="font-semibold ml-2">Esc</span>
</p>
</div>
)}
</main>
</div>
);
}