codex-linux 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +10 -0
- package/.eslintrc.json +27 -0
- package/.github/workflows/ci.yml +156 -0
- package/.huskyrc +7 -0
- package/.lintstagedrc +13 -0
- package/.prettierrc +12 -0
- package/CLAUDE.md +163 -0
- package/DESIGN_SUPERIOR.md +73 -0
- package/Dockerfile +64 -0
- package/INSTALLATION.md +152 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/assets/skills/code-review/instructions.md +102 -0
- package/assets/skills/code-review/skill.yaml +15 -0
- package/assets/skills/refactoring/instructions.md +149 -0
- package/assets/skills/refactoring/skill.yaml +15 -0
- package/assets/skills/testing/skill.yaml +15 -0
- package/commitlint.config.js +23 -0
- package/dist/main/DatabaseManager.js +763 -0
- package/dist/main/DatabaseManager.js.map +1 -0
- package/dist/main/SettingsManager.js +61 -0
- package/dist/main/SettingsManager.js.map +1 -0
- package/dist/main/agents/AgentOrchestrator.js +787 -0
- package/dist/main/agents/AgentOrchestrator.js.map +1 -0
- package/dist/main/agents/AgentSDK.js +219 -0
- package/dist/main/agents/AgentSDK.js.map +1 -0
- package/dist/main/agents/AgentTools.js +348 -0
- package/dist/main/agents/AgentTools.js.map +1 -0
- package/dist/main/agents/CodeIndex.js +233 -0
- package/dist/main/agents/CodeIndex.js.map +1 -0
- package/dist/main/agents/EmbeddingService.js +80 -0
- package/dist/main/agents/EmbeddingService.js.map +1 -0
- package/dist/main/agents/NativeToolCalling.js +206 -0
- package/dist/main/agents/NativeToolCalling.js.map +1 -0
- package/dist/main/api/APIServer.js +278 -0
- package/dist/main/api/APIServer.js.map +1 -0
- package/dist/main/api/RateLimiter.js +138 -0
- package/dist/main/api/RateLimiter.js.map +1 -0
- package/dist/main/api/WebSocketManager.js +300 -0
- package/dist/main/api/WebSocketManager.js.map +1 -0
- package/dist/main/assistant/ContextOptimizer.js +192 -0
- package/dist/main/assistant/ContextOptimizer.js.map +1 -0
- package/dist/main/assistant/PredictedOutputManager.js +172 -0
- package/dist/main/assistant/PredictedOutputManager.js.map +1 -0
- package/dist/main/assistant/PromptCacheManager.js +193 -0
- package/dist/main/assistant/PromptCacheManager.js.map +1 -0
- package/dist/main/assistant/PromptOptimizer.js +626 -0
- package/dist/main/assistant/PromptOptimizer.js.map +1 -0
- package/dist/main/assistant/SmartCodeAssistant.js +224 -0
- package/dist/main/assistant/SmartCodeAssistant.js.map +1 -0
- package/dist/main/auth/SessionManager.js +300 -0
- package/dist/main/auth/SessionManager.js.map +1 -0
- package/dist/main/automations/AdvancedWebhookSystem.js +212 -0
- package/dist/main/automations/AdvancedWebhookSystem.js.map +1 -0
- package/dist/main/automations/AutomationScheduler.js +269 -0
- package/dist/main/automations/AutomationScheduler.js.map +1 -0
- package/dist/main/automations/BatchProcessingSystem.js +159 -0
- package/dist/main/automations/BatchProcessingSystem.js.map +1 -0
- package/dist/main/automations/BrowserAutomationManager.js +195 -0
- package/dist/main/automations/BrowserAutomationManager.js.map +1 -0
- package/dist/main/automations/GitHubActionsManager.js +129 -0
- package/dist/main/automations/GitHubActionsManager.js.map +1 -0
- package/dist/main/automations/GitLabCIManager.js +122 -0
- package/dist/main/automations/GitLabCIManager.js.map +1 -0
- package/dist/main/automations/PriorityQueueManager.js +240 -0
- package/dist/main/automations/PriorityQueueManager.js.map +1 -0
- package/dist/main/background/BackgroundModeManager.js +117 -0
- package/dist/main/background/BackgroundModeManager.js.map +1 -0
- package/dist/main/backup/BackupManager.js +254 -0
- package/dist/main/backup/BackupManager.js.map +1 -0
- package/dist/main/backup/MigrationManager.js +114 -0
- package/dist/main/backup/MigrationManager.js.map +1 -0
- package/dist/main/commands/SlashCommandManager.js +399 -0
- package/dist/main/commands/SlashCommandManager.js.map +1 -0
- package/dist/main/config/ClaudeMdParser.js +519 -0
- package/dist/main/config/ClaudeMdParser.js.map +1 -0
- package/dist/main/config/CustomizationManager.js +381 -0
- package/dist/main/config/CustomizationManager.js.map +1 -0
- package/dist/main/config/LaunchConfigManager.js +211 -0
- package/dist/main/config/LaunchConfigManager.js.map +1 -0
- package/dist/main/config/SettingsManager.js +166 -0
- package/dist/main/config/SettingsManager.js.map +1 -0
- package/dist/main/connectors/ConnectorManager.js +151 -0
- package/dist/main/connectors/ConnectorManager.js.map +1 -0
- package/dist/main/connectors/DatabaseConnector.js +222 -0
- package/dist/main/connectors/DatabaseConnector.js.map +1 -0
- package/dist/main/cowork/CoworkManager.js +324 -0
- package/dist/main/cowork/CoworkManager.js.map +1 -0
- package/dist/main/evals/AgentEvalFramework.js +538 -0
- package/dist/main/evals/AgentEvalFramework.js.map +1 -0
- package/dist/main/evals/GraderManager.js +285 -0
- package/dist/main/evals/GraderManager.js.map +1 -0
- package/dist/main/git/GitWorktreeManager.js +214 -0
- package/dist/main/git/GitWorktreeManager.js.map +1 -0
- package/dist/main/github/GitHubPRMonitor.js +244 -0
- package/dist/main/github/GitHubPRMonitor.js.map +1 -0
- package/dist/main/ide/ContinueInManager.js +181 -0
- package/dist/main/ide/ContinueInManager.js.map +1 -0
- package/dist/main/ide/IDEIntegration.js +277 -0
- package/dist/main/ide/IDEIntegration.js.map +1 -0
- package/dist/main/integrations/LinearManager.js +252 -0
- package/dist/main/integrations/LinearManager.js.map +1 -0
- package/dist/main/integrations/SlackBotManager.js +247 -0
- package/dist/main/integrations/SlackBotManager.js.map +1 -0
- package/dist/main/lsp/LSPManager.js +394 -0
- package/dist/main/lsp/LSPManager.js.map +1 -0
- package/dist/main/main.js +1087 -0
- package/dist/main/main.js.map +1 -0
- package/dist/main/mcp/MCPConfigurationManager.js +281 -0
- package/dist/main/mcp/MCPConfigurationManager.js.map +1 -0
- package/dist/main/mcp/MCPManager.js +710 -0
- package/dist/main/mcp/MCPManager.js.map +1 -0
- package/dist/main/mcp/MCPRegistry.js +272 -0
- package/dist/main/mcp/MCPRegistry.js.map +1 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js +268 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js.map +1 -0
- package/dist/main/monitoring/ErrorTracker.js +57 -0
- package/dist/main/monitoring/ErrorTracker.js.map +1 -0
- package/dist/main/monitoring/MetricsCollector.js +155 -0
- package/dist/main/monitoring/MetricsCollector.js.map +1 -0
- package/dist/main/monitoring/TraceGradingSystem.js +148 -0
- package/dist/main/monitoring/TraceGradingSystem.js.map +1 -0
- package/dist/main/notifications/NotificationManager.js +67 -0
- package/dist/main/notifications/NotificationManager.js.map +1 -0
- package/dist/main/pair/AIPairProgramming.js +200 -0
- package/dist/main/pair/AIPairProgramming.js.map +1 -0
- package/dist/main/plugins/PluginManager.js +222 -0
- package/dist/main/plugins/PluginManager.js.map +1 -0
- package/dist/main/plugins/PluginMarketplace.js +237 -0
- package/dist/main/plugins/PluginMarketplace.js.map +1 -0
- package/dist/main/preload.js +189 -0
- package/dist/main/preload.js.map +1 -0
- package/dist/main/preview/PreviewSessionManager.js +170 -0
- package/dist/main/preview/PreviewSessionManager.js.map +1 -0
- package/dist/main/providers/AIProviderManager.js +327 -0
- package/dist/main/providers/AIProviderManager.js.map +1 -0
- package/dist/main/providers/FineTuningManager.js +276 -0
- package/dist/main/providers/FineTuningManager.js.map +1 -0
- package/dist/main/providers/FreeModelsProvider.js +1104 -0
- package/dist/main/providers/FreeModelsProvider.js.map +1 -0
- package/dist/main/realtime/RealtimeManager.js +116 -0
- package/dist/main/realtime/RealtimeManager.js.map +1 -0
- package/dist/main/remote/CloudEnvironmentManager.js +232 -0
- package/dist/main/remote/CloudEnvironmentManager.js.map +1 -0
- package/dist/main/remote/RemoteSessionManager.js +255 -0
- package/dist/main/remote/RemoteSessionManager.js.map +1 -0
- package/dist/main/search/DeepResearchManager.js +335 -0
- package/dist/main/search/DeepResearchManager.js.map +1 -0
- package/dist/main/search/WebSearchIntegration.js +147 -0
- package/dist/main/search/WebSearchIntegration.js.map +1 -0
- package/dist/main/security/AdminConsoleManager.js +223 -0
- package/dist/main/security/AdminConsoleManager.js.map +1 -0
- package/dist/main/security/AuditLogger.js +136 -0
- package/dist/main/security/AuditLogger.js.map +1 -0
- package/dist/main/security/PermissionManager.js +144 -0
- package/dist/main/security/PermissionManager.js.map +1 -0
- package/dist/main/security/SSOManager.js +173 -0
- package/dist/main/security/SSOManager.js.map +1 -0
- package/dist/main/security/SecurityManager.js +152 -0
- package/dist/main/security/SecurityManager.js.map +1 -0
- package/dist/main/skills/SkillsManager.js +223 -0
- package/dist/main/skills/SkillsManager.js.map +1 -0
- package/dist/main/ssh/SSHManager.js +65 -0
- package/dist/main/ssh/SSHManager.js.map +1 -0
- package/dist/main/streaming/StreamingManager.js +225 -0
- package/dist/main/streaming/StreamingManager.js.map +1 -0
- package/dist/main/sync/CloudSyncManager.js +422 -0
- package/dist/main/sync/CloudSyncManager.js.map +1 -0
- package/dist/main/types.js +28 -0
- package/dist/main/types.js.map +1 -0
- package/dist/main/verification/AutoVerifyManager.js +235 -0
- package/dist/main/verification/AutoVerifyManager.js.map +1 -0
- package/dist/main/vision/ComputerUseManager.js +376 -0
- package/dist/main/vision/ComputerUseManager.js.map +1 -0
- package/dist/main/vision/ImageVideoGenerationManager.js +401 -0
- package/dist/main/vision/ImageVideoGenerationManager.js.map +1 -0
- package/dist/main/vision/VisionManager.js +172 -0
- package/dist/main/vision/VisionManager.js.map +1 -0
- package/dist/renderer/assets/main-DJlZQBCA.js +304 -0
- package/dist/renderer/assets/main-N33ZXEr8.css +1 -0
- package/dist/renderer/index.html +21 -0
- package/dist/renderer/manifest.json +42 -0
- package/dist/renderer/sw.ts +109 -0
- package/dist/shared/types.js +35 -0
- package/dist/shared/types.js.map +1 -0
- package/docker-compose.yml +65 -0
- package/docs/API.md +307 -0
- package/docs/USER_GUIDE.md +476 -0
- package/examples/plugins/sample-plugin/package.json +41 -0
- package/examples/plugins/sample-plugin/src/index.ts +75 -0
- package/index.html +20 -0
- package/jest.config.js +39 -0
- package/package.json +180 -0
- package/packages/cli/package.json +29 -0
- package/packages/cli/src/commands/agents.ts +199 -0
- package/packages/cli/src/commands/tasks.ts +61 -0
- package/packages/cli/src/index.ts +91 -0
- package/packages/cli/src/utils/api.ts +45 -0
- package/packages/cli/src/utils/config.ts +61 -0
- package/packages/npm-installer/bin/codex-linux +126 -0
- package/packages/npm-installer/lib/download.js +273 -0
- package/packages/npm-installer/package.json +42 -0
- package/packages/vscode-extension/package.json +167 -0
- package/packages/vscode-extension/src/api.ts +68 -0
- package/packages/vscode-extension/src/extension.ts +161 -0
- package/packages/vscode-extension/src/panels/chatPanel.ts +265 -0
- package/packages/vscode-extension/src/panels/createAgentPanel.ts +227 -0
- package/packages/vscode-extension/src/providers/agentsProvider.ts +80 -0
- package/postcss.config.js +6 -0
- package/public/manifest.json +42 -0
- package/public/sw.ts +109 -0
- package/scripts/install-dev.sh +103 -0
- package/scripts/install.sh +275 -0
- package/src/main/DatabaseManager.ts +950 -0
- package/src/main/SettingsManager.ts +63 -0
- package/src/main/agents/AgentOrchestrator.ts +930 -0
- package/src/main/agents/AgentSDK.ts +269 -0
- package/src/main/agents/AgentTools.ts +380 -0
- package/src/main/agents/CodeIndex.ts +240 -0
- package/src/main/agents/EmbeddingService.ts +88 -0
- package/src/main/agents/NativeToolCalling.ts +245 -0
- package/src/main/api/APIServer.ts +316 -0
- package/src/main/api/RateLimiter.ts +165 -0
- package/src/main/api/WebSocketManager.ts +398 -0
- package/src/main/assistant/ContextOptimizer.ts +214 -0
- package/src/main/assistant/PredictedOutputManager.ts +265 -0
- package/src/main/assistant/PromptCacheManager.ts +280 -0
- package/src/main/assistant/PromptOptimizer.ts +746 -0
- package/src/main/assistant/SmartCodeAssistant.ts +234 -0
- package/src/main/auth/SessionManager.ts +415 -0
- package/src/main/automations/AdvancedWebhookSystem.ts +281 -0
- package/src/main/automations/AutomationScheduler.ts +272 -0
- package/src/main/automations/BatchProcessingSystem.ts +207 -0
- package/src/main/automations/BrowserAutomationManager.ts +203 -0
- package/src/main/automations/GitHubActionsManager.ts +151 -0
- package/src/main/automations/GitLabCIManager.ts +206 -0
- package/src/main/automations/PriorityQueueManager.ts +328 -0
- package/src/main/background/BackgroundModeManager.ts +130 -0
- package/src/main/backup/BackupManager.ts +287 -0
- package/src/main/backup/MigrationManager.ts +132 -0
- package/src/main/commands/SlashCommandManager.ts +407 -0
- package/src/main/config/ClaudeMdParser.ts +539 -0
- package/src/main/config/CustomizationManager.ts +493 -0
- package/src/main/config/LaunchConfigManager.ts +212 -0
- package/src/main/config/SettingsManager.ts +163 -0
- package/src/main/connectors/ConnectorManager.ts +175 -0
- package/src/main/connectors/DatabaseConnector.ts +212 -0
- package/src/main/cowork/CoworkManager.ts +431 -0
- package/src/main/evals/AgentEvalFramework.ts +665 -0
- package/src/main/evals/GraderManager.ts +417 -0
- package/src/main/git/GitWorktreeManager.ts +211 -0
- package/src/main/github/GitHubPRMonitor.ts +317 -0
- package/src/main/ide/ContinueInManager.ts +180 -0
- package/src/main/ide/IDEIntegration.ts +288 -0
- package/src/main/integrations/LinearManager.ts +327 -0
- package/src/main/integrations/SlackBotManager.ts +312 -0
- package/src/main/lsp/LSPManager.ts +445 -0
- package/src/main/main.ts +1221 -0
- package/src/main/mcp/MCPConfigurationManager.ts +281 -0
- package/src/main/mcp/MCPManager.ts +799 -0
- package/src/main/mcp/MCPRegistry.ts +273 -0
- package/src/main/monitoring/ErrorRecoveryManager.ts +359 -0
- package/src/main/monitoring/ErrorTracker.ts +60 -0
- package/src/main/monitoring/MetricsCollector.ts +196 -0
- package/src/main/monitoring/TraceGradingSystem.ts +196 -0
- package/src/main/notifications/NotificationManager.ts +96 -0
- package/src/main/pair/AIPairProgramming.ts +290 -0
- package/src/main/plugins/PluginManager.ts +266 -0
- package/src/main/plugins/PluginMarketplace.ts +318 -0
- package/src/main/preload.ts +215 -0
- package/src/main/preview/PreviewSessionManager.ts +186 -0
- package/src/main/providers/AIProviderManager.ts +394 -0
- package/src/main/providers/FineTuningManager.ts +390 -0
- package/src/main/providers/FreeModelsProvider.ts +1156 -0
- package/src/main/realtime/RealtimeManager.ts +147 -0
- package/src/main/remote/CloudEnvironmentManager.ts +253 -0
- package/src/main/remote/RemoteSessionManager.ts +323 -0
- package/src/main/search/DeepResearchManager.ts +458 -0
- package/src/main/search/WebSearchIntegration.ts +203 -0
- package/src/main/security/AdminConsoleManager.ts +244 -0
- package/src/main/security/AuditLogger.ts +143 -0
- package/src/main/security/PermissionManager.ts +184 -0
- package/src/main/security/SSOManager.ts +241 -0
- package/src/main/security/SecurityManager.ts +139 -0
- package/src/main/skills/SkillsManager.ts +218 -0
- package/src/main/ssh/SSHManager.ts +86 -0
- package/src/main/streaming/StreamingManager.ts +306 -0
- package/src/main/sync/CloudSyncManager.ts +532 -0
- package/src/main/verification/AutoVerifyManager.ts +285 -0
- package/src/main/vision/ComputerUseManager.ts +475 -0
- package/src/main/vision/ImageVideoGenerationManager.ts +526 -0
- package/src/main/vision/VisionManager.ts +186 -0
- package/src/renderer/App.tsx +314 -0
- package/src/renderer/components/AdvancedSettingsPanel.tsx +225 -0
- package/src/renderer/components/AgentPanel.tsx +760 -0
- package/src/renderer/components/AppPreview.tsx +220 -0
- package/src/renderer/components/AuditTrailPanel.tsx +148 -0
- package/src/renderer/components/AutomationPanel.tsx +220 -0
- package/src/renderer/components/ChatInterface.tsx +595 -0
- package/src/renderer/components/ChatTab.tsx +296 -0
- package/src/renderer/components/CodeEditor.tsx +257 -0
- package/src/renderer/components/CodeReviewPanel.tsx +256 -0
- package/src/renderer/components/CodeWorkspace.tsx +192 -0
- package/src/renderer/components/CodebaseDashboard.tsx +295 -0
- package/src/renderer/components/ComputerUsePanel.tsx +262 -0
- package/src/renderer/components/ConnectorsPanel.tsx +471 -0
- package/src/renderer/components/ContextMenu.tsx +155 -0
- package/src/renderer/components/ContextUsageDisplay.tsx +248 -0
- package/src/renderer/components/CoworkPanel.tsx +415 -0
- package/src/renderer/components/DiffViewer.tsx +452 -0
- package/src/renderer/components/ErrorBoundary.tsx +273 -0
- package/src/renderer/components/ExtendedThinkingToggle.tsx +244 -0
- package/src/renderer/components/FileAttachments.tsx +247 -0
- package/src/renderer/components/FileExplorer.tsx +242 -0
- package/src/renderer/components/FileExplorerPanel.tsx +302 -0
- package/src/renderer/components/GitPanel.tsx +154 -0
- package/src/renderer/components/Header.tsx +113 -0
- package/src/renderer/components/MCPPanel.tsx +326 -0
- package/src/renderer/components/MentionAutocomplete.tsx +239 -0
- package/src/renderer/components/PermissionPanel.tsx +159 -0
- package/src/renderer/components/PermissionSelector.tsx +203 -0
- package/src/renderer/components/PluginMarketplace.tsx +325 -0
- package/src/renderer/components/PromptOptimizerPanel.tsx +399 -0
- package/src/renderer/components/SearchPanel.tsx +173 -0
- package/src/renderer/components/SearchReplace.tsx +284 -0
- package/src/renderer/components/SessionSidebar.tsx +367 -0
- package/src/renderer/components/SettingsPanel.tsx +426 -0
- package/src/renderer/components/Sidebar.tsx +100 -0
- package/src/renderer/components/SkillsPanel.tsx +245 -0
- package/src/renderer/components/SplitPane.tsx +173 -0
- package/src/renderer/components/Terminal.tsx +190 -0
- package/src/renderer/components/VoiceCommand.tsx +129 -0
- package/src/renderer/components/WorktreePanel.tsx +163 -0
- package/src/renderer/components/ui/AriaComponents.tsx +193 -0
- package/src/renderer/components/ui/Button.tsx +68 -0
- package/src/renderer/components/ui/Card.tsx +102 -0
- package/src/renderer/components/ui/Input.tsx +44 -0
- package/src/renderer/components/ui/Skeleton.tsx +55 -0
- package/src/renderer/components/ui/VirtualList.tsx +196 -0
- package/src/renderer/i18n/I18nProvider.tsx +101 -0
- package/src/renderer/i18n/de.ts +161 -0
- package/src/renderer/i18n/en.ts +163 -0
- package/src/renderer/i18n/es.ts +161 -0
- package/src/renderer/i18n/fr.ts +161 -0
- package/src/renderer/i18n/index.ts +44 -0
- package/src/renderer/index.css +129 -0
- package/src/renderer/lib/accessibility.tsx +287 -0
- package/src/renderer/lib/hooks.ts +304 -0
- package/src/renderer/lib/utils.ts +6 -0
- package/src/renderer/main.tsx +25 -0
- package/src/renderer/styles/minimalist.css +539 -0
- package/src/renderer/sw.ts +180 -0
- package/src/renderer/types.d.ts +138 -0
- package/src/shared/types.ts +813 -0
- package/supabase/schema.sql +234 -0
- package/tailwind.config.js +78 -0
- package/tests/e2e/package.json +15 -0
- package/tests/e2e/playwright.config.ts +31 -0
- package/tests/e2e/specs/app.spec.ts +194 -0
- package/tests/setup.ts +99 -0
- package/tests/unit/AgentOrchestrator.test.ts +274 -0
- package/tests/unit/DatabaseManager.test.ts +262 -0
- package/tests/unit/GitWorktreeManager.test.ts +150 -0
- package/tests/unit/SecurityManager.test.ts +110 -0
- package/tsconfig.main.json +22 -0
- package/tsconfig.renderer.json +27 -0
- package/vite.config.ts +28 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Settings, AIProvider } from '../../shared/types';
|
|
3
|
+
import { Settings as SettingsIcon, Key, Eye, EyeOff, Save, Zap, Check, Sparkles } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
interface SettingsPanelProps {
|
|
6
|
+
settings: Settings;
|
|
7
|
+
providers: AIProvider[];
|
|
8
|
+
onSettingsChange: (settings: Settings) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
12
|
+
settings,
|
|
13
|
+
providers,
|
|
14
|
+
onSettingsChange
|
|
15
|
+
}) => {
|
|
16
|
+
const [localSettings, setLocalSettings] = useState(settings);
|
|
17
|
+
const [showApiKeys, setShowApiKeys] = useState<Record<string, boolean>>({});
|
|
18
|
+
const [activeTab, setActiveTab] = useState('general');
|
|
19
|
+
const [selectedModel, setSelectedModel] = useState<string>(localSettings.defaultModel);
|
|
20
|
+
|
|
21
|
+
const handleSave = async () => {
|
|
22
|
+
for (const [key, value] of Object.entries(localSettings)) {
|
|
23
|
+
await window.electronAPI.settings.set(key, value);
|
|
24
|
+
}
|
|
25
|
+
onSettingsChange(localSettings);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handleProviderConfig = async (providerId: string, apiKey: string) => {
|
|
29
|
+
try {
|
|
30
|
+
await window.electronAPI.providers.configure(providerId, { apiKey });
|
|
31
|
+
const success = await window.electronAPI.providers.test(providerId);
|
|
32
|
+
if (success) {
|
|
33
|
+
alert('Connection successful!');
|
|
34
|
+
} else {
|
|
35
|
+
alert('Connection failed. Please check your API key.');
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('Failed to configure provider:', error);
|
|
39
|
+
alert('Failed to configure provider');
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleModelSelect = (modelId: string, providerId: string) => {
|
|
44
|
+
setSelectedModel(modelId);
|
|
45
|
+
setLocalSettings({
|
|
46
|
+
...localSettings,
|
|
47
|
+
defaultModel: modelId,
|
|
48
|
+
defaultProvider: providerId
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const tabs = [
|
|
53
|
+
{ id: 'general', label: 'General' },
|
|
54
|
+
{ id: 'providers', label: 'AI Providers' },
|
|
55
|
+
{ id: 'appearance', label: 'Appearance' },
|
|
56
|
+
{ id: 'shortcuts', label: 'Shortcuts' }
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const freeModelsProvider = providers.find(p => p.isFree);
|
|
60
|
+
const paidProviders = providers.filter(p => !p.isFree);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className="h-full flex flex-col">
|
|
64
|
+
<div className="p-4 border-b border-border">
|
|
65
|
+
<div className="flex items-center gap-2 mb-4">
|
|
66
|
+
<SettingsIcon className="w-5 h-5" />
|
|
67
|
+
<h2 className="text-lg font-semibold">Settings</h2>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="flex gap-2">
|
|
71
|
+
{tabs.map(tab => (
|
|
72
|
+
<button
|
|
73
|
+
key={tab.id}
|
|
74
|
+
onClick={() => setActiveTab(tab.id)}
|
|
75
|
+
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
76
|
+
activeTab === tab.id
|
|
77
|
+
? 'bg-primary text-primary-foreground'
|
|
78
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
79
|
+
}`}
|
|
80
|
+
>
|
|
81
|
+
{tab.label}
|
|
82
|
+
</button>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div className="flex-1 overflow-auto p-6">
|
|
88
|
+
{activeTab === 'general' && (
|
|
89
|
+
<div className="max-w-2xl space-y-6">
|
|
90
|
+
<section>
|
|
91
|
+
<h3 className="text-lg font-medium mb-4">General Settings</h3>
|
|
92
|
+
|
|
93
|
+
<div className="space-y-4">
|
|
94
|
+
<div className="flex items-center justify-between">
|
|
95
|
+
<div>
|
|
96
|
+
<label className="block font-medium">Auto Save</label>
|
|
97
|
+
<p className="text-sm text-muted-foreground">Automatically save changes</p>
|
|
98
|
+
</div>
|
|
99
|
+
<input
|
|
100
|
+
type="checkbox"
|
|
101
|
+
checked={localSettings.autoSave}
|
|
102
|
+
onChange={e => setLocalSettings({ ...localSettings, autoSave: e.target.checked })}
|
|
103
|
+
className="w-5 h-5"
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div className="flex items-center justify-between">
|
|
108
|
+
<div>
|
|
109
|
+
<label className="block font-medium">Max Parallel Agents</label>
|
|
110
|
+
<p className="text-sm text-muted-foreground">Maximum number of agents running simultaneously</p>
|
|
111
|
+
</div>
|
|
112
|
+
<input
|
|
113
|
+
type="number"
|
|
114
|
+
value={localSettings.maxParallelAgents}
|
|
115
|
+
onChange={e => setLocalSettings({ ...localSettings, maxParallelAgents: parseInt(e.target.value) })}
|
|
116
|
+
className="w-20 px-3 py-2 bg-background border border-input rounded-md"
|
|
117
|
+
min={1}
|
|
118
|
+
max={10}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div className="flex items-center justify-between">
|
|
123
|
+
<div>
|
|
124
|
+
<label className="block font-medium">Show Notifications</label>
|
|
125
|
+
<p className="text-sm text-muted-foreground">Display desktop notifications for agent events</p>
|
|
126
|
+
</div>
|
|
127
|
+
<input
|
|
128
|
+
type="checkbox"
|
|
129
|
+
checked={localSettings.showNotifications}
|
|
130
|
+
onChange={e => setLocalSettings({ ...localSettings, showNotifications: e.target.checked })}
|
|
131
|
+
className="w-5 h-5"
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div className="flex items-center justify-between">
|
|
136
|
+
<div>
|
|
137
|
+
<label className="block font-medium">Confirm Destructive Actions</label>
|
|
138
|
+
<p className="text-sm text-muted-foreground">Show confirmation dialogs before deleting agents or worktrees</p>
|
|
139
|
+
</div>
|
|
140
|
+
<input
|
|
141
|
+
type="checkbox"
|
|
142
|
+
checked={localSettings.confirmDestructiveActions}
|
|
143
|
+
onChange={e => setLocalSettings({ ...localSettings, confirmDestructiveActions: e.target.checked })}
|
|
144
|
+
className="w-5 h-5"
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</section>
|
|
149
|
+
|
|
150
|
+
<section>
|
|
151
|
+
<h3 className="text-lg font-medium mb-4">Git Configuration</h3>
|
|
152
|
+
|
|
153
|
+
<div className="space-y-4">
|
|
154
|
+
<div>
|
|
155
|
+
<label className="block font-medium mb-1">Author Name</label>
|
|
156
|
+
<input
|
|
157
|
+
type="text"
|
|
158
|
+
value={localSettings.gitAuthorName}
|
|
159
|
+
onChange={e => setLocalSettings({ ...localSettings, gitAuthorName: e.target.value })}
|
|
160
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
161
|
+
placeholder="Your Name"
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div>
|
|
166
|
+
<label className="block font-medium mb-1">Author Email</label>
|
|
167
|
+
<input
|
|
168
|
+
type="email"
|
|
169
|
+
value={localSettings.gitAuthorEmail}
|
|
170
|
+
onChange={e => setLocalSettings({ ...localSettings, gitAuthorEmail: e.target.value })}
|
|
171
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
172
|
+
placeholder="your@email.com"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</section>
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
|
|
180
|
+
{activeTab === 'providers' && (
|
|
181
|
+
<div className="max-w-4xl space-y-6">
|
|
182
|
+
{freeModelsProvider && (
|
|
183
|
+
<section className="p-4 border-2 border-green-500/30 bg-green-500/5 rounded-lg">
|
|
184
|
+
<div className="flex items-center gap-2 mb-4">
|
|
185
|
+
<Sparkles className="w-5 h-5 text-green-500" />
|
|
186
|
+
<h3 className="text-lg font-medium">Free AI Models</h3>
|
|
187
|
+
<span className="px-2 py-0.5 text-xs font-medium bg-green-500/20 text-green-500 rounded-full">
|
|
188
|
+
No API Key Required
|
|
189
|
+
</span>
|
|
190
|
+
</div>
|
|
191
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
192
|
+
Start using AI immediately with free models from OpenRouter, Groq, Google AI Studio, and Cerebras.
|
|
193
|
+
No API key required for OpenRouter free tier - just select a model and start chatting!
|
|
194
|
+
</p>
|
|
195
|
+
|
|
196
|
+
<div className="mb-4">
|
|
197
|
+
<label className="block text-sm font-medium mb-2">Select Model</label>
|
|
198
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 max-h-96 overflow-y-auto">
|
|
199
|
+
{freeModelsProvider.models
|
|
200
|
+
.sort((a, b) => {
|
|
201
|
+
const backends = ['openrouter', 'groq', 'google', 'cerebras'];
|
|
202
|
+
const aBackend = a.backend || 'openrouter';
|
|
203
|
+
const bBackend = b.backend || 'openrouter';
|
|
204
|
+
return backends.indexOf(aBackend) - backends.indexOf(bBackend);
|
|
205
|
+
})
|
|
206
|
+
.map(model => (
|
|
207
|
+
<button
|
|
208
|
+
key={model.id}
|
|
209
|
+
onClick={() => handleModelSelect(model.id, freeModelsProvider.id)}
|
|
210
|
+
className={`p-3 text-left rounded-lg border transition-all ${
|
|
211
|
+
selectedModel === model.id
|
|
212
|
+
? 'border-green-500 bg-green-500/10'
|
|
213
|
+
: 'border-border hover:border-green-500/50 hover:bg-muted'
|
|
214
|
+
}`}
|
|
215
|
+
>
|
|
216
|
+
<div className="flex items-center justify-between">
|
|
217
|
+
<span className="font-medium text-sm truncate">{model.name}</span>
|
|
218
|
+
{selectedModel === model.id && (
|
|
219
|
+
<Check className="w-4 h-4 text-green-500 flex-shrink-0" />
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
<div className="flex items-center gap-1 mt-1">
|
|
223
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-blue-500/20 text-blue-400">
|
|
224
|
+
{model.backend || 'openrouter'}
|
|
225
|
+
</span>
|
|
226
|
+
{model.supportsVision && (
|
|
227
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-purple-500/20 text-purple-400">
|
|
228
|
+
vision
|
|
229
|
+
</span>
|
|
230
|
+
)}
|
|
231
|
+
{model.contextWindow >= 100000 && (
|
|
232
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-orange-500/20 text-orange-400">
|
|
233
|
+
{model.contextWindow >= 1000000 ? '1M ctx' : `${model.contextWindow / 1000}k ctx`}
|
|
234
|
+
</span>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
<p className="text-xs text-muted-foreground mt-1 truncate">{model.description}</p>
|
|
238
|
+
</button>
|
|
239
|
+
))}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<div className="text-xs text-muted-foreground">
|
|
244
|
+
<strong>Tip:</strong> OpenRouter models work without an API key. For Groq, Google AI Studio, or Cerebras models,
|
|
245
|
+
you may need to add your free API key below.
|
|
246
|
+
</div>
|
|
247
|
+
</section>
|
|
248
|
+
)}
|
|
249
|
+
|
|
250
|
+
<section>
|
|
251
|
+
<h3 className="text-lg font-medium mb-4">Premium Providers</h3>
|
|
252
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
253
|
+
Add your API keys for premium models with higher limits and advanced features.
|
|
254
|
+
</p>
|
|
255
|
+
|
|
256
|
+
<div className="space-y-4">
|
|
257
|
+
{paidProviders.map(provider => (
|
|
258
|
+
<div key={provider.id} className="p-4 bg-muted rounded-lg">
|
|
259
|
+
<div className="flex items-center justify-between mb-3">
|
|
260
|
+
<div>
|
|
261
|
+
<h4 className="font-medium">{provider.name}</h4>
|
|
262
|
+
<p className="text-sm text-muted-foreground">{provider.description}</p>
|
|
263
|
+
</div>
|
|
264
|
+
<div className="flex items-center gap-2">
|
|
265
|
+
<input
|
|
266
|
+
type="radio"
|
|
267
|
+
name="activeProvider"
|
|
268
|
+
checked={localSettings.defaultProvider === provider.id}
|
|
269
|
+
onChange={() => setLocalSettings({ ...localSettings, defaultProvider: provider.id })}
|
|
270
|
+
className="w-4 h-4"
|
|
271
|
+
/>
|
|
272
|
+
<span className="text-sm">Default</span>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div>
|
|
277
|
+
<label className="block text-sm font-medium mb-1">API Key</label>
|
|
278
|
+
<div className="flex gap-2">
|
|
279
|
+
<div className="relative flex-1">
|
|
280
|
+
<input
|
|
281
|
+
type={showApiKeys[provider.id] ? 'text' : 'password'}
|
|
282
|
+
defaultValue={provider.config.apiKey}
|
|
283
|
+
placeholder={`Enter ${provider.name} API key`}
|
|
284
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md pr-10"
|
|
285
|
+
onBlur={e => handleProviderConfig(provider.id, e.target.value)}
|
|
286
|
+
/>
|
|
287
|
+
<button
|
|
288
|
+
onClick={() => setShowApiKeys({
|
|
289
|
+
...showApiKeys,
|
|
290
|
+
[provider.id]: !showApiKeys[provider.id]
|
|
291
|
+
})}
|
|
292
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
|
293
|
+
>
|
|
294
|
+
{showApiKeys[provider.id] ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
|
295
|
+
</button>
|
|
296
|
+
</div>
|
|
297
|
+
<button
|
|
298
|
+
onClick={() => handleProviderConfig(provider.id, provider.config.apiKey || '')}
|
|
299
|
+
className="px-3 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
|
300
|
+
>
|
|
301
|
+
Test
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
{provider.models.length > 0 && (
|
|
307
|
+
<div className="mt-3">
|
|
308
|
+
<label className="block text-sm font-medium mb-1">Available Models</label>
|
|
309
|
+
<div className="flex flex-wrap gap-2">
|
|
310
|
+
{provider.models.map(model => (
|
|
311
|
+
<span
|
|
312
|
+
key={model.id}
|
|
313
|
+
className="px-2 py-1 bg-background rounded text-xs"
|
|
314
|
+
>
|
|
315
|
+
{model.name}
|
|
316
|
+
</span>
|
|
317
|
+
))}
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
</div>
|
|
322
|
+
))}
|
|
323
|
+
</div>
|
|
324
|
+
</section>
|
|
325
|
+
|
|
326
|
+
<section className="p-4 bg-blue-500/10 border border-blue-500/30 rounded-lg">
|
|
327
|
+
<div className="flex items-center gap-2 mb-2">
|
|
328
|
+
<Zap className="w-4 h-4 text-blue-400" />
|
|
329
|
+
<h4 className="font-medium text-blue-400">Get Free API Keys</h4>
|
|
330
|
+
</div>
|
|
331
|
+
<div className="text-sm text-muted-foreground space-y-1">
|
|
332
|
+
<p><strong>OpenRouter:</strong> <a href="https://openrouter.ai" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">openrouter.ai</a> - No key needed for free models</p>
|
|
333
|
+
<p><strong>Ollama:</strong> <a href="https://ollama.com" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">ollama.com</a> - Local models, no API key needed</p>
|
|
334
|
+
<p><strong>NVIDIA NIM:</strong> <a href="https://build.nvidia.com" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">build.nvidia.com</a> - Kimi K2.5, DeepSeek R1, Llama (40 req/min free)</p>
|
|
335
|
+
<p><strong>Groq:</strong> <a href="https://console.groq.com" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">console.groq.com</a> - Fast inference, generous free tier</p>
|
|
336
|
+
<p><strong>Google AI Studio:</strong> <a href="https://aistudio.google.com" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">aistudio.google.com</a> - Gemini models free tier</p>
|
|
337
|
+
<p><strong>Cerebras:</strong> <a href="https://cloud.cerebras.ai" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">cloud.cerebras.ai</a> - Ultra-fast inference</p>
|
|
338
|
+
</div>
|
|
339
|
+
</section>
|
|
340
|
+
</div>
|
|
341
|
+
)}
|
|
342
|
+
|
|
343
|
+
{activeTab === 'appearance' && (
|
|
344
|
+
<div className="max-w-2xl space-y-6">
|
|
345
|
+
<section>
|
|
346
|
+
<h3 className="text-lg font-medium mb-4">Appearance</h3>
|
|
347
|
+
|
|
348
|
+
<div className="space-y-4">
|
|
349
|
+
<div>
|
|
350
|
+
<label className="block font-medium mb-1">Theme</label>
|
|
351
|
+
<select
|
|
352
|
+
value={localSettings.theme}
|
|
353
|
+
onChange={e => setLocalSettings({ ...localSettings, theme: e.target.value as any })}
|
|
354
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
355
|
+
>
|
|
356
|
+
<option value="light">Light</option>
|
|
357
|
+
<option value="dark">Dark</option>
|
|
358
|
+
<option value="system">System</option>
|
|
359
|
+
</select>
|
|
360
|
+
</div>
|
|
361
|
+
|
|
362
|
+
<div>
|
|
363
|
+
<label className="block font-medium mb-1">Font Size</label>
|
|
364
|
+
<input
|
|
365
|
+
type="range"
|
|
366
|
+
value={localSettings.fontSize}
|
|
367
|
+
onChange={e => setLocalSettings({ ...localSettings, fontSize: parseInt(e.target.value) })}
|
|
368
|
+
className="w-full"
|
|
369
|
+
min={10}
|
|
370
|
+
max={20}
|
|
371
|
+
/>
|
|
372
|
+
<span className="text-sm text-muted-foreground">{localSettings.fontSize}px</span>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
<div>
|
|
376
|
+
<label className="block font-medium mb-1">Font Family</label>
|
|
377
|
+
<input
|
|
378
|
+
type="text"
|
|
379
|
+
value={localSettings.fontFamily}
|
|
380
|
+
onChange={e => setLocalSettings({ ...localSettings, fontFamily: e.target.value })}
|
|
381
|
+
className="w-full px-3 py-2 bg-background border border-input rounded-md"
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</section>
|
|
386
|
+
</div>
|
|
387
|
+
)}
|
|
388
|
+
|
|
389
|
+
{activeTab === 'shortcuts' && (
|
|
390
|
+
<div className="max-w-2xl">
|
|
391
|
+
<section>
|
|
392
|
+
<h3 className="text-lg font-medium mb-4">Keyboard Shortcuts</h3>
|
|
393
|
+
|
|
394
|
+
<div className="space-y-3">
|
|
395
|
+
{Object.entries(localSettings.shortcuts).map(([action, shortcut]) => (
|
|
396
|
+
<div key={action} className="flex items-center justify-between py-2 border-b border-border last:border-0">
|
|
397
|
+
<span className="text-sm capitalize">{action.replace(/:/g, ' ')}</span>
|
|
398
|
+
<input
|
|
399
|
+
type="text"
|
|
400
|
+
value={shortcut}
|
|
401
|
+
onChange={e => setLocalSettings({
|
|
402
|
+
...localSettings,
|
|
403
|
+
shortcuts: { ...localSettings.shortcuts, [action]: e.target.value }
|
|
404
|
+
})}
|
|
405
|
+
className="px-3 py-1 bg-background border border-input rounded-md text-sm font-mono w-32"
|
|
406
|
+
/>
|
|
407
|
+
</div>
|
|
408
|
+
))}
|
|
409
|
+
</div>
|
|
410
|
+
</section>
|
|
411
|
+
</div>
|
|
412
|
+
)}
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<div className="p-4 border-t border-border flex justify-end">
|
|
416
|
+
<button
|
|
417
|
+
onClick={handleSave}
|
|
418
|
+
className="flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
|
419
|
+
>
|
|
420
|
+
<Save className="w-4 h-4" />
|
|
421
|
+
Save Settings
|
|
422
|
+
</button>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
);
|
|
426
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Bot,
|
|
4
|
+
GitBranch,
|
|
5
|
+
Wrench,
|
|
6
|
+
Clock,
|
|
7
|
+
Settings,
|
|
8
|
+
ChevronRight,
|
|
9
|
+
Code2,
|
|
10
|
+
ScrollText
|
|
11
|
+
} from 'lucide-react';
|
|
12
|
+
import { cn } from '@/lib/utils';
|
|
13
|
+
|
|
14
|
+
interface SidebarProps {
|
|
15
|
+
activeTab: string;
|
|
16
|
+
onTabChange: (tab: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const menuItems = [
|
|
20
|
+
{ id: 'agents', label: 'Agents', icon: Bot, description: 'Manage AI agents' },
|
|
21
|
+
{ id: 'code', label: 'Code', icon: Code2, description: 'Code editor' },
|
|
22
|
+
{ id: 'worktrees', label: 'Worktrees', icon: GitBranch, description: 'Git workspaces' },
|
|
23
|
+
{ id: 'skills', label: 'Skills', icon: Wrench, description: 'Reusable prompts' },
|
|
24
|
+
{ id: 'automations', label: 'Automations', icon: Clock, description: 'Scheduled tasks' },
|
|
25
|
+
{ id: 'audit', label: 'Audit', icon: ScrollText, description: 'Activity log' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const Sidebar: React.FC<SidebarProps> = ({ activeTab, onTabChange }) => {
|
|
29
|
+
return (
|
|
30
|
+
<aside className="w-64 bg-[var(--color-bg-secondary)] border-r border-[var(--color-border)] flex flex-col" data-testid="sidebar">
|
|
31
|
+
{/* Logo */}
|
|
32
|
+
<div className="p-6 border-b border-[var(--color-border)]">
|
|
33
|
+
<div className="flex items-center gap-3">
|
|
34
|
+
<div className="w-8 h-8 bg-neutral-900 rounded-lg flex items-center justify-center">
|
|
35
|
+
<Bot className="w-5 h-5 text-white" />
|
|
36
|
+
</div>
|
|
37
|
+
<div>
|
|
38
|
+
<span className="font-semibold text-lg tracking-tight">Codex</span>
|
|
39
|
+
<span className="text-xs text-neutral-400 block">Linux</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
{/* Navigation */}
|
|
45
|
+
<nav className="flex-1 p-3 space-y-1 overflow-y-auto" data-testid="sidebar-nav">
|
|
46
|
+
{menuItems.map((item) => {
|
|
47
|
+
const Icon = item.icon;
|
|
48
|
+
const isActive = activeTab === item.id;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<button
|
|
52
|
+
key={item.id}
|
|
53
|
+
onClick={() => onTabChange(item.id)}
|
|
54
|
+
className={cn(
|
|
55
|
+
'w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200 group',
|
|
56
|
+
isActive
|
|
57
|
+
? 'bg-neutral-900 text-white shadow-sm'
|
|
58
|
+
: 'text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900'
|
|
59
|
+
)}
|
|
60
|
+
data-testid={`nav-${item.id}`}
|
|
61
|
+
>
|
|
62
|
+
<Icon className={cn(
|
|
63
|
+
'w-4 h-4 transition-transform duration-200',
|
|
64
|
+
isActive ? '' : 'group-hover:scale-110'
|
|
65
|
+
)} />
|
|
66
|
+
<div className="flex-1 text-left">
|
|
67
|
+
<span className="block">{item.label}</span>
|
|
68
|
+
{!isActive && (
|
|
69
|
+
<span className="text-xs text-neutral-400 font-normal opacity-0 group-hover:opacity-100 transition-opacity">
|
|
70
|
+
{item.description}
|
|
71
|
+
</span>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
{isActive && (
|
|
75
|
+
<ChevronRight className="w-4 h-4 opacity-50" />
|
|
76
|
+
)}
|
|
77
|
+
</button>
|
|
78
|
+
);
|
|
79
|
+
})}
|
|
80
|
+
</nav>
|
|
81
|
+
|
|
82
|
+
{/* Settings */}
|
|
83
|
+
<div className="p-3 border-t border-[var(--color-border)]">
|
|
84
|
+
<button
|
|
85
|
+
onClick={() => onTabChange('settings')}
|
|
86
|
+
className={cn(
|
|
87
|
+
'w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200',
|
|
88
|
+
activeTab === 'settings'
|
|
89
|
+
? 'bg-neutral-900 text-white'
|
|
90
|
+
: 'text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900'
|
|
91
|
+
)}
|
|
92
|
+
data-testid="nav-settings"
|
|
93
|
+
>
|
|
94
|
+
<Settings className="w-4 h-4" />
|
|
95
|
+
<span>Settings</span>
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
</aside>
|
|
99
|
+
);
|
|
100
|
+
};
|