* AppContent component - Contains the main app logic, wrapped by providers
()
| 46 | * AppContent component - Contains the main app logic, wrapped by providers |
| 47 | */ |
| 48 | function AppContent() { |
| 49 | const [view, setView] = useState<View>("tabs"); |
| 50 | const { createClaudeMdTab, createSettingsTab, createUsageTab, createMCPTab, createAgentsTab } = useTabState(); |
| 51 | const [projects, setProjects] = useState<Project[]>([]); |
| 52 | const [selectedProject, setSelectedProject] = useState<Project | null>(null); |
| 53 | const [sessions, setSessions] = useState<Session[]>([]); |
| 54 | const [editingClaudeFile, setEditingClaudeFile] = useState<ClaudeMdFile | null>(null); |
| 55 | const [loading, setLoading] = useState(true); |
| 56 | const [_error, setError] = useState<string | null>(null); |
| 57 | const [showNFO, setShowNFO] = useState(false); |
| 58 | const [showClaudeBinaryDialog, setShowClaudeBinaryDialog] = useState(false); |
| 59 | const [showProjectPicker, setShowProjectPicker] = useState(false); |
| 60 | const [homeDirectory, setHomeDirectory] = useState<string>('/'); |
| 61 | const [toast, setToast] = useState<{ message: string; type: "success" | "error" | "info" } | null>(null); |
| 62 | const [projectForSettings, setProjectForSettings] = useState<Project | null>(null); |
| 63 | const [previousView] = useState<View>("welcome"); |
| 64 | |
| 65 | // Initialize analytics lifecycle tracking |
| 66 | useAppLifecycle(); |
| 67 | const trackEvent = useTrackEvent(); |
| 68 | |
| 69 | // Track user journey milestones |
| 70 | const [hasTrackedFirstChat] = useState(false); |
| 71 | // const [hasTrackedFirstAgent] = useState(false); |
| 72 | |
| 73 | // Track when user reaches different journey stages |
| 74 | useEffect(() => { |
| 75 | if (view === "projects" && projects.length > 0 && !hasTrackedFirstChat) { |
| 76 | // User has projects - they're past onboarding |
| 77 | trackEvent.journeyMilestone({ |
| 78 | journey_stage: 'onboarding', |
| 79 | milestone_reached: 'projects_created', |
| 80 | time_to_milestone_ms: Date.now() - performance.timing.navigationStart |
| 81 | }); |
| 82 | } |
| 83 | }, [view, projects.length, hasTrackedFirstChat, trackEvent]); |
| 84 | |
| 85 | // Load projects on mount when in projects view |
| 86 | useEffect(() => { |
| 87 | if (view === "projects") { |
| 88 | loadProjects(); |
| 89 | } else if (view === "welcome") { |
| 90 | // Reset loading state for welcome view |
| 91 | setLoading(false); |
| 92 | } |
| 93 | }, [view]); |
| 94 | |
| 95 | // Keyboard shortcuts for tab navigation |
| 96 | useEffect(() => { |
| 97 | if (view !== "tabs") return; |
| 98 | |
| 99 | const handleKeyDown = (e: KeyboardEvent) => { |
| 100 | const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; |
| 101 | const modKey = isMac ? e.metaKey : e.ctrlKey; |
| 102 | |
| 103 | if (modKey) { |
| 104 | switch (e.key) { |
| 105 | case 't': |
nothing calls this directly
no test coverage detected