mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-17 10:29:11 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de0758eba4 | |||
| 22552f7a7f |
@@ -252,6 +252,50 @@ impl SkillService {
|
|||||||
.map(|s| s.to_string_lossy().to_string())
|
.map(|s| s.to_string_lossy().to_string())
|
||||||
.unwrap_or_else(|| skill.directory.clone());
|
.unwrap_or_else(|| skill.directory.clone());
|
||||||
|
|
||||||
|
// 检查数据库中是否已有同名 directory 的 skill(来自其他仓库)
|
||||||
|
let existing_skills = db.get_all_installed_skills()?;
|
||||||
|
for existing in existing_skills.values() {
|
||||||
|
if existing.directory.eq_ignore_ascii_case(&install_name) {
|
||||||
|
// 检查是否来自同一仓库
|
||||||
|
let same_repo = existing.repo_owner.as_deref() == Some(&skill.repo_owner)
|
||||||
|
&& existing.repo_name.as_deref() == Some(&skill.repo_name);
|
||||||
|
if same_repo {
|
||||||
|
// 同一仓库的同名 skill,返回现有记录(可能需要更新启用状态)
|
||||||
|
let mut updated = existing.clone();
|
||||||
|
updated.apps.set_enabled_for(current_app, true);
|
||||||
|
db.save_skill(&updated)?;
|
||||||
|
Self::sync_to_app_dir(&updated.directory, current_app)?;
|
||||||
|
log::info!(
|
||||||
|
"Skill {} 已存在,更新 {:?} 启用状态",
|
||||||
|
updated.name,
|
||||||
|
current_app
|
||||||
|
);
|
||||||
|
return Ok(updated);
|
||||||
|
} else {
|
||||||
|
// 不同仓库的同名 skill,报错
|
||||||
|
return Err(anyhow!(format_skill_error(
|
||||||
|
"SKILL_DIRECTORY_CONFLICT",
|
||||||
|
&[
|
||||||
|
("directory", &install_name),
|
||||||
|
(
|
||||||
|
"existing_repo",
|
||||||
|
&format!(
|
||||||
|
"{}/{}",
|
||||||
|
existing.repo_owner.as_deref().unwrap_or("unknown"),
|
||||||
|
existing.repo_name.as_deref().unwrap_or("unknown")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"new_repo",
|
||||||
|
&format!("{}/{}", skill.repo_owner, skill.repo_name)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Some("uninstallFirst"),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let dest = ssot_dir.join(&install_name);
|
let dest = ssot_dir.join(&install_name);
|
||||||
|
|
||||||
// 如果已存在则跳过下载
|
// 如果已存在则跳过下载
|
||||||
@@ -933,10 +977,12 @@ impl SkillService {
|
|||||||
Ok(meta)
|
Ok(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 去重技能列表
|
/// 去重技能列表(基于完整 key,不同仓库的同名 skill 分开显示)
|
||||||
fn deduplicate_discoverable_skills(skills: &mut Vec<DiscoverableSkill>) {
|
fn deduplicate_discoverable_skills(skills: &mut Vec<DiscoverableSkill>) {
|
||||||
let mut seen = HashMap::new();
|
let mut seen = HashMap::new();
|
||||||
skills.retain(|skill| {
|
skills.retain(|skill| {
|
||||||
|
// 使用完整 key(owner/repo:directory)作为唯一标识
|
||||||
|
// 这样不同仓库的同名 skill 会分开显示
|
||||||
let unique_key = skill.key.to_lowercase();
|
let unique_key = skill.key.to_lowercase();
|
||||||
if let std::collections::hash_map::Entry::Vacant(e) = seen.entry(unique_key) {
|
if let std::collections::hash_map::Entry::Vacant(e) = seen.entry(unique_key) {
|
||||||
e.insert(true);
|
e.insert(true);
|
||||||
|
|||||||
@@ -65,10 +65,17 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
|||||||
const addRepoMutation = useAddSkillRepo();
|
const addRepoMutation = useAddSkillRepo();
|
||||||
const removeRepoMutation = useRemoveSkillRepo();
|
const removeRepoMutation = useRemoveSkillRepo();
|
||||||
|
|
||||||
// 已安装的 directory 集合
|
// 已安装的 skill key 集合(使用 directory + repoOwner + repoName 组合判断)
|
||||||
const installedDirs = useMemo(() => {
|
const installedKeys = useMemo(() => {
|
||||||
if (!installedSkills) return new Set<string>();
|
if (!installedSkills) return new Set<string>();
|
||||||
return new Set(installedSkills.map((s) => s.directory.toLowerCase()));
|
return new Set(
|
||||||
|
installedSkills.map((s) => {
|
||||||
|
// 构建唯一 key:directory + repoOwner + repoName
|
||||||
|
const owner = s.repoOwner?.toLowerCase() || "";
|
||||||
|
const name = s.repoName?.toLowerCase() || "";
|
||||||
|
return `${s.directory.toLowerCase()}:${owner}:${name}`;
|
||||||
|
}),
|
||||||
|
);
|
||||||
}, [installedSkills]);
|
}, [installedSkills]);
|
||||||
|
|
||||||
type DiscoverableSkillItem = DiscoverableSkill & { installed: boolean };
|
type DiscoverableSkillItem = DiscoverableSkill & { installed: boolean };
|
||||||
@@ -80,12 +87,14 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
|
|||||||
const installName =
|
const installName =
|
||||||
d.directory.split("/").pop()?.toLowerCase() ||
|
d.directory.split("/").pop()?.toLowerCase() ||
|
||||||
d.directory.toLowerCase();
|
d.directory.toLowerCase();
|
||||||
|
// 使用 directory + repoOwner + repoName 组合判断是否已安装
|
||||||
|
const key = `${installName}:${d.repoOwner.toLowerCase()}:${d.repoName.toLowerCase()}`;
|
||||||
return {
|
return {
|
||||||
...d,
|
...d,
|
||||||
installed: installedDirs.has(installName),
|
installed: installedKeys.has(key),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [discoverableSkills, installedDirs]);
|
}, [discoverableSkills, installedKeys]);
|
||||||
|
|
||||||
const loading = loadingDiscoverable || fetchingDiscoverable;
|
const loading = loadingDiscoverable || fetchingDiscoverable;
|
||||||
|
|
||||||
|
|||||||
@@ -972,6 +972,7 @@
|
|||||||
"downloadTimeoutHint": "Please check network connection or retry later",
|
"downloadTimeoutHint": "Please check network connection or retry later",
|
||||||
"skillPathNotFound": "Skill path '{{path}}' not found in repository {{owner}}/{{name}}",
|
"skillPathNotFound": "Skill path '{{path}}' not found in repository {{owner}}/{{name}}",
|
||||||
"skillDirNotFound": "Skill directory not found: {{path}}",
|
"skillDirNotFound": "Skill directory not found: {{path}}",
|
||||||
|
"directoryConflict": "Skill directory '{{directory}}' is already occupied by {{existing_repo}}, cannot install from {{new_repo}}",
|
||||||
"emptyArchive": "Downloaded archive is empty",
|
"emptyArchive": "Downloaded archive is empty",
|
||||||
"downloadFailed": "Download failed: HTTP {{status}}",
|
"downloadFailed": "Download failed: HTTP {{status}}",
|
||||||
"allBranchesFailed": "All branches failed, tried: {{branches}}",
|
"allBranchesFailed": "All branches failed, tried: {{branches}}",
|
||||||
@@ -990,7 +991,8 @@
|
|||||||
"retryLater": "Please retry later",
|
"retryLater": "Please retry later",
|
||||||
"checkRepoUrl": "Please check repository URL and branch name",
|
"checkRepoUrl": "Please check repository URL and branch name",
|
||||||
"checkDiskSpace": "Please check disk space",
|
"checkDiskSpace": "Please check disk space",
|
||||||
"checkPermission": "Please check directory permissions"
|
"checkPermission": "Please check directory permissions",
|
||||||
|
"uninstallFirst": "Please uninstall the existing skill with the same name first"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repo": {
|
"repo": {
|
||||||
|
|||||||
@@ -972,6 +972,7 @@
|
|||||||
"downloadTimeoutHint": "ネットワークを確認するか、時間をおいて再試行してください",
|
"downloadTimeoutHint": "ネットワークを確認するか、時間をおいて再試行してください",
|
||||||
"skillPathNotFound": "リポジトリ {{owner}}/{{name}} にスキルパス '{{path}}' がありません",
|
"skillPathNotFound": "リポジトリ {{owner}}/{{name}} にスキルパス '{{path}}' がありません",
|
||||||
"skillDirNotFound": "スキルディレクトリが見つかりません: {{path}}",
|
"skillDirNotFound": "スキルディレクトリが見つかりません: {{path}}",
|
||||||
|
"directoryConflict": "スキルディレクトリ '{{directory}}' は既に {{existing_repo}} で使用されています。{{new_repo}} からインストールできません",
|
||||||
"emptyArchive": "ダウンロードしたアーカイブが空です",
|
"emptyArchive": "ダウンロードしたアーカイブが空です",
|
||||||
"downloadFailed": "ダウンロードに失敗しました: HTTP {{status}}",
|
"downloadFailed": "ダウンロードに失敗しました: HTTP {{status}}",
|
||||||
"allBranchesFailed": "すべてのブランチで失敗しました。試行: {{branches}}",
|
"allBranchesFailed": "すべてのブランチで失敗しました。試行: {{branches}}",
|
||||||
@@ -990,7 +991,8 @@
|
|||||||
"retryLater": "時間をおいて再試行してください",
|
"retryLater": "時間をおいて再試行してください",
|
||||||
"checkRepoUrl": "リポジトリ URL とブランチ名を確認してください",
|
"checkRepoUrl": "リポジトリ URL とブランチ名を確認してください",
|
||||||
"checkDiskSpace": "ディスク容量を確認してください",
|
"checkDiskSpace": "ディスク容量を確認してください",
|
||||||
"checkPermission": "ディレクトリの権限を確認してください"
|
"checkPermission": "ディレクトリの権限を確認してください",
|
||||||
|
"uninstallFirst": "同名のスキルを先にアンインストールしてください"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repo": {
|
"repo": {
|
||||||
|
|||||||
@@ -972,6 +972,7 @@
|
|||||||
"downloadTimeoutHint": "请检查网络连接或稍后重试",
|
"downloadTimeoutHint": "请检查网络连接或稍后重试",
|
||||||
"skillPathNotFound": "仓库 {{owner}}/{{name}} 中未找到技能路径 '{{path}}'",
|
"skillPathNotFound": "仓库 {{owner}}/{{name}} 中未找到技能路径 '{{path}}'",
|
||||||
"skillDirNotFound": "技能目录不存在:{{path}}",
|
"skillDirNotFound": "技能目录不存在:{{path}}",
|
||||||
|
"directoryConflict": "技能目录 '{{directory}}' 已被 {{existing_repo}} 占用,无法从 {{new_repo}} 安装",
|
||||||
"emptyArchive": "下载的压缩包为空",
|
"emptyArchive": "下载的压缩包为空",
|
||||||
"downloadFailed": "下载失败:HTTP {{status}}",
|
"downloadFailed": "下载失败:HTTP {{status}}",
|
||||||
"allBranchesFailed": "所有分支下载失败,尝试了:{{branches}}",
|
"allBranchesFailed": "所有分支下载失败,尝试了:{{branches}}",
|
||||||
@@ -990,7 +991,8 @@
|
|||||||
"retryLater": "请稍后重试",
|
"retryLater": "请稍后重试",
|
||||||
"checkRepoUrl": "请检查仓库地址和分支名称",
|
"checkRepoUrl": "请检查仓库地址和分支名称",
|
||||||
"checkDiskSpace": "请检查磁盘空间",
|
"checkDiskSpace": "请检查磁盘空间",
|
||||||
"checkPermission": "请检查目录权限"
|
"checkPermission": "请检查目录权限",
|
||||||
|
"uninstallFirst": "请先卸载已安装的同名技能"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"repo": {
|
"repo": {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ function getErrorI18nKey(code: string): string {
|
|||||||
DOWNLOAD_TIMEOUT: "skills.error.downloadTimeout",
|
DOWNLOAD_TIMEOUT: "skills.error.downloadTimeout",
|
||||||
DOWNLOAD_FAILED: "skills.error.downloadFailed",
|
DOWNLOAD_FAILED: "skills.error.downloadFailed",
|
||||||
SKILL_DIR_NOT_FOUND: "skills.error.skillDirNotFound",
|
SKILL_DIR_NOT_FOUND: "skills.error.skillDirNotFound",
|
||||||
|
SKILL_DIRECTORY_CONFLICT: "skills.error.directoryConflict",
|
||||||
EMPTY_ARCHIVE: "skills.error.emptyArchive",
|
EMPTY_ARCHIVE: "skills.error.emptyArchive",
|
||||||
GET_HOME_DIR_FAILED: "skills.error.getHomeDirFailed",
|
GET_HOME_DIR_FAILED: "skills.error.getHomeDirFailed",
|
||||||
};
|
};
|
||||||
@@ -52,6 +53,7 @@ function getSuggestionI18nKey(suggestion: string): string {
|
|||||||
retryLater: "skills.error.suggestion.retryLater",
|
retryLater: "skills.error.suggestion.retryLater",
|
||||||
checkRepoUrl: "skills.error.suggestion.checkRepoUrl",
|
checkRepoUrl: "skills.error.suggestion.checkRepoUrl",
|
||||||
checkPermission: "skills.error.suggestion.checkPermission",
|
checkPermission: "skills.error.suggestion.checkPermission",
|
||||||
|
uninstallFirst: "skills.error.suggestion.uninstallFirst",
|
||||||
http403: "skills.error.http403",
|
http403: "skills.error.http403",
|
||||||
http404: "skills.error.http404",
|
http404: "skills.error.http404",
|
||||||
http429: "skills.error.http429",
|
http429: "skills.error.http429",
|
||||||
|
|||||||
Reference in New Issue
Block a user