mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 01:25:33 +08:00
Merge origin/main into main
Resolved conflict in src-tauri/src/commands/misc.rs by combining imports from both sides. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+102
-34
@@ -13,6 +13,8 @@ import {
|
||||
Wrench,
|
||||
Server,
|
||||
RefreshCw,
|
||||
Search,
|
||||
Download,
|
||||
} from "lucide-react";
|
||||
import type { Provider } from "@/types";
|
||||
import type { EnvConflict } from "@/types/env";
|
||||
@@ -26,6 +28,7 @@ import {
|
||||
import { checkAllEnvConflicts, checkEnvConflicts } from "@/lib/api/env";
|
||||
import { useProviderActions } from "@/hooks/useProviderActions";
|
||||
import { useProxyStatus } from "@/hooks/useProxyStatus";
|
||||
import { useLastValidValue } from "@/hooks/useLastValidValue";
|
||||
import { extractErrorMessage } from "@/utils/errorUtils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AppSwitcher } from "@/components/AppSwitcher";
|
||||
@@ -41,6 +44,7 @@ import UsageScriptModal from "@/components/UsageScriptModal";
|
||||
import UnifiedMcpPanel from "@/components/mcp/UnifiedMcpPanel";
|
||||
import PromptPanel from "@/components/prompts/PromptPanel";
|
||||
import { SkillsPage } from "@/components/skills/SkillsPage";
|
||||
import UnifiedSkillsPanel from "@/components/skills/UnifiedSkillsPanel";
|
||||
import { DeepLinkImportDialog } from "@/components/DeepLinkImportDialog";
|
||||
import { AgentsPanel } from "@/components/agents/AgentsPanel";
|
||||
import { UniversalProviderPanel } from "@/components/universal";
|
||||
@@ -51,6 +55,7 @@ type View =
|
||||
| "settings"
|
||||
| "prompts"
|
||||
| "skills"
|
||||
| "skillsDiscovery"
|
||||
| "mcp"
|
||||
| "agents"
|
||||
| "universal";
|
||||
@@ -73,25 +78,14 @@ function App() {
|
||||
const [envConflicts, setEnvConflicts] = useState<EnvConflict[]>([]);
|
||||
const [showEnvBanner, setShowEnvBanner] = useState(false);
|
||||
|
||||
// 保存最后一个有效的 provider,用于动画退出期间显示内容
|
||||
const lastUsageProviderRef = useRef<Provider | null>(null);
|
||||
const lastEditingProviderRef = useRef<Provider | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (usageProvider) {
|
||||
lastUsageProviderRef.current = usageProvider;
|
||||
}
|
||||
}, [usageProvider]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editingProvider) {
|
||||
lastEditingProviderRef.current = editingProvider;
|
||||
}
|
||||
}, [editingProvider]);
|
||||
// 使用 Hook 保存最后有效值,用于动画退出期间保持内容显示
|
||||
const effectiveEditingProvider = useLastValidValue(editingProvider);
|
||||
const effectiveUsageProvider = useLastValidValue(usageProvider);
|
||||
|
||||
const promptPanelRef = useRef<any>(null);
|
||||
const mcpPanelRef = useRef<any>(null);
|
||||
const skillsPageRef = useRef<any>(null);
|
||||
const unifiedSkillsPanelRef = useRef<any>(null);
|
||||
const addActionButtonClass =
|
||||
"bg-orange-500 hover:bg-orange-600 dark:bg-orange-500 dark:hover:bg-orange-600 text-white shadow-lg shadow-orange-500/30 dark:shadow-orange-500/40 rounded-full w-8 h-8";
|
||||
|
||||
@@ -117,8 +111,7 @@ function App() {
|
||||
});
|
||||
const providers = useMemo(() => data?.providers ?? {}, [data]);
|
||||
const currentProviderId = data?.currentProviderId ?? "";
|
||||
// Skills 功能仅支持 Claude 和 Codex
|
||||
const hasSkillsSupport = activeApp === "claude" || activeApp === "codex";
|
||||
const hasSkillsSupport = true;
|
||||
|
||||
// 🎯 使用 useProviderActions Hook 统一管理所有 Provider 操作
|
||||
const {
|
||||
@@ -229,6 +222,35 @@ function App() {
|
||||
checkMigration();
|
||||
}, [t]);
|
||||
|
||||
// 应用启动时检查是否刚完成了 Skills 自动导入(统一管理 SSOT)
|
||||
useEffect(() => {
|
||||
const checkSkillsMigration = async () => {
|
||||
try {
|
||||
const result = await invoke<{ count: number; error?: string } | null>(
|
||||
"get_skills_migration_result",
|
||||
);
|
||||
if (result?.error) {
|
||||
toast.error(t("migration.skillsFailed"), {
|
||||
description: t("migration.skillsFailedDescription"),
|
||||
closeButton: true,
|
||||
});
|
||||
console.error("[App] Skills SSOT migration failed:", result.error);
|
||||
return;
|
||||
}
|
||||
if (result && result.count > 0) {
|
||||
toast.success(t("migration.skillsSuccess", { count: result.count }), {
|
||||
closeButton: true,
|
||||
});
|
||||
await queryClient.invalidateQueries({ queryKey: ["skills"] });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[App] Failed to check skills migration result:", error);
|
||||
}
|
||||
};
|
||||
|
||||
checkSkillsMigration();
|
||||
}, [t, queryClient]);
|
||||
|
||||
// 切换应用时检测当前应用的环境变量冲突
|
||||
useEffect(() => {
|
||||
const checkEnvOnSwitch = async () => {
|
||||
@@ -421,10 +443,16 @@ function App() {
|
||||
/>
|
||||
);
|
||||
case "skills":
|
||||
return (
|
||||
<UnifiedSkillsPanel
|
||||
ref={unifiedSkillsPanelRef}
|
||||
onOpenDiscovery={() => setCurrentView("skillsDiscovery")}
|
||||
/>
|
||||
);
|
||||
case "skillsDiscovery":
|
||||
return (
|
||||
<SkillsPage
|
||||
ref={skillsPageRef}
|
||||
onClose={() => setCurrentView("providers")}
|
||||
initialApp={activeApp}
|
||||
/>
|
||||
);
|
||||
@@ -564,7 +592,11 @@ function App() {
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => setCurrentView("providers")}
|
||||
onClick={() =>
|
||||
setCurrentView(
|
||||
currentView === "skillsDiscovery" ? "skills" : "providers",
|
||||
)
|
||||
}
|
||||
className="mr-2 rounded-lg"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
@@ -574,6 +606,7 @@ function App() {
|
||||
{currentView === "prompts" &&
|
||||
t("prompts.title", { appName: t(`apps.${activeApp}`) })}
|
||||
{currentView === "skills" && t("skills.title")}
|
||||
{currentView === "skillsDiscovery" && t("skills.title")}
|
||||
{currentView === "mcp" && t("mcp.unifiedPanel.title")}
|
||||
{currentView === "agents" && t("agents.title")}
|
||||
{currentView === "universal" &&
|
||||
@@ -619,25 +652,60 @@ function App() {
|
||||
>
|
||||
{currentView === "prompts" && (
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => promptPanelRef.current?.openAdd()}
|
||||
className={`ml-auto ${addActionButtonClass}`}
|
||||
title={t("prompts.add")}
|
||||
className="hover:bg-black/5 dark:hover:bg-white/5"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
{t("prompts.add")}
|
||||
</Button>
|
||||
)}
|
||||
{currentView === "mcp" && (
|
||||
<Button
|
||||
size="icon"
|
||||
onClick={() => mcpPanelRef.current?.openAdd()}
|
||||
className={`ml-auto ${addActionButtonClass}`}
|
||||
title={t("mcp.unifiedPanel.addServer")}
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => mcpPanelRef.current?.openImport()}
|
||||
className="hover:bg-black/5 dark:hover:bg-white/5"
|
||||
>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
{t("mcp.importExisting")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => mcpPanelRef.current?.openAdd()}
|
||||
className="hover:bg-black/5 dark:hover:bg-white/5"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
{t("mcp.addMcp")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{currentView === "skills" && (
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => unifiedSkillsPanelRef.current?.openImport()}
|
||||
className="hover:bg-black/5 dark:hover:bg-white/5"
|
||||
>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
{t("skills.import")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setCurrentView("skillsDiscovery")}
|
||||
className="hover:bg-black/5 dark:hover:bg-white/5"
|
||||
>
|
||||
<Search className="w-4 h-4 mr-2" />
|
||||
{t("skills.discover")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{currentView === "skillsDiscovery" && (
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -739,7 +807,7 @@ function App() {
|
||||
|
||||
<EditProviderDialog
|
||||
open={Boolean(editingProvider)}
|
||||
provider={lastEditingProviderRef.current}
|
||||
provider={effectiveEditingProvider}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setEditingProvider(null);
|
||||
@@ -750,9 +818,9 @@ function App() {
|
||||
isProxyTakeover={isProxyRunning && isCurrentAppTakeoverActive}
|
||||
/>
|
||||
|
||||
{lastUsageProviderRef.current && (
|
||||
{effectiveUsageProvider && (
|
||||
<UsageScriptModal
|
||||
provider={lastUsageProviderRef.current}
|
||||
provider={effectiveUsageProvider}
|
||||
appId={activeApp}
|
||||
isOpen={Boolean(usageProvider)}
|
||||
onClose={() => setUsageProvider(null)}
|
||||
|
||||
Reference in New Issue
Block a user