import { useState, useCallback, useEffect } from 'react'; import { Plus, Bug } from 'lucide-react'; import { useNavigate } from '@tanstack/react-router'; import { cn } from '@/lib/utils'; import { useAppStore } from '@/store/app-store'; import { useOSDetection } from '@/hooks/use-os-detection'; import { ProjectSwitcherItem } from './components/project-switcher-item'; import { ProjectContextMenu } from './components/project-context-menu'; import { EditProjectDialog } from './components/edit-project-dialog'; import { NewProjectModal } from '@/components/dialogs/new-project-modal'; import { OnboardingDialog } from '@/components/layout/sidebar/dialogs'; import { useProjectCreation, useProjectTheme } from '@/components/layout/sidebar/hooks'; import type { Project } from '@/lib/electron'; import { getElectronAPI } from '@/lib/electron'; function getOSAbbreviation(os: string): string { switch (os) { case 'mac': return 'M'; case 'windows': return 'W'; case 'linux': return 'L'; default: return '?'; } } export function ProjectSwitcher() { const navigate = useNavigate(); const { projects, currentProject, setCurrentProject, trashedProjects, upsertAndSetCurrentProject, } = useAppStore(); const [contextMenuProject, setContextMenuProject] = useState(null); const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>( null ); const [editDialogProject, setEditDialogProject] = useState(null); // Version info const appVersion = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0'; const { os } = useOSDetection(); const appMode = import.meta.env.VITE_APP_MODE || '?'; const versionSuffix = `${getOSAbbreviation(os)}${appMode}`; // Get global theme for project creation const { globalTheme } = useProjectTheme(); // Project creation state and handlers const { showNewProjectModal, setShowNewProjectModal, isCreatingProject, showOnboardingDialog, setShowOnboardingDialog, newProjectName, handleCreateBlankProject, handleCreateFromTemplate, handleCreateFromCustomUrl, } = useProjectCreation({ trashedProjects, currentProject, globalTheme, upsertAndSetCurrentProject, }); const handleContextMenu = (project: Project, event: React.MouseEvent) => { event.preventDefault(); setContextMenuProject(project); setContextMenuPosition({ x: event.clientX, y: event.clientY }); }; const handleCloseContextMenu = () => { setContextMenuProject(null); setContextMenuPosition(null); }; const handleEditProject = (project: Project) => { setEditDialogProject(project); handleCloseContextMenu(); }; const handleProjectClick = useCallback( (project: Project) => { setCurrentProject(project); // Navigate to board view when switching projects navigate({ to: '/board' }); }, [setCurrentProject, navigate] ); const handleNewProject = () => { // Open the new project modal setShowNewProjectModal(true); }; const handleOnboardingSkip = () => { setShowOnboardingDialog(false); navigate({ to: '/board' }); }; const handleBugReportClick = useCallback(() => { const api = getElectronAPI(); api.openExternalLink('https://github.com/AutoMaker-Org/automaker/issues'); }, []); // Keyboard shortcuts for project switching (1-9, 0) useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { // Ignore if user is typing in an input, textarea, or contenteditable const target = event.target as HTMLElement; if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) { return; } // Ignore if modifier keys are pressed (except for standalone number keys) if (event.ctrlKey || event.metaKey || event.altKey) { return; } // Map key to project index: "1" -> 0, "2" -> 1, ..., "9" -> 8, "0" -> 9 const key = event.key; let projectIndex: number | null = null; if (key >= '1' && key <= '9') { projectIndex = parseInt(key, 10) - 1; // "1" -> 0, "9" -> 8 } else if (key === '0') { projectIndex = 9; // "0" -> 9 } if (projectIndex !== null && projectIndex < projects.length) { const targetProject = projects[projectIndex]; if (targetProject && targetProject.id !== currentProject?.id) { handleProjectClick(targetProject); } } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [projects, currentProject, handleProjectClick]); return ( <> {/* Context Menu */} {contextMenuProject && contextMenuPosition && ( )} {/* Edit Project Dialog */} {editDialogProject && ( !open && setEditDialogProject(null)} /> )} {/* New Project Modal */} {/* Onboarding Dialog */} ); }