From 56acc96229eafbf59734b565da666a234e120387 Mon Sep 17 00:00:00 2001 From: nessi Date: Wed, 18 Mar 2026 10:34:12 +0100 Subject: [PATCH] refactor: reduce window size and remove transfer metrics display from main UI Reduce default window dimensions from 1120x760 to 940x640 pixels and disable resizing. Remove TunnelMetrics and RawTunnelMetrics types, formatDataSize and normalizeTunnelMetrics helpers, and all transfer statistics tracking from App component. Replace refreshTunnelMetrics with simpler refreshTunnelStatus that only queries tunnel active state. Remove received/sent data display cards from status panel and eliminate metrics --- desktop-client/src-tauri/tauri.conf.json | 4 +- desktop-client/src/App.tsx | 86 +++--------------------- desktop-client/src/styles.css | 43 +++++++----- 3 files changed, 37 insertions(+), 96 deletions(-) diff --git a/desktop-client/src-tauri/tauri.conf.json b/desktop-client/src-tauri/tauri.conf.json index 2a4d652..352e3e7 100644 --- a/desktop-client/src-tauri/tauri.conf.json +++ b/desktop-client/src-tauri/tauri.conf.json @@ -13,8 +13,8 @@ "windows": [ { "title": "NexaVPN", - "width": 1120, - "height": 760, + "width": 940, + "height": 640, "resizable": false, "maximizable": false } diff --git a/desktop-client/src/App.tsx b/desktop-client/src/App.tsx index 22becc2..8684925 100644 --- a/desktop-client/src/App.tsx +++ b/desktop-client/src/App.tsx @@ -11,20 +11,6 @@ type EnrollmentState = { tunnelStrategy: string; }; -type TunnelMetrics = { - active: boolean; - rxBytes: number; - txBytes: number; -}; - -type RawTunnelMetrics = { - active?: boolean; - rxBytes?: number; - txBytes?: number; - rx_bytes?: number; - tx_bytes?: number; -}; - function formatInvokeError(err: unknown, fallback: string) { if (typeof err === "string" && err.trim().length > 0) { return err; @@ -58,31 +44,6 @@ function currentProfileLabel(state: EnrollmentState | null) { return `Split tunnel (${state.resources.length} resources)`; } -function formatDataSize(bytes: number) { - if (!bytes) { - return "0 B"; - } - - const units = ["B", "KB", "MB", "GB", "TB"]; - let value = bytes; - let unitIndex = 0; - - while (value >= 1024 && unitIndex < units.length - 1) { - value /= 1024; - unitIndex += 1; - } - - return `${value >= 100 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`; -} - -function normalizeTunnelMetrics(value: RawTunnelMetrics | null | undefined): TunnelMetrics { - return { - active: Boolean(value?.active), - rxBytes: Number(value?.rxBytes ?? value?.rx_bytes ?? 0), - txBytes: Number(value?.txBytes ?? value?.tx_bytes ?? 0) - }; -} - export function App() { const [serverUrl, setServerUrl] = useState("http://localhost"); const [username, setUsername] = useState(""); @@ -92,26 +53,12 @@ export function App() { const [error, setError] = useState(null); const [connected, setConnected] = useState(false); const [state, setState] = useState(null); - const [metrics, setMetrics] = useState({ - active: false, - rxBytes: 0, - txBytes: 0 - }); - - async function refreshTunnelMetrics() { + async function refreshTunnelStatus() { try { - const value = normalizeTunnelMetrics(await invoke("tunnel_metrics")); - setMetrics(value); - setConnected(value.active); + const active = await invoke("tunnel_status"); + setConnected(active); } catch { - try { - const active = await invoke("tunnel_status"); - setConnected(active); - setMetrics((current) => ({ ...current, active: Boolean(active) })); - } catch { - setMetrics({ active: false, rxBytes: 0, txBytes: 0 }); - setConnected(false); - } + setConnected(false); } } @@ -120,7 +67,7 @@ export function App() { .then(async (value) => { if (value) { setState(value); - await refreshTunnelMetrics(); + await refreshTunnelStatus(); } }) .catch(() => undefined); @@ -128,13 +75,13 @@ export function App() { useEffect(() => { if (!state) { - setMetrics({ active: false, rxBytes: 0, txBytes: 0 }); + setConnected(false); return undefined; } - void refreshTunnelMetrics(); + void refreshTunnelStatus(); const timer = window.setInterval(() => { - void refreshTunnelMetrics(); + void refreshTunnelStatus(); }, 5000); return () => window.clearInterval(timer); @@ -148,12 +95,10 @@ export function App() { const active = await invoke("tunnel_status"); setConnected(active); if (active === expected) { - await refreshTunnelMetrics(); return active; } } catch { if (!expected) { - setMetrics({ active: false, rxBytes: 0, txBytes: 0 }); setConnected(false); return false; } @@ -165,7 +110,6 @@ export function App() { return invoke("tunnel_status") .then(async (active) => { setConnected(active); - await refreshTunnelMetrics(); return active; }) .catch(() => false); @@ -181,7 +125,6 @@ export function App() { payload: { serverUrl, username, password } }); setState(result); - setMetrics({ active: false, rxBytes: 0, txBytes: 0 }); } catch (err) { setError(formatInvokeError(err, "Enrollment failed")); } finally { @@ -196,7 +139,7 @@ export function App() { try { const result = await invoke("sync_profile"); setState(result); - await refreshTunnelMetrics(); + await refreshTunnelStatus(); } catch (err) { setError(formatInvokeError(err, "Profile sync failed")); } finally { @@ -240,7 +183,6 @@ export function App() { await invoke("clear_session"); setConnected(false); setState(null); - setMetrics({ active: false, rxBytes: 0, txBytes: 0 }); setError(null); } catch (err) { setError(formatInvokeError(err, "Unable to clear local profile")); @@ -343,14 +285,6 @@ export function App() { Last sync {state.lastSyncTime} -
- Received - {formatDataSize(metrics.rxBytes)} -
-
- Sent - {formatDataSize(metrics.txBytes)} -
@@ -365,7 +299,7 @@ export function App() {

Resources

-

Press Sync after policy changes.

+

Press Sync after policy changes.

    {(state?.resources ?? ["No resources assigned yet"]).map((resource) => (
  • {resource}
  • diff --git a/desktop-client/src/styles.css b/desktop-client/src/styles.css index 28c59c5..957afc3 100644 --- a/desktop-client/src/styles.css +++ b/desktop-client/src/styles.css @@ -28,14 +28,14 @@ input { .client-shell { min-height: 100vh; - padding: 28px; + padding: 18px; } .app-frame { - width: min(1120px, 100%); + width: min(920px, 100%); margin: 0 auto; display: grid; - gap: 18px; + gap: 14px; } .top-strip { @@ -88,7 +88,7 @@ input { .top-actions { display: flex; - gap: 10px; + gap: 8px; flex-wrap: wrap; } @@ -96,7 +96,7 @@ input { .shell-button-secondary { border: 0; border-radius: 999px; - padding: 12px 18px; + padding: 10px 16px; font-weight: 700; cursor: pointer; transition: 160ms ease; @@ -130,16 +130,16 @@ input { .body-grid { display: grid; - grid-template-columns: 1.3fr 0.7fr; - gap: 22px; + grid-template-columns: minmax(0, 1fr) 290px; + gap: 16px; } .login-panel, .status-panel { border-radius: 28px; - padding: 24px; + padding: 18px; display: grid; - gap: 20px; + gap: 14px; } .status-panel > p { @@ -178,9 +178,9 @@ input { .surface { border-radius: 24px; - padding: 18px; + padding: 14px; display: grid; - gap: 14px; + gap: 12px; } .surface-header { @@ -199,22 +199,22 @@ input { .profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; + gap: 10px; } .detail-card, .profile-card { - padding: 14px; - border-radius: 18px; + padding: 12px; + border-radius: 16px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(177, 197, 229, 0.1); display: grid; - gap: 6px; + gap: 5px; } .detail-card strong, .profile-card strong { - font-size: 1rem; + font-size: 0.98rem; word-break: break-word; } @@ -223,16 +223,23 @@ input { padding: 0; list-style: none; display: grid; - gap: 10px; + gap: 8px; } .resource-stack li { - padding: 14px 16px; + padding: 12px 14px; border-radius: 16px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(177, 197, 229, 0.1); color: #eef4ff; word-break: break-word; + min-height: 54px; + display: flex; + align-items: center; +} + +.access-hint { + font-size: 0.95rem; } .login-panel form {