feat(skills): add restore and delete for skill backups

Introduce list/restore/delete commands for skill backups created during
uninstall. Restore copies files back to SSOT, saves the DB record, and
syncs to the current app with rollback on failure. Delete removes the
backup directory after a confirmation dialog. ConfirmDialog gains a
configurable zIndex prop to support nested dialog stacking.
This commit is contained in:
Jason
2026-03-16 00:00:31 +08:00
parent 9336001746
commit 333c9f277b
12 changed files with 678 additions and 3 deletions
+43 -1
View File
@@ -1,6 +1,7 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
skillsApi,
type SkillBackupEntry,
type DiscoverableSkill,
type ImportSkillSelection,
type InstalledSkill,
@@ -17,6 +18,24 @@ export function useInstalledSkills() {
});
}
export function useSkillBackups() {
return useQuery({
queryKey: ["skills", "backups"],
queryFn: () => skillsApi.getBackups(),
enabled: false,
});
}
export function useDeleteSkillBackup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (backupId: string) => skillsApi.deleteBackup(backupId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["skills", "backups"] });
},
});
}
/**
* 发现可安装的 Skills(从仓库获取)
*/
@@ -62,6 +81,23 @@ export function useUninstallSkill() {
});
}
export function useRestoreSkillBackup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
backupId,
currentApp,
}: {
backupId: string;
currentApp: AppId;
}) => skillsApi.restoreBackup(backupId, currentApp),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["skills", "installed"] });
queryClient.invalidateQueries({ queryKey: ["skills", "backups"] });
},
});
}
/**
* 切换 Skill 在特定应用的启用状态
*/
@@ -170,4 +206,10 @@ export function useInstallSkillsFromZip() {
// ========== 辅助类型 ==========
export type { InstalledSkill, DiscoverableSkill, ImportSkillSelection, AppId };
export type {
InstalledSkill,
DiscoverableSkill,
ImportSkillSelection,
SkillBackupEntry,
AppId,
};