diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx deleted file mode 100644 index 9517257..0000000 --- a/src/components/Settings.jsx +++ /dev/null @@ -1,1977 +0,0 @@ -import { useState, useEffect } from 'react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Badge } from './ui/badge'; -import { X, Plus, Settings as SettingsIcon, Shield, AlertTriangle, Moon, Sun, Server, Edit3, Trash2, Globe, Terminal, Zap, FolderOpen, LogIn, Key, GitBranch, Check } from 'lucide-react'; -import { useTheme } from '../contexts/ThemeContext'; -import { useTranslation } from 'react-i18next'; -import CredentialsSettings from './CredentialsSettings'; -import GitSettings from './GitSettings'; -import TasksSettings from './TasksSettings'; -import LoginModal from './LoginModal'; -import { authenticatedFetch } from '../utils/api'; - -// New settings components -import AgentListItem from './settings/AgentListItem'; -import AccountContent from './settings/AccountContent'; -import PermissionsContent from './settings/PermissionsContent'; -import McpServersContent from './settings/McpServersContent'; -import LanguageSelector from './LanguageSelector'; - -function Settings({ isOpen, onClose, projects = [], initialTab = 'agents' }) { - const { isDarkMode, toggleDarkMode } = useTheme(); - const { t } = useTranslation('settings'); - const [allowedTools, setAllowedTools] = useState([]); - const [disallowedTools, setDisallowedTools] = useState([]); - const [newAllowedTool, setNewAllowedTool] = useState(''); - const [newDisallowedTool, setNewDisallowedTool] = useState(''); - const [skipPermissions, setSkipPermissions] = useState(false); - const [isSaving, setIsSaving] = useState(false); - const [saveStatus, setSaveStatus] = useState(null); - const [projectSortOrder, setProjectSortOrder] = useState('name'); - - const [mcpServers, setMcpServers] = useState([]); - const [showMcpForm, setShowMcpForm] = useState(false); - const [editingMcpServer, setEditingMcpServer] = useState(null); - const [mcpFormData, setMcpFormData] = useState({ - name: '', - type: 'stdio', - scope: 'user', - projectPath: '', // For local scope - config: { - command: '', - args: [], - env: {}, - url: '', - headers: {}, - timeout: 30000 - }, - jsonInput: '', // For JSON import - importMode: 'form' // 'form' or 'json' - }); - const [mcpLoading, setMcpLoading] = useState(false); - const [mcpTestResults, setMcpTestResults] = useState({}); - const [mcpServerTools, setMcpServerTools] = useState({}); - const [mcpToolsLoading, setMcpToolsLoading] = useState({}); - const [activeTab, setActiveTab] = useState(initialTab); - const [jsonValidationError, setJsonValidationError] = useState(''); - const [selectedAgent, setSelectedAgent] = useState('claude'); // 'claude', 'cursor', or 'codex' - const [selectedCategory, setSelectedCategory] = useState('account'); // 'account', 'permissions', or 'mcp' - - // Code Editor settings - const [codeEditorTheme, setCodeEditorTheme] = useState(() => - localStorage.getItem('codeEditorTheme') || 'dark' - ); - const [codeEditorWordWrap, setCodeEditorWordWrap] = useState(() => - localStorage.getItem('codeEditorWordWrap') === 'true' - ); - const [codeEditorShowMinimap, setCodeEditorShowMinimap] = useState(() => - localStorage.getItem('codeEditorShowMinimap') !== 'false' // Default true - ); - const [codeEditorLineNumbers, setCodeEditorLineNumbers] = useState(() => - localStorage.getItem('codeEditorLineNumbers') !== 'false' // Default true - ); - const [codeEditorFontSize, setCodeEditorFontSize] = useState(() => - localStorage.getItem('codeEditorFontSize') || '14' - ); - - // Cursor-specific states - const [cursorAllowedCommands, setCursorAllowedCommands] = useState([]); - const [cursorDisallowedCommands, setCursorDisallowedCommands] = useState([]); - const [cursorSkipPermissions, setCursorSkipPermissions] = useState(false); - const [newCursorCommand, setNewCursorCommand] = useState(''); - const [newCursorDisallowedCommand, setNewCursorDisallowedCommand] = useState(''); - const [cursorMcpServers, setCursorMcpServers] = useState([]); - - // Codex-specific states - const [codexMcpServers, setCodexMcpServers] = useState([]); - const [codexPermissionMode, setCodexPermissionMode] = useState('default'); - const [showCodexMcpForm, setShowCodexMcpForm] = useState(false); - const [codexMcpFormData, setCodexMcpFormData] = useState({ - name: '', - type: 'stdio', - config: { - command: '', - args: [], - env: {} - } - }); - const [editingCodexMcpServer, setEditingCodexMcpServer] = useState(null); - const [codexMcpLoading, setCodexMcpLoading] = useState(false); - - const [showLoginModal, setShowLoginModal] = useState(false); - const [loginProvider, setLoginProvider] = useState(''); - const [selectedProject, setSelectedProject] = useState(null); - - const [claudeAuthStatus, setClaudeAuthStatus] = useState({ - authenticated: false, - email: null, - loading: true, - error: null - }); - const [cursorAuthStatus, setCursorAuthStatus] = useState({ - authenticated: false, - email: null, - loading: true, - error: null - }); - const [codexAuthStatus, setCodexAuthStatus] = useState({ - authenticated: false, - email: null, - loading: true, - error: null - }); - - // Common tool patterns for Claude - const commonTools = [ - 'Bash(git log:*)', - 'Bash(git diff:*)', - 'Bash(git status:*)', - 'Write', - 'Read', - 'Edit', - 'Glob', - 'Grep', - 'MultiEdit', - 'Task', - 'TodoWrite', - 'TodoRead', - 'WebFetch', - 'WebSearch' - ]; - - // Common shell commands for Cursor - const commonCursorCommands = [ - 'Shell(ls)', - 'Shell(mkdir)', - 'Shell(cd)', - 'Shell(cat)', - 'Shell(echo)', - 'Shell(git status)', - 'Shell(git diff)', - 'Shell(git log)', - 'Shell(npm install)', - 'Shell(npm run)', - 'Shell(python)', - 'Shell(node)' - ]; - - // Fetch Cursor MCP servers - const fetchCursorMcpServers = async () => { - try { - const response = await authenticatedFetch('/api/cursor/mcp'); - - if (response.ok) { - const data = await response.json(); - setCursorMcpServers(data.servers || []); - } else { - console.error('Failed to fetch Cursor MCP servers'); - } - } catch (error) { - console.error('Error fetching Cursor MCP servers:', error); - } - }; - - const fetchCodexMcpServers = async () => { - try { - const configResponse = await authenticatedFetch('/api/codex/mcp/config/read'); - - if (configResponse.ok) { - const configData = await configResponse.json(); - if (configData.success && configData.servers) { - setCodexMcpServers(configData.servers); - return; - } - } - - const cliResponse = await authenticatedFetch('/api/codex/mcp/cli/list'); - - if (cliResponse.ok) { - const cliData = await cliResponse.json(); - if (cliData.success && cliData.servers) { - const servers = cliData.servers.map(server => ({ - id: server.name, - name: server.name, - type: server.type || 'stdio', - scope: 'user', - config: { - command: server.command || '', - args: server.args || [], - env: server.env || {} - } - })); - setCodexMcpServers(servers); - } - } - } catch (error) { - console.error('Error fetching Codex MCP servers:', error); - } - }; - - // MCP API functions - const fetchMcpServers = async () => { - try { - // Try to read directly from config files for complete details - const configResponse = await authenticatedFetch('/api/mcp/config/read'); - - if (configResponse.ok) { - const configData = await configResponse.json(); - if (configData.success && configData.servers) { - setMcpServers(configData.servers); - return; - } - } - - // Fallback to Claude CLI - const cliResponse = await authenticatedFetch('/api/mcp/cli/list'); - - if (cliResponse.ok) { - const cliData = await cliResponse.json(); - if (cliData.success && cliData.servers) { - // Convert CLI format to our format - const servers = cliData.servers.map(server => ({ - id: server.name, - name: server.name, - type: server.type, - scope: 'user', - config: { - command: server.command || '', - args: server.args || [], - env: server.env || {}, - url: server.url || '', - headers: server.headers || {}, - timeout: 30000 - }, - created: new Date().toISOString(), - updated: new Date().toISOString() - })); - setMcpServers(servers); - return; - } - } - - // Final fallback to direct config reading - const response = await authenticatedFetch('/api/mcp/servers?scope=user'); - - if (response.ok) { - const data = await response.json(); - setMcpServers(data.servers || []); - } else { - console.error('Failed to fetch MCP servers'); - } - } catch (error) { - console.error('Error fetching MCP servers:', error); - } - }; - - const saveMcpServer = async (serverData) => { - try { - if (editingMcpServer) { - // For editing, remove old server and add new one - await deleteMcpServer(editingMcpServer.id, 'user'); - } - - // Use Claude CLI to add the server - const response = await authenticatedFetch('/api/mcp/cli/add', { - method: 'POST', - body: JSON.stringify({ - name: serverData.name, - type: serverData.type, - scope: serverData.scope, - projectPath: serverData.projectPath, - command: serverData.config?.command, - args: serverData.config?.args || [], - url: serverData.config?.url, - headers: serverData.config?.headers || {}, - env: serverData.config?.env || {} - }) - }); - - if (response.ok) { - const result = await response.json(); - if (result.success) { - await fetchMcpServers(); // Refresh the list - return true; - } else { - throw new Error(result.error || 'Failed to save server via Claude CLI'); - } - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to save server'); - } - } catch (error) { - console.error('Error saving MCP server:', error); - throw error; - } - }; - - const deleteMcpServer = async (serverId, scope = 'user') => { - try { - // Use Claude CLI to remove the server with proper scope - const response = await authenticatedFetch(`/api/mcp/cli/remove/${serverId}?scope=${scope}`, { - method: 'DELETE' - }); - - if (response.ok) { - const result = await response.json(); - if (result.success) { - await fetchMcpServers(); // Refresh the list - return true; - } else { - throw new Error(result.error || 'Failed to delete server via Claude CLI'); - } - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to delete server'); - } - } catch (error) { - console.error('Error deleting MCP server:', error); - throw error; - } - }; - - const testMcpServer = async (serverId, scope = 'user') => { - try { - const response = await authenticatedFetch(`/api/mcp/servers/${serverId}/test?scope=${scope}`, { - method: 'POST' - }); - - if (response.ok) { - const data = await response.json(); - return data.testResult; - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to test server'); - } - } catch (error) { - console.error('Error testing MCP server:', error); - throw error; - } - }; - - - const discoverMcpTools = async (serverId, scope = 'user') => { - try { - const response = await authenticatedFetch(`/api/mcp/servers/${serverId}/tools?scope=${scope}`, { - method: 'POST' - }); - - if (response.ok) { - const data = await response.json(); - return data.toolsResult; - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to discover tools'); - } - } catch (error) { - console.error('Error discovering MCP tools:', error); - throw error; - } - }; - - const saveCodexMcpServer = async (serverData) => { - try { - if (editingCodexMcpServer) { - await deleteCodexMcpServer(editingCodexMcpServer.id); - } - - const response = await authenticatedFetch('/api/codex/mcp/cli/add', { - method: 'POST', - body: JSON.stringify({ - name: serverData.name, - command: serverData.config?.command, - args: serverData.config?.args || [], - env: serverData.config?.env || {} - }) - }); - - if (response.ok) { - const result = await response.json(); - if (result.success) { - await fetchCodexMcpServers(); - return true; - } else { - throw new Error(result.error || 'Failed to save Codex MCP server'); - } - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to save server'); - } - } catch (error) { - console.error('Error saving Codex MCP server:', error); - throw error; - } - }; - - const deleteCodexMcpServer = async (serverId) => { - try { - const response = await authenticatedFetch(`/api/codex/mcp/cli/remove/${serverId}`, { - method: 'DELETE' - }); - - if (response.ok) { - const result = await response.json(); - if (result.success) { - await fetchCodexMcpServers(); - return true; - } else { - throw new Error(result.error || 'Failed to delete Codex MCP server'); - } - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to delete server'); - } - } catch (error) { - console.error('Error deleting Codex MCP server:', error); - throw error; - } - }; - - const resetCodexMcpForm = () => { - setCodexMcpFormData({ - name: '', - type: 'stdio', - config: { - command: '', - args: [], - env: {} - } - }); - setEditingCodexMcpServer(null); - setShowCodexMcpForm(false); - }; - - const openCodexMcpForm = (server = null) => { - if (server) { - setEditingCodexMcpServer(server); - setCodexMcpFormData({ - name: server.name, - type: server.type || 'stdio', - config: { - command: server.config?.command || '', - args: server.config?.args || [], - env: server.config?.env || {} - } - }); - } else { - resetCodexMcpForm(); - } - setShowCodexMcpForm(true); - }; - - const handleCodexMcpSubmit = async (e) => { - e.preventDefault(); - setCodexMcpLoading(true); - - try { - if (editingCodexMcpServer) { - // Delete old server first, then add new one - await deleteCodexMcpServer(editingCodexMcpServer.name); - } - await saveCodexMcpServer(codexMcpFormData); - resetCodexMcpForm(); - setSaveStatus('success'); - } catch (error) { - alert(`Error: ${error.message}`); - setSaveStatus('error'); - } finally { - setCodexMcpLoading(false); - } - }; - - const handleCodexMcpDelete = async (serverName) => { - if (confirm('Are you sure you want to delete this MCP server?')) { - try { - await deleteCodexMcpServer(serverName); - setSaveStatus('success'); - } catch (error) { - alert(`Error: ${error.message}`); - setSaveStatus('error'); - } - } - }; - - useEffect(() => { - if (isOpen) { - loadSettings(); - checkClaudeAuthStatus(); - checkCursorAuthStatus(); - checkCodexAuthStatus(); - setActiveTab(initialTab); - } - }, [isOpen, initialTab]); - - // Persist code editor settings to localStorage - useEffect(() => { - localStorage.setItem('codeEditorTheme', codeEditorTheme); - window.dispatchEvent(new Event('codeEditorSettingsChanged')); - }, [codeEditorTheme]); - - useEffect(() => { - localStorage.setItem('codeEditorWordWrap', codeEditorWordWrap.toString()); - window.dispatchEvent(new Event('codeEditorSettingsChanged')); - }, [codeEditorWordWrap]); - - useEffect(() => { - localStorage.setItem('codeEditorShowMinimap', codeEditorShowMinimap.toString()); - window.dispatchEvent(new Event('codeEditorSettingsChanged')); - }, [codeEditorShowMinimap]); - - useEffect(() => { - localStorage.setItem('codeEditorLineNumbers', codeEditorLineNumbers.toString()); - window.dispatchEvent(new Event('codeEditorSettingsChanged')); - }, [codeEditorLineNumbers]); - - useEffect(() => { - localStorage.setItem('codeEditorFontSize', codeEditorFontSize); - window.dispatchEvent(new Event('codeEditorSettingsChanged')); - }, [codeEditorFontSize]); - - const loadSettings = async () => { - try { - - // Load Claude settings from localStorage - const savedSettings = localStorage.getItem('claude-settings'); - - if (savedSettings) { - const settings = JSON.parse(savedSettings); - setAllowedTools(settings.allowedTools || []); - setDisallowedTools(settings.disallowedTools || []); - setSkipPermissions(settings.skipPermissions || false); - setProjectSortOrder(settings.projectSortOrder || 'name'); - } else { - // Set defaults - setAllowedTools([]); - setDisallowedTools([]); - setSkipPermissions(false); - setProjectSortOrder('name'); - } - - // Load Cursor settings from localStorage - const savedCursorSettings = localStorage.getItem('cursor-tools-settings'); - - if (savedCursorSettings) { - const cursorSettings = JSON.parse(savedCursorSettings); - setCursorAllowedCommands(cursorSettings.allowedCommands || []); - setCursorDisallowedCommands(cursorSettings.disallowedCommands || []); - setCursorSkipPermissions(cursorSettings.skipPermissions || false); - } else { - // Set Cursor defaults - setCursorAllowedCommands([]); - setCursorDisallowedCommands([]); - setCursorSkipPermissions(false); - } - - // Load Codex settings from localStorage - const savedCodexSettings = localStorage.getItem('codex-settings'); - - if (savedCodexSettings) { - const codexSettings = JSON.parse(savedCodexSettings); - setCodexPermissionMode(codexSettings.permissionMode || 'default'); - } else { - setCodexPermissionMode('default'); - } - - // Load MCP servers from API - await fetchMcpServers(); - - // Load Cursor MCP servers - await fetchCursorMcpServers(); - - // Load Codex MCP servers - await fetchCodexMcpServers(); - } catch (error) { - console.error('Error loading tool settings:', error); - setAllowedTools([]); - setDisallowedTools([]); - setSkipPermissions(false); - setProjectSortOrder('name'); - } - }; - - const checkClaudeAuthStatus = async () => { - try { - const response = await authenticatedFetch('/api/cli/claude/status'); - - if (response.ok) { - const data = await response.json(); - setClaudeAuthStatus({ - authenticated: data.authenticated, - email: data.email, - loading: false, - error: data.error || null - }); - } else { - setClaudeAuthStatus({ - authenticated: false, - email: null, - loading: false, - error: 'Failed to check authentication status' - }); - } - } catch (error) { - console.error('Error checking Claude auth status:', error); - setClaudeAuthStatus({ - authenticated: false, - email: null, - loading: false, - error: error.message - }); - } - }; - - const checkCursorAuthStatus = async () => { - try { - const response = await authenticatedFetch('/api/cli/cursor/status'); - - if (response.ok) { - const data = await response.json(); - setCursorAuthStatus({ - authenticated: data.authenticated, - email: data.email, - loading: false, - error: data.error || null - }); - } else { - setCursorAuthStatus({ - authenticated: false, - email: null, - loading: false, - error: 'Failed to check authentication status' - }); - } - } catch (error) { - console.error('Error checking Cursor auth status:', error); - setCursorAuthStatus({ - authenticated: false, - email: null, - loading: false, - error: error.message - }); - } - }; - - const checkCodexAuthStatus = async () => { - try { - const response = await authenticatedFetch('/api/cli/codex/status'); - - if (response.ok) { - const data = await response.json(); - setCodexAuthStatus({ - authenticated: data.authenticated, - email: data.email, - loading: false, - error: data.error || null - }); - } else { - setCodexAuthStatus({ - authenticated: false, - email: null, - loading: false, - error: 'Failed to check authentication status' - }); - } - } catch (error) { - console.error('Error checking Codex auth status:', error); - setCodexAuthStatus({ - authenticated: false, - email: null, - loading: false, - error: error.message - }); - } - }; - - const handleClaudeLogin = () => { - setLoginProvider('claude'); - setSelectedProject(projects?.[0] || { name: 'default', fullPath: process.cwd() }); - setShowLoginModal(true); - }; - - const handleCursorLogin = () => { - setLoginProvider('cursor'); - setSelectedProject(projects?.[0] || { name: 'default', fullPath: process.cwd() }); - setShowLoginModal(true); - }; - - const handleCodexLogin = () => { - setLoginProvider('codex'); - setSelectedProject(projects?.[0] || { name: 'default', fullPath: process.cwd() }); - setShowLoginModal(true); - }; - - const handleLoginComplete = (exitCode) => { - if (exitCode === 0) { - setSaveStatus('success'); - - if (loginProvider === 'claude') { - checkClaudeAuthStatus(); - } else if (loginProvider === 'cursor') { - checkCursorAuthStatus(); - } else if (loginProvider === 'codex') { - checkCodexAuthStatus(); - } - } - }; - - const saveSettings = () => { - setIsSaving(true); - setSaveStatus(null); - - try { - // Save Claude settings - const claudeSettings = { - allowedTools, - disallowedTools, - skipPermissions, - projectSortOrder, - lastUpdated: new Date().toISOString() - }; - - // Save Cursor settings - const cursorSettings = { - allowedCommands: cursorAllowedCommands, - disallowedCommands: cursorDisallowedCommands, - skipPermissions: cursorSkipPermissions, - lastUpdated: new Date().toISOString() - }; - - // Save Codex settings - const codexSettings = { - permissionMode: codexPermissionMode, - lastUpdated: new Date().toISOString() - }; - - // Save to localStorage - localStorage.setItem('claude-settings', JSON.stringify(claudeSettings)); - localStorage.setItem('cursor-tools-settings', JSON.stringify(cursorSettings)); - localStorage.setItem('codex-settings', JSON.stringify(codexSettings)); - - setSaveStatus('success'); - - setTimeout(() => { - onClose(); - }, 1000); - } catch (error) { - console.error('Error saving tool settings:', error); - setSaveStatus('error'); - } finally { - setIsSaving(false); - } - }; - - const addAllowedTool = (tool) => { - if (tool && !allowedTools.includes(tool)) { - setAllowedTools([...allowedTools, tool]); - setNewAllowedTool(''); - } - }; - - const removeAllowedTool = (tool) => { - setAllowedTools(allowedTools.filter(t => t !== tool)); - }; - - const addDisallowedTool = (tool) => { - if (tool && !disallowedTools.includes(tool)) { - setDisallowedTools([...disallowedTools, tool]); - setNewDisallowedTool(''); - } - }; - - const removeDisallowedTool = (tool) => { - setDisallowedTools(disallowedTools.filter(t => t !== tool)); - }; - - // MCP form handling functions - const resetMcpForm = () => { - setMcpFormData({ - name: '', - type: 'stdio', - scope: 'user', // Default to user scope - projectPath: '', - config: { - command: '', - args: [], - env: {}, - url: '', - headers: {}, - timeout: 30000 - }, - jsonInput: '', - importMode: 'form' - }); - setEditingMcpServer(null); - setShowMcpForm(false); - setJsonValidationError(''); - }; - - const openMcpForm = (server = null) => { - if (server) { - setEditingMcpServer(server); - setMcpFormData({ - name: server.name, - type: server.type, - scope: server.scope, - projectPath: server.projectPath || '', - config: { ...server.config }, - raw: server.raw, // Store raw config for display - importMode: 'form', // Always use form mode when editing - jsonInput: '' - }); - } else { - resetMcpForm(); - } - setShowMcpForm(true); - }; - - const handleMcpSubmit = async (e) => { - e.preventDefault(); - - setMcpLoading(true); - - try { - if (mcpFormData.importMode === 'json') { - // Use JSON import endpoint - const response = await authenticatedFetch('/api/mcp/cli/add-json', { - method: 'POST', - body: JSON.stringify({ - name: mcpFormData.name, - jsonConfig: mcpFormData.jsonInput, - scope: mcpFormData.scope, - projectPath: mcpFormData.projectPath - }) - }); - - if (response.ok) { - const result = await response.json(); - if (result.success) { - await fetchMcpServers(); // Refresh the list - resetMcpForm(); - setSaveStatus('success'); - } else { - throw new Error(result.error || 'Failed to add server via JSON'); - } - } else { - const error = await response.json(); - throw new Error(error.error || 'Failed to add server'); - } - } else { - // Use regular form-based save - await saveMcpServer(mcpFormData); - resetMcpForm(); - setSaveStatus('success'); - } - } catch (error) { - alert(`Error: ${error.message}`); - setSaveStatus('error'); - } finally { - setMcpLoading(false); - } - }; - - const handleMcpDelete = async (serverId, scope) => { - if (confirm('Are you sure you want to delete this MCP server?')) { - try { - await deleteMcpServer(serverId, scope); - setSaveStatus('success'); - } catch (error) { - alert(`Error: ${error.message}`); - setSaveStatus('error'); - } - } - }; - - const handleMcpTest = async (serverId, scope) => { - try { - setMcpTestResults({ ...mcpTestResults, [serverId]: { loading: true } }); - const result = await testMcpServer(serverId, scope); - setMcpTestResults({ ...mcpTestResults, [serverId]: result }); - } catch (error) { - setMcpTestResults({ - ...mcpTestResults, - [serverId]: { - success: false, - message: error.message, - details: [] - } - }); - } - }; - - const handleMcpToolsDiscovery = async (serverId, scope) => { - try { - setMcpToolsLoading({ ...mcpToolsLoading, [serverId]: true }); - const result = await discoverMcpTools(serverId, scope); - setMcpServerTools({ ...mcpServerTools, [serverId]: result }); - } catch (error) { - setMcpServerTools({ - ...mcpServerTools, - [serverId]: { - success: false, - tools: [], - resources: [], - prompts: [] - } - }); - } finally { - setMcpToolsLoading({ ...mcpToolsLoading, [serverId]: false }); - } - }; - - const updateMcpConfig = (key, value) => { - setMcpFormData(prev => ({ - ...prev, - config: { - ...prev.config, - [key]: value - } - })); - }; - - - const getTransportIcon = (type) => { - switch (type) { - case 'stdio': return ; - case 'sse': return ; - case 'http': return ; - default: return ; - } - }; - - if (!isOpen) return null; - - return ( -
-
-
-
- -

- {t('title')} -

-
- -
- -
- {/* Tab Navigation */} -
-
- - - - - -
-
- -
- - {/* Appearance Tab */} - {activeTab === 'appearance' && ( -
- {activeTab === 'appearance' && ( -
- {/* Theme Settings */} -
-
-
-
-
- {t('appearanceSettings.darkMode.label')} -
-
- {t('appearanceSettings.darkMode.description')} -
-
- -
-
-
- - {/* Language Selector */} -
- -
- - {/* Project Sorting */} -
-
-
-
-
- {t('appearanceSettings.projectSorting.label')} -
-
- {t('appearanceSettings.projectSorting.description')} -
-
- -
-
-
- - {/* Code Editor Settings */} -
-

{t('appearanceSettings.codeEditor.title')}

- - {/* Editor Theme */} -
-
-
-
- {t('appearanceSettings.codeEditor.theme.label')} -
-
- {t('appearanceSettings.codeEditor.theme.description')} -
-
- -
-
- - {/* Word Wrap */} -
-
-
-
- {t('appearanceSettings.codeEditor.wordWrap.label')} -
-
- {t('appearanceSettings.codeEditor.wordWrap.description')} -
-
- -
-
- - {/* Show Minimap */} -
-
-
-
- {t('appearanceSettings.codeEditor.showMinimap.label')} -
-
- {t('appearanceSettings.codeEditor.showMinimap.description')} -
-
- -
-
- - {/* Show Line Numbers */} -
-
-
-
- {t('appearanceSettings.codeEditor.lineNumbers.label')} -
-
- {t('appearanceSettings.codeEditor.lineNumbers.description')} -
-
- -
-
- - {/* Font Size */} -
-
-
-
- {t('appearanceSettings.codeEditor.fontSize.label')} -
-
- {t('appearanceSettings.codeEditor.fontSize.description')} -
-
- -
-
-
-
-)} - -
- )} - - {/* Git Tab */} - {activeTab === 'git' && } - - {/* Agents Tab */} - {activeTab === 'agents' && ( -
- {/* Mobile: Horizontal Agent Tabs */} -
-
- setSelectedAgent('claude')} - isMobile={true} - /> - setSelectedAgent('cursor')} - isMobile={true} - /> - setSelectedAgent('codex')} - isMobile={true} - /> -
-
- - {/* Desktop: Sidebar - Agent List */} -
-
- setSelectedAgent('claude')} - /> - setSelectedAgent('cursor')} - /> - setSelectedAgent('codex')} - /> -
-
- - {/* Main Panel */} -
- {/* Category Tabs */} -
-
- - - -
-
- - {/* Category Content */} -
- {/* Account Category */} - {selectedCategory === 'account' && ( - - )} - - {/* Permissions Category */} - {selectedCategory === 'permissions' && selectedAgent === 'claude' && ( - - )} - - {selectedCategory === 'permissions' && selectedAgent === 'cursor' && ( - - )} - - {selectedCategory === 'permissions' && selectedAgent === 'codex' && ( - - )} - - {/* MCP Servers Category */} - {selectedCategory === 'mcp' && selectedAgent === 'claude' && ( - openMcpForm()} - onEdit={(server) => openMcpForm(server)} - onDelete={(serverId, scope) => handleMcpDelete(serverId, scope)} - onTest={(serverId, scope) => handleMcpTest(serverId, scope)} - onDiscoverTools={(serverId, scope) => handleMcpToolsDiscovery(serverId, scope)} - testResults={mcpTestResults} - serverTools={mcpServerTools} - toolsLoading={mcpToolsLoading} - /> - )} - - {selectedCategory === 'mcp' && selectedAgent === 'cursor' && ( - {/* TODO: Add cursor MCP form */}} - onEdit={(server) => {/* TODO: Edit cursor MCP form */}} - onDelete={(serverId) => {/* TODO: Delete cursor MCP */}} - /> - )} - - {selectedCategory === 'mcp' && selectedAgent === 'codex' && ( - openCodexMcpForm()} - onEdit={(server) => openCodexMcpForm(server)} - onDelete={(serverId) => handleCodexMcpDelete(serverId)} - /> - )} -
-
-
- )} - - {/* MCP Server Form Modal */} - {showMcpForm && ( -
-
-
-

- {editingMcpServer ? t('mcpForm.title.edit') : t('mcpForm.title.add')} -

- -
- -
- - {!editingMcpServer && ( -
- - -
- )} - - {/* Show current scope when editing */} - {mcpFormData.importMode === 'form' && editingMcpServer && ( -
- -
- {mcpFormData.scope === 'user' ? : } - - {mcpFormData.scope === 'user' ? t('mcpForm.scope.userGlobal') : t('mcpForm.scope.projectLocal')} - - {mcpFormData.scope === 'local' && mcpFormData.projectPath && ( - - - {mcpFormData.projectPath} - - )} -
-

- {t('mcpForm.scope.cannotChange')} -

-
- )} - - {/* Scope Selection - Moved to top, disabled when editing */} - {mcpFormData.importMode === 'form' && !editingMcpServer && ( -
-
- -
- - -
-

- {mcpFormData.scope === 'user' - ? t('mcpForm.scope.userDescription') - : t('mcpForm.scope.projectDescription') - } -

-
- - {/* Project Selection for Local Scope */} - {mcpFormData.scope === 'local' && !editingMcpServer && ( -
- - - {mcpFormData.projectPath && ( -

- {t('mcpForm.projectPath', { path: mcpFormData.projectPath })} -

- )} -
- )} -
- )} - - {/* Basic Info */} -
-
- - { - setMcpFormData(prev => ({...prev, name: e.target.value})); - }} - placeholder={t('mcpForm.placeholders.serverName')} - required - /> -
- - {mcpFormData.importMode === 'form' && ( -
- - -
- )} -
- - - {/* Show raw configuration details when editing */} - {editingMcpServer && mcpFormData.raw && mcpFormData.importMode === 'form' && ( -
-

- {t('mcpForm.configDetails', { configFile: editingMcpServer.scope === 'global' ? '~/.claude.json' : 'project config' })} -

-
-                          {JSON.stringify(mcpFormData.raw, null, 2)}
-                        
-
- )} - - {/* JSON Import Mode */} - {mcpFormData.importMode === 'json' && ( -
-
- -