import { useState, useEffect } from 'react'; import { useSearch } from '@tanstack/react-router'; import { useAppStore } from '@/store/app-store'; import { useSettingsView, type SettingsViewId } from './settings-view/hooks'; import { NAV_ITEMS } from './settings-view/config/navigation'; import { SettingsHeader } from './settings-view/components/settings-header'; import { KeyboardMapDialog } from './settings-view/components/keyboard-map-dialog'; import { DeleteProjectDialog } from './settings-view/components/delete-project-dialog'; import { SettingsNavigation } from './settings-view/components/settings-navigation'; import { ApiKeysSection } from './settings-view/api-keys/api-keys-section'; import { ModelDefaultsSection } from './settings-view/model-defaults'; import { AppearanceSection } from './settings-view/appearance/appearance-section'; import { TerminalSection } from './settings-view/terminal/terminal-section'; import { AudioSection } from './settings-view/audio/audio-section'; import { KeyboardShortcutsSection } from './settings-view/keyboard-shortcuts/keyboard-shortcuts-section'; import { FeatureDefaultsSection } from './settings-view/feature-defaults/feature-defaults-section'; import { WorktreesSection } from './settings-view/worktrees'; import { DangerZoneSection } from './settings-view/danger-zone/danger-zone-section'; import { AccountSection } from './settings-view/account'; import { SecuritySection } from './settings-view/security'; import { ClaudeSettingsTab, CursorSettingsTab, CodexSettingsTab, OpencodeSettingsTab, } from './settings-view/providers'; import { MCPServersSection } from './settings-view/mcp-servers'; import { PromptCustomizationSection } from './settings-view/prompts'; import type { Project as SettingsProject, Theme } from './settings-view/shared/types'; import type { Project as ElectronProject } from '@/lib/electron'; // Breakpoint constant for mobile (matches Tailwind lg breakpoint) const LG_BREAKPOINT = 1024; export function SettingsView() { const { theme, setTheme, setProjectTheme, defaultSkipTests, setDefaultSkipTests, enableDependencyBlocking, setEnableDependencyBlocking, skipVerificationInAutoMode, setSkipVerificationInAutoMode, enableAiCommitMessages, setEnableAiCommitMessages, useWorktrees, setUseWorktrees, muteDoneSound, setMuteDoneSound, currentProject, moveProjectToTrash, defaultPlanningMode, setDefaultPlanningMode, defaultRequirePlanApproval, setDefaultRequirePlanApproval, defaultFeatureModel, setDefaultFeatureModel, autoLoadClaudeMd, setAutoLoadClaudeMd, promptCustomization, setPromptCustomization, skipSandboxWarning, setSkipSandboxWarning, } = useAppStore(); // Convert electron Project to settings-view Project type const convertProject = (project: ElectronProject | null): SettingsProject | null => { if (!project) return null; return { id: project.id, name: project.name, path: project.path, theme: project.theme as Theme | undefined, icon: project.icon, customIconPath: project.customIconPath, }; }; const settingsProject = convertProject(currentProject); // Compute the effective theme for the current project const effectiveTheme = (settingsProject?.theme || theme) as Theme; // Handler to set theme - always updates global theme (user's preference), // and also sets per-project theme if a project is selected const handleSetTheme = (newTheme: typeof theme) => { // Always update global theme so user's preference persists across all projects setTheme(newTheme); // Also set per-project theme if a project is selected if (currentProject) { setProjectTheme(currentProject.id, newTheme); } }; // Get initial view from URL search params const { view: initialView } = useSearch({ from: '/settings' }); // Use settings view navigation hook const { activeView, navigateTo } = useSettingsView({ initialView }); // Handle navigation - if navigating to 'providers', default to 'claude-provider' const handleNavigate = (viewId: SettingsViewId) => { if (viewId === 'providers') { navigateTo('claude-provider'); } else { navigateTo(viewId); } }; const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false); // Mobile navigation state - default to showing on desktop, hidden on mobile const [showNavigation, setShowNavigation] = useState(() => { if (typeof window !== 'undefined') { return window.innerWidth >= LG_BREAKPOINT; } return true; // Default to showing on SSR }); // Auto-close navigation on mobile when a section is selected useEffect(() => { if (typeof window !== 'undefined' && window.innerWidth < LG_BREAKPOINT) { setShowNavigation(false); } }, [activeView]); // Handle window resize to show/hide navigation appropriately useEffect(() => { const handleResize = () => { if (window.innerWidth >= LG_BREAKPOINT) { setShowNavigation(true); } }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // Render the active section based on current view const renderActiveSection = () => { switch (activeView) { case 'claude-provider': return ; case 'cursor-provider': return ; case 'codex-provider': return ; case 'opencode-provider': return ; case 'providers': case 'claude': // Backwards compatibility - redirect to claude-provider return ; case 'mcp-servers': return ; case 'prompts': return ( ); case 'model-defaults': return ; case 'appearance': return ( handleSetTheme(theme as any)} /> ); case 'terminal': return ; case 'keyboard': return ( setShowKeyboardMapDialog(true)} /> ); case 'audio': return ( ); case 'defaults': return ( ); case 'worktrees': return ( ); case 'account': return ; case 'security': return ( ); case 'danger': return ( setShowDeleteDialog(true)} /> ); default: return ; } }; return ( {/* Header Section */} setShowNavigation(!showNavigation)} /> {/* Content Area with Sidebar */} {/* Side Navigation - Overlay on mobile, sidebar on desktop */} setShowNavigation(false)} /> {/* Content Panel - Shows only the active section */} {renderActiveSection()} {/* Keyboard Map Dialog */} {/* Delete Project Confirmation Dialog */} ); }