Skip to content

Commit 1db1e2c

Browse files
committed
fix: show dynamic version and gateway URL in sidebar, prompt for updates on startup
The sidebar footer previously hardcoded "McpMux v0.1.0" and "Gateway: localhost:9315" which never reflected the actual app version or gateway URL. Now fetches the real version via get_version Tauri command and tracks gateway status reactively via getGatewayStatus + useGatewayEvents. Also surfaces a dismissible update banner at the top of the content area when a new version is detected on startup. https://claude.ai/code/session_01K876wXjp55HfRLDCAG9FmU Signed-off-by: Claude <noreply@anthropic.com>
1 parent 2d99f14 commit 1db1e2c

1 file changed

Lines changed: 71 additions & 4 deletions

File tree

apps/desktop/src/App.tsx

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { useState, useEffect } from 'react';
1+
import { useState, useEffect, useCallback } from 'react';
2+
import { invoke } from '@tauri-apps/api/core';
23
import {
34
Home,
45
Server,
@@ -12,6 +13,8 @@ import {
1213
Loader2,
1314
FolderOpen,
1415
FileText,
16+
Download,
17+
X,
1518
} from 'lucide-react';
1619
import {
1720
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

Comments
 (0)