diff --git a/src/components/skills/SkillsPage.tsx b/src/components/skills/SkillsPage.tsx index 7de6a7a..2de0491 100644 --- a/src/components/skills/SkillsPage.tsx +++ b/src/components/skills/SkillsPage.tsx @@ -1,7 +1,8 @@ -import { useState, useEffect, forwardRef, useImperativeHandle } from "react"; +import { useState, useEffect, useMemo, forwardRef, useImperativeHandle } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; -import { RefreshCw } from "lucide-react"; +import { Input } from "@/components/ui/input"; +import { RefreshCw, Search } from "lucide-react"; import { toast } from "sonner"; import { SkillCard } from "./SkillCard"; import { RepoManagerPanel } from "./RepoManagerPanel"; @@ -24,6 +25,7 @@ export const SkillsPage = forwardRef( const [repos, setRepos] = useState([]); const [loading, setLoading] = useState(true); const [repoManagerOpen, setRepoManagerOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); const loadSkills = async (afterLoad?: (data: Skill[]) => void) => { try { @@ -162,6 +164,24 @@ export const SkillsPage = forwardRef( await Promise.all([loadRepos(), loadSkills()]); }; + // 过滤技能列表 + const filteredSkills = useMemo(() => { + if (!searchQuery.trim()) return skills; + + const query = searchQuery.toLowerCase(); + return skills.filter((skill) => { + const name = skill.name?.toLowerCase() || ""; + const description = skill.description?.toLowerCase() || ""; + const directory = skill.directory?.toLowerCase() || ""; + + return ( + name.includes(query) || + description.includes(query) || + directory.includes(query) + ); + }); + }, [skills, searchQuery]); + return (
{/* 顶部操作栏(固定区域)已移除,由 App.tsx 接管 */} @@ -190,16 +210,49 @@ export const SkillsPage = forwardRef(
) : ( -
- {skills.map((skill) => ( - - ))} -
+ <> + {/* 搜索框 */} +
+
+ + setSearchQuery(e.target.value)} + className="pl-9" + /> +
+ {searchQuery && ( +

+ {t("skills.count", { count: filteredSkills.length })} +

+ )} +
+ + {/* 技能列表或无结果提示 */} + {filteredSkills.length === 0 ? ( +
+

+ {t("skills.noResults")} +

+

+ {t("skills.emptyDescription")} +

+
+ ) : ( +
+ {filteredSkills.map((skill) => ( + + ))} +
+ )} + )} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 17a6f7c..afaeacd 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -735,7 +735,10 @@ "removeSuccess": "Repository {{owner}}/{{name}} removed", "removeFailed": "Failed to remove", "skillCount": "{{count}} skills detected" - } + }, + "search": "Search Skills", + "searchPlaceholder": "Search skill name or description...", + "noResults": "No matching skills found" }, "deeplink": { "confirmImport": "Confirm Import Provider", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 59faf39..2208efa 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -735,7 +735,10 @@ "removeSuccess": "仓库 {{owner}}/{{name}} 已删除", "removeFailed": "删除失败", "skillCount": "识别到 {{count}} 个技能" - } + }, + "search": "搜索技能", + "searchPlaceholder": "搜索技能名称或描述...", + "noResults": "未找到匹配的技能" }, "deeplink": { "confirmImport": "确认导入供应商配置",