From fe127e045ff72821eb2c120443eb55249480ea90 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sun, 9 Nov 2025 08:11:29 +0800 Subject: [PATCH] feat: Add smooth macOS-style page transitions - Add fullscreen crossfade transition for login/logout (300ms fade-out + 400ms fade-in) - Add slide-in-from-right animation for all page switches (Dashboard, Terminal, SSH Manager, Admin, Profile) - Fix TypeScript compilation by adding esModuleInterop to tsconfig.node.json - Pass handleLogout from DesktopApp to LeftSidebar for consistent transition behavior All page transitions now use Tailwind animate-in utilities with 300ms duration for smooth, native-feeling UX --- src/ui/desktop/DesktopApp.tsx | 73 +++++++++++++++++++---- src/ui/desktop/navigation/LeftSidebar.tsx | 4 +- tsconfig.node.json | 1 + 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/ui/desktop/DesktopApp.tsx b/src/ui/desktop/DesktopApp.tsx index b7bcb6de..093ba6d5 100644 --- a/src/ui/desktop/DesktopApp.tsx +++ b/src/ui/desktop/DesktopApp.tsx @@ -23,6 +23,8 @@ function AppContent() { const saved = localStorage.getItem("topNavbarOpen"); return saved !== null ? JSON.parse(saved) : true; }); + const [isTransitioning, setIsTransitioning] = useState(false); + const [transitionPhase, setTransitionPhase] = useState<'idle' | 'fadeOut' | 'fadeIn'>('idle'); const { currentTab, tabs } = useTabs(); const [isCommandPaletteOpen, setIsCommandPaletteOpen] = useState(false); @@ -98,13 +100,44 @@ function AppContent() { username: string | null; userId: string | null; }) => { - setIsAuthenticated(true); - setIsAdmin(authData.isAdmin); - setUsername(authData.username); + setIsTransitioning(true); + setTransitionPhase('fadeOut'); + + setTimeout(() => { + setIsAuthenticated(true); + setIsAdmin(authData.isAdmin); + setUsername(authData.username); + setTransitionPhase('fadeIn'); + + setTimeout(() => { + setIsTransitioning(false); + setTransitionPhase('idle'); + }, 400); + }, 300); }, [], ); + const handleLogout = useCallback(async () => { + setIsTransitioning(true); + setTransitionPhase('fadeOut'); + + setTimeout(async () => { + try { + const { logoutUser, isElectron } = await import("@/ui/main-axios.ts"); + await logoutUser(); + + if (isElectron()) { + localStorage.removeItem("jwt"); + } + } catch (error) { + console.error("Logout failed:", error); + } + + window.location.reload(); + }, 300); + }, []); + const currentTabData = tabs.find((tab) => tab.id === currentTab); const showTerminalView = currentTabData?.type === "terminal" || @@ -135,20 +168,25 @@ function AppContent() { {isAuthenticated && ( + onSelectView={handleSelectView} + disabled={!isAuthenticated || authLoading} + isAdmin={isAdmin} + username={username} + onLogout={handleLogout} + >
- + {showTerminalView && ( +
+ +
+ )}
{showHome && ( -
+
+
+
)} {showProfile && ( -
+
)} @@ -189,6 +227,15 @@ function AppContent() { /> )} + + {isTransitioning && ( +
+ )} + void; } async function handleLogout() { @@ -87,6 +88,7 @@ export function LeftSidebar({ isAdmin, username, children, + onLogout, }: SidebarProps): React.ReactElement { const { t } = useTranslation(); @@ -486,7 +488,7 @@ export function LeftSidebar({ )} {t("common.logout")} diff --git a/tsconfig.node.json b/tsconfig.node.json index 4e53dcad..2edfb287 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -8,6 +8,7 @@ "moduleResolution": "nodenext", "verbatimModuleSyntax": true, "moduleDetection": "force", + "esModuleInterop": true, "noEmit": false, "outDir": "./dist/backend", "strict": false,