mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 08:14:33 +08:00
feat(skill): add database migration and Gemini support for multi-app skills
- Refactor skills table from single key to (directory, app_type) composite primary key - Add migration logic to convert existing skill records - Support skill installation/uninstallation for Claude/Codex/Gemini independently - Add new Tauri commands: get_skills_for_app, install_skill_for_app, uninstall_skill_for_app - Update frontend API and components to support app-specific skill operations
This commit is contained in:
@@ -19,11 +19,17 @@ import { RefreshCw, Search } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { SkillCard } from "./SkillCard";
|
||||
import { RepoManagerPanel } from "./RepoManagerPanel";
|
||||
import { skillsApi, type Skill, type SkillRepo } from "@/lib/api/skills";
|
||||
import {
|
||||
skillsApi,
|
||||
type Skill,
|
||||
type SkillRepo,
|
||||
type AppType,
|
||||
} from "@/lib/api/skills";
|
||||
import { formatSkillError } from "@/lib/errors/skillErrorParser";
|
||||
|
||||
interface SkillsPageProps {
|
||||
onClose?: () => void;
|
||||
initialApp?: AppType;
|
||||
}
|
||||
|
||||
export interface SkillsPageHandle {
|
||||
@@ -32,7 +38,7 @@ export interface SkillsPageHandle {
|
||||
}
|
||||
|
||||
export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
||||
({ onClose: _onClose }, ref) => {
|
||||
({ onClose: _onClose, initialApp = "claude" }, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [skills, setSkills] = useState<Skill[]>([]);
|
||||
const [repos, setRepos] = useState<SkillRepo[]>([]);
|
||||
@@ -42,11 +48,13 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
||||
const [filterStatus, setFilterStatus] = useState<
|
||||
"all" | "installed" | "uninstalled"
|
||||
>("all");
|
||||
// 使用 initialApp,不允许切换
|
||||
const selectedApp = initialApp;
|
||||
|
||||
const loadSkills = async (afterLoad?: (data: Skill[]) => void) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await skillsApi.getAll();
|
||||
const data = await skillsApi.getAll(selectedApp);
|
||||
setSkills(data);
|
||||
if (afterLoad) {
|
||||
afterLoad(data);
|
||||
@@ -84,6 +92,7 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([loadSkills(), loadRepos()]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
@@ -93,7 +102,7 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
||||
|
||||
const handleInstall = async (directory: string) => {
|
||||
try {
|
||||
await skillsApi.install(directory);
|
||||
await skillsApi.install(directory, selectedApp);
|
||||
toast.success(t("skills.installSuccess", { name: directory }));
|
||||
await loadSkills();
|
||||
} catch (error) {
|
||||
@@ -122,7 +131,7 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
||||
|
||||
const handleUninstall = async (directory: string) => {
|
||||
try {
|
||||
await skillsApi.uninstall(directory);
|
||||
await skillsApi.uninstall(directory, selectedApp);
|
||||
toast.success(t("skills.uninstallSuccess", { name: directory }));
|
||||
await loadSkills();
|
||||
} catch (error) {
|
||||
|
||||
+20
-6
@@ -19,17 +19,31 @@ export interface SkillRepo {
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export type AppType = "claude" | "codex" | "gemini";
|
||||
|
||||
export const skillsApi = {
|
||||
async getAll(): Promise<Skill[]> {
|
||||
return await invoke("get_skills");
|
||||
async getAll(app: AppType = "claude"): Promise<Skill[]> {
|
||||
if (app === "claude") {
|
||||
return await invoke("get_skills");
|
||||
}
|
||||
return await invoke("get_skills_for_app", { app });
|
||||
},
|
||||
|
||||
async install(directory: string): Promise<boolean> {
|
||||
return await invoke("install_skill", { directory });
|
||||
async install(directory: string, app: AppType = "claude"): Promise<boolean> {
|
||||
if (app === "claude") {
|
||||
return await invoke("install_skill", { directory });
|
||||
}
|
||||
return await invoke("install_skill_for_app", { app, directory });
|
||||
},
|
||||
|
||||
async uninstall(directory: string): Promise<boolean> {
|
||||
return await invoke("uninstall_skill", { directory });
|
||||
async uninstall(
|
||||
directory: string,
|
||||
app: AppType = "claude",
|
||||
): Promise<boolean> {
|
||||
if (app === "claude") {
|
||||
return await invoke("uninstall_skill", { directory });
|
||||
}
|
||||
return await invoke("uninstall_skill_for_app", { app, directory });
|
||||
},
|
||||
|
||||
async getRepos(): Promise<SkillRepo[]> {
|
||||
|
||||
Reference in New Issue
Block a user