mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-21 08:32:04 +08:00
Users need one settings surface to discover and install skills without manually navigating provider-specific directories. Add provider-backed global skill installation for Claude, Codex, Gemini, and Cursor, while keeping OpenCode read-only because it reuses other providers' skill locations. Add a responsive Skills settings tab with scoped discovery, search, refresh controls, markdown and folder uploads, upload feedback, and overflow-safe layouts. Validate bundled skill files and paths before writing them, preserve scripts and assets, and cover provider discovery and installation behavior with tests.
106 lines
3.6 KiB
TypeScript
106 lines
3.6 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
|
|
import type { AgentCategory, AgentProvider } from '../../../types/types';
|
|
|
|
import type { AgentContext, AgentsSettingsTabProps } from './types';
|
|
import AgentCategoryContentSection from './sections/AgentCategoryContentSection';
|
|
import AgentCategoryTabsSection from './sections/AgentCategoryTabsSection';
|
|
import AgentSelectorSection from './sections/AgentSelectorSection';
|
|
|
|
export default function AgentsSettingsTab({
|
|
providerAuthStatus,
|
|
onProviderLogin,
|
|
claudePermissions,
|
|
onClaudePermissionsChange,
|
|
cursorPermissions,
|
|
onCursorPermissionsChange,
|
|
codexPermissionMode,
|
|
onCodexPermissionModeChange,
|
|
geminiPermissionMode,
|
|
onGeminiPermissionModeChange,
|
|
projects,
|
|
}: AgentsSettingsTabProps) {
|
|
const [selectedAgent, setSelectedAgent] = useState<AgentProvider>('claude');
|
|
const [selectedCategory, setSelectedCategory] = useState<AgentCategory>('account');
|
|
const visibleCategories = useMemo<AgentCategory[]>(() => (
|
|
selectedAgent === 'opencode'
|
|
? ['account', 'permissions', 'mcp']
|
|
: ['account', 'permissions', 'mcp', 'skills']
|
|
), [selectedAgent]);
|
|
|
|
const visibleAgents = useMemo<AgentProvider[]>(() => {
|
|
return ['claude', 'cursor', 'codex', 'gemini', 'opencode'];
|
|
}, []);
|
|
|
|
const agentContextById = useMemo<Record<AgentProvider, AgentContext>>(() => ({
|
|
claude: {
|
|
authStatus: providerAuthStatus.claude,
|
|
onLogin: () => onProviderLogin('claude'),
|
|
},
|
|
cursor: {
|
|
authStatus: providerAuthStatus.cursor,
|
|
onLogin: () => onProviderLogin('cursor'),
|
|
},
|
|
codex: {
|
|
authStatus: providerAuthStatus.codex,
|
|
onLogin: () => onProviderLogin('codex'),
|
|
},
|
|
gemini: {
|
|
authStatus: providerAuthStatus.gemini,
|
|
onLogin: () => onProviderLogin('gemini'),
|
|
},
|
|
opencode: {
|
|
authStatus: providerAuthStatus.opencode,
|
|
onLogin: () => onProviderLogin('opencode'),
|
|
},
|
|
}), [
|
|
onProviderLogin,
|
|
providerAuthStatus.claude,
|
|
providerAuthStatus.codex,
|
|
providerAuthStatus.cursor,
|
|
providerAuthStatus.gemini,
|
|
providerAuthStatus.opencode,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (!visibleCategories.includes(selectedCategory)) {
|
|
setSelectedCategory(visibleCategories[0] ?? 'account');
|
|
}
|
|
}, [selectedCategory, visibleCategories]);
|
|
|
|
return (
|
|
<div className="-mx-4 -mb-4 -mt-2 flex min-h-[300px] min-w-0 flex-col overflow-hidden md:-mx-6 md:-mb-6 md:-mt-2 md:min-h-[500px]">
|
|
<AgentSelectorSection
|
|
agents={visibleAgents}
|
|
selectedAgent={selectedAgent}
|
|
onSelectAgent={setSelectedAgent}
|
|
agentContextById={agentContextById}
|
|
/>
|
|
|
|
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
|
<AgentCategoryTabsSection
|
|
categories={visibleCategories}
|
|
selectedAgent={selectedAgent}
|
|
selectedCategory={selectedCategory}
|
|
onSelectCategory={setSelectedCategory}
|
|
/>
|
|
|
|
<AgentCategoryContentSection
|
|
selectedAgent={selectedAgent}
|
|
selectedCategory={selectedCategory}
|
|
agentContextById={agentContextById}
|
|
claudePermissions={claudePermissions}
|
|
onClaudePermissionsChange={onClaudePermissionsChange}
|
|
cursorPermissions={cursorPermissions}
|
|
onCursorPermissionsChange={onCursorPermissionsChange}
|
|
codexPermissionMode={codexPermissionMode}
|
|
onCodexPermissionModeChange={onCodexPermissionModeChange}
|
|
geminiPermissionMode={geminiPermissionMode}
|
|
onGeminiPermissionModeChange={onGeminiPermissionModeChange}
|
|
projects={projects}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|