1- import { useState , useEffect } from 'react' ;
1+ import { useState , useEffect , useCallback } from 'react' ;
2+ import { invoke } from '@tauri-apps/api/core' ;
23import {
34 Home ,
45 Server ,
@@ -11,6 +12,8 @@ import {
1112 Loader2 ,
1213 FolderOpen ,
1314 FileText ,
15+ Download ,
16+ X ,
1417} from 'lucide-react' ;
1518import {
1619 AppShell ,
@@ -82,6 +85,7 @@ function AppContent() {
8285 useDataSync ( ) ;
8386
8487 const [ activeNav , setActiveNav ] = useState < NavItem > ( 'home' ) ;
88+ const [ availableUpdate , setAvailableUpdate ] = useState < { version : string } | null > ( null ) ;
8589
8690 // Auto-check for updates on startup (silent check after 5 seconds)
8791 useEffect ( ( ) => {
@@ -91,7 +95,7 @@ function AppContent() {
9195 const update = await check ( ) ;
9296 if ( update ) {
9397 console . log ( `[Auto-Update] Update available: ${ update . version } ` ) ;
94- // User can check Settings page to see the update
98+ setAvailableUpdate ( { version : update . version } ) ;
9599 }
96100 } catch ( error ) {
97101 console . error ( '[Auto-Update] Failed to check for updates:' , error ) ;
@@ -106,6 +110,39 @@ function AppContent() {
106110 const theme = useTheme ( ) ;
107111 const setTheme = useAppStore ( ( state ) => state . setTheme ) ;
108112 const activeSpace = useActiveSpace ( ) ;
113+ const viewSpace = useViewSpace ( ) ;
114+
115+ // App version from Rust backend
116+ const [ appVersion , setAppVersion ] = useState ( '' ) ;
117+ useEffect ( ( ) => {
118+ invoke < string > ( 'get_version' )
119+ . then ( setAppVersion )
120+ . catch ( ( err ) => console . error ( 'Failed to get version:' , err ) ) ;
121+ } , [ ] ) ;
122+
123+ // Gateway status for sidebar footer
124+ const [ gatewayUrl , setGatewayUrl ] = useState < string | null > ( null ) ;
125+ const loadGatewayUrl = useCallback ( async ( ) => {
126+ try {
127+ const { getGatewayStatus } = await import ( '@/lib/api/gateway' ) ;
128+ const status = await getGatewayStatus ( viewSpace ?. id ) ;
129+ setGatewayUrl ( status . running && status . url ? status . url : null ) ;
130+ } catch {
131+ setGatewayUrl ( null ) ;
132+ }
133+ } , [ viewSpace ?. id ] ) ;
134+
135+ useEffect ( ( ) => {
136+ loadGatewayUrl ( ) ;
137+ } , [ loadGatewayUrl ] ) ;
138+
139+ useGatewayEvents ( ( payload ) => {
140+ if ( payload . action === 'started' ) {
141+ setGatewayUrl ( payload . url || null ) ;
142+ } else if ( payload . action === 'stopped' ) {
143+ setGatewayUrl ( null ) ;
144+ }
145+ } ) ;
109146
110147 // Toggle dark mode
111148 const toggleDarkMode = ( ) => {
@@ -119,8 +156,8 @@ function AppContent() {
119156 }
120157 footer = {
121158 < div className = "text-xs text-[rgb(var(--muted))]" >
122- < div > McpMux v0.1.0 </ div >
123- < div > Gateway: localhost:9315 </ div >
159+ < div > McpMux{ appVersion ? ` v ${ appVersion } ` : '' } </ div >
160+ < div > Gateway: { gatewayUrl ?? 'Not running' } </ div >
124161 </ div >
125162 }
126163 >
@@ -234,6 +271,36 @@ function AppContent() {
234271 }
235272 >
236273 < div className = "animate-fade-in" >
274+ { availableUpdate && (
275+ < div
276+ className = "flex items-center justify-between gap-3 px-4 py-2.5 bg-blue-500/10 border-b border-blue-500/20 text-sm"
277+ data-testid = "update-banner"
278+ >
279+ < div className = "flex items-center gap-2" >
280+ < Download className = "h-4 w-4 text-blue-500 flex-shrink-0" />
281+ < span >
282+ McpMux < strong > v{ availableUpdate . version } </ strong > is available.
283+ </ span >
284+ < button
285+ onClick = { ( ) => {
286+ setActiveNav ( 'settings' ) ;
287+ setAvailableUpdate ( null ) ;
288+ } }
289+ className = "text-blue-500 hover:text-blue-400 font-medium underline underline-offset-2"
290+ >
291+ Update now
292+ </ button >
293+ </ div >
294+ < button
295+ onClick = { ( ) => setAvailableUpdate ( null ) }
296+ className = "text-[rgb(var(--muted))] hover:text-[rgb(var(--foreground))] transition-colors flex-shrink-0"
297+ aria-label = "Dismiss update notification"
298+ data-testid = "dismiss-update-banner"
299+ >
300+ < X className = "h-4 w-4" />
301+ </ button >
302+ </ div >
303+ ) }
237304 { activeNav === 'home' && < DashboardView /> }
238305 { activeNav === 'registry' && < RegistryPage /> }
239306 { activeNav === 'servers' && < ServersPage /> }
0 commit comments