1- import { useState , useEffect } from "react" ;
1+ import { useState , useEffect , useCallback } from "react" ;
22import { motion , AnimatePresence } from "framer-motion" ;
33import { Plus , Loader2 , Bot , FolderCode } from "lucide-react" ;
44import { api , type Project , type Session , type ClaudeMdFile } from "@/lib/api" ;
55import { OutputCacheProvider } from "@/lib/outputCache" ;
6+ import { SessionProvider , useSessionContext } from "@/contexts/SessionContext" ;
67import { Button } from "@/components/ui/button" ;
78import { Card } from "@/components/ui/card" ;
89import { ProjectList } from "@/components/ProjectList" ;
@@ -17,14 +18,17 @@ import { UsageDashboard } from "@/components/UsageDashboard";
1718import { MCPManager } from "@/components/MCPManager" ;
1819import { NFOCredits } from "@/components/NFOCredits" ;
1920import { ClaudeBinaryDialog } from "@/components/ClaudeBinaryDialog" ;
21+ import { NavigationConfirmDialog } from "@/components/NavigationConfirmDialog" ;
22+ import { ActiveClaudeSessions } from "@/components/ActiveClaudeSessions" ;
2023import { Toast , ToastContainer } from "@/components/ui/toast" ;
2124
2225type View = "welcome" | "projects" | "agents" | "editor" | "settings" | "claude-file-editor" | "claude-code-session" | "usage-dashboard" | "mcp" ;
2326
2427/**
25- * Main App component - Manages the Claude directory browser UI
28+ * Inner App component that uses the session context
2629 */
27- function App ( ) {
30+ function AppInner ( ) {
31+ const { checkNavigationAllowed, activeSession } = useSessionContext ( ) ;
2832 const [ view , setView ] = useState < View > ( "welcome" ) ;
2933 const [ projects , setProjects ] = useState < Project [ ] > ( [ ] ) ;
3034 const [ selectedProject , setSelectedProject ] = useState < Project | null > ( null ) ;
@@ -36,6 +40,39 @@ function App() {
3640 const [ showNFO , setShowNFO ] = useState ( false ) ;
3741 const [ showClaudeBinaryDialog , setShowClaudeBinaryDialog ] = useState ( false ) ;
3842 const [ toast , setToast ] = useState < { message : string ; type : "success" | "error" | "info" } | null > ( null ) ;
43+ const [ pendingView , setPendingView ] = useState < View | null > ( null ) ;
44+ const [ showNavConfirmDialog , setShowNavConfirmDialog ] = useState ( false ) ;
45+
46+ // Navigation guard handler
47+ const handleNavigation = useCallback ( async ( newView : View ) => {
48+ // Skip check if we're already on the same view or navigating to claude-code-session
49+ if ( view === newView || newView === "claude-code-session" ) {
50+ setView ( newView ) ;
51+ return ;
52+ }
53+
54+ // Check if navigation is allowed when leaving claude-code-session
55+ if ( view === "claude-code-session" && activeSession ?. isActive ) {
56+ setPendingView ( newView ) ;
57+ setShowNavConfirmDialog ( true ) ;
58+ } else {
59+ setView ( newView ) ;
60+ }
61+ } , [ view , activeSession , checkNavigationAllowed ] ) ;
62+
63+ // Handle navigation confirmation
64+ const handleConfirmNavigation = useCallback ( ( ) => {
65+ if ( pendingView ) {
66+ setView ( pendingView ) ;
67+ setPendingView ( null ) ;
68+ }
69+ setShowNavConfirmDialog ( false ) ;
70+ } , [ pendingView ] ) ;
71+
72+ const handleCancelNavigation = useCallback ( ( ) => {
73+ setPendingView ( null ) ;
74+ setShowNavConfirmDialog ( false ) ;
75+ } , [ ] ) ;
3976
4077 // Load projects on mount when in projects view
4178 useEffect ( ( ) => {
@@ -52,7 +89,7 @@ function App() {
5289 const handleSessionSelected = ( event : CustomEvent ) => {
5390 const { session } = event . detail ;
5491 setSelectedSession ( session ) ;
55- setView ( "claude-code-session" ) ;
92+ handleNavigation ( "claude-code-session" ) ;
5693 } ;
5794
5895 const handleClaudeNotFound = ( ) => {
@@ -65,7 +102,7 @@ function App() {
65102 window . removeEventListener ( 'claude-session-selected' , handleSessionSelected as EventListener ) ;
66103 window . removeEventListener ( 'claude-not-found' , handleClaudeNotFound as EventListener ) ;
67104 } ;
68- } , [ ] ) ;
105+ } , [ handleNavigation ] ) ;
69106
70107 /**
71108 * Loads all projects from the ~/.claude/projects directory
@@ -106,7 +143,7 @@ function App() {
106143 * Opens a new Claude Code session in the interactive UI
107144 */
108145 const handleNewSession = async ( ) => {
109- setView ( "claude-code-session" ) ;
146+ handleNavigation ( "claude-code-session" ) ;
110147 setSelectedSession ( null ) ;
111148 } ;
112149
@@ -131,7 +168,7 @@ function App() {
131168 */
132169 const handleBackFromClaudeFileEditor = ( ) => {
133170 setEditingClaudeFile ( null ) ;
134- setView ( "projects" ) ;
171+ handleNavigation ( "projects" ) ;
135172 } ;
136173
137174 const renderContent = ( ) => {
@@ -153,6 +190,11 @@ function App() {
153190 </ h1 >
154191 </ motion . div >
155192
193+ { /* Active Sessions */ }
194+ < div className = "mb-8" >
195+ < ActiveClaudeSessions />
196+ </ div >
197+
156198 { /* Navigation Cards */ }
157199 < div className = "grid grid-cols-1 md:grid-cols-2 gap-6 max-w-2xl mx-auto" >
158200 { /* CC Agents Card */ }
@@ -163,7 +205,7 @@ function App() {
163205 >
164206 < Card
165207 className = "h-64 cursor-pointer transition-all duration-200 hover:scale-105 hover:shadow-lg border border-border/50 shimmer-hover"
166- onClick = { ( ) => setView ( "agents" ) }
208+ onClick = { ( ) => handleNavigation ( "agents" ) }
167209 >
168210 < div className = "h-full flex flex-col items-center justify-center p-8" >
169211 < Bot className = "h-16 w-16 mb-4 text-primary" />
@@ -180,7 +222,7 @@ function App() {
180222 >
181223 < Card
182224 className = "h-64 cursor-pointer transition-all duration-200 hover:scale-105 hover:shadow-lg border border-border/50 shimmer-hover"
183- onClick = { ( ) => setView ( "projects" ) }
225+ onClick = { ( ) => handleNavigation ( "projects" ) }
184226 >
185227 < div className = "h-full flex flex-col items-center justify-center p-8" >
186228 < FolderCode className = "h-16 w-16 mb-4 text-primary" />
@@ -197,21 +239,21 @@ function App() {
197239 case "agents" :
198240 return (
199241 < div className = "flex-1 overflow-hidden" >
200- < CCAgents onBack = { ( ) => setView ( "welcome" ) } />
242+ < CCAgents onBack = { ( ) => handleNavigation ( "welcome" ) } />
201243 </ div >
202244 ) ;
203245
204246 case "editor" :
205247 return (
206248 < div className = "flex-1 overflow-hidden" >
207- < MarkdownEditor onBack = { ( ) => setView ( "welcome" ) } />
249+ < MarkdownEditor onBack = { ( ) => handleNavigation ( "welcome" ) } />
208250 </ div >
209251 ) ;
210252
211253 case "settings" :
212254 return (
213255 < div className = "flex-1 flex flex-col" style = { { minHeight : 0 } } >
214- < Settings onBack = { ( ) => setView ( "welcome" ) } />
256+ < Settings onBack = { ( ) => handleNavigation ( "welcome" ) } />
215257 </ div >
216258 ) ;
217259
@@ -229,7 +271,7 @@ function App() {
229271 < Button
230272 variant = "ghost"
231273 size = "sm"
232- onClick = { ( ) => setView ( "welcome" ) }
274+ onClick = { ( ) => handleNavigation ( "welcome" ) }
233275 className = "mb-4"
234276 >
235277 ← Back to Home
@@ -338,19 +380,19 @@ function App() {
338380 session = { selectedSession || undefined }
339381 onBack = { ( ) => {
340382 setSelectedSession ( null ) ;
341- setView ( "projects" ) ;
383+ handleNavigation ( "projects" ) ;
342384 } }
343385 />
344386 ) ;
345387
346388 case "usage-dashboard" :
347389 return (
348- < UsageDashboard onBack = { ( ) => setView ( "welcome" ) } />
390+ < UsageDashboard onBack = { ( ) => handleNavigation ( "welcome" ) } />
349391 ) ;
350392
351393 case "mcp" :
352394 return (
353- < MCPManager onBack = { ( ) => setView ( "welcome" ) } />
395+ < MCPManager onBack = { ( ) => handleNavigation ( "welcome" ) } />
354396 ) ;
355397
356398 default :
@@ -363,10 +405,10 @@ function App() {
363405 < div className = "h-screen bg-background flex flex-col" >
364406 { /* Topbar */ }
365407 < Topbar
366- onClaudeClick = { ( ) => setView ( "editor" ) }
367- onSettingsClick = { ( ) => setView ( "settings" ) }
368- onUsageClick = { ( ) => setView ( "usage-dashboard" ) }
369- onMCPClick = { ( ) => setView ( "mcp" ) }
408+ onClaudeClick = { ( ) => handleNavigation ( "editor" ) }
409+ onSettingsClick = { ( ) => handleNavigation ( "settings" ) }
410+ onUsageClick = { ( ) => handleNavigation ( "usage-dashboard" ) }
411+ onMCPClick = { ( ) => handleNavigation ( "mcp" ) }
370412 onInfoClick = { ( ) => setShowNFO ( true ) }
371413 />
372414
@@ -400,9 +442,27 @@ function App() {
400442 />
401443 ) }
402444 </ ToastContainer >
445+
446+ { /* Navigation Confirmation Dialog */ }
447+ < NavigationConfirmDialog
448+ open = { showNavConfirmDialog }
449+ onConfirm = { handleConfirmNavigation }
450+ onCancel = { handleCancelNavigation }
451+ />
403452 </ div >
404453 </ OutputCacheProvider >
405454 ) ;
406455}
407456
457+ /**
458+ * Main App component - Manages the Claude directory browser UI
459+ */
460+ function App ( ) {
461+ return (
462+ < SessionProvider >
463+ < AppInner />
464+ </ SessionProvider >
465+ ) ;
466+ }
467+
408468export default App ;
0 commit comments