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
This commit is contained in:
ZacharyZcR
2025-11-09 08:11:29 +08:00
parent cd1afc9078
commit fe127e045f
3 changed files with 64 additions and 14 deletions

View File

@@ -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 && (
<LeftSidebar
onSelectView={handleSelectView}
disabled={!isAuthenticated || authLoading}
isAdmin={isAdmin}
username={username}
>
onSelectView={handleSelectView}
disabled={!isAuthenticated || authLoading}
isAdmin={isAdmin}
username={username}
onLogout={handleLogout}
>
<div
className="h-screen w-full visible pointer-events-auto static overflow-hidden"
style={{ display: showTerminalView ? "block" : "none" }}
>
<AppView isTopbarOpen={isTopbarOpen} />
{showTerminalView && (
<div className="animate-in fade-in slide-in-from-right-4 duration-300">
<AppView isTopbarOpen={isTopbarOpen} />
</div>
)}
</div>
{showHome && (
<div className="h-screen w-full visible pointer-events-auto static overflow-hidden">
<div className="h-screen w-full visible pointer-events-auto static overflow-hidden animate-in fade-in slide-in-from-right-4 duration-300">
<Dashboard
onSelectView={handleSelectView}
isAuthenticated={isAuthenticated}
@@ -160,7 +198,7 @@ function AppContent() {
)}
{showSshManager && (
<div className="h-screen w-full visible pointer-events-auto static overflow-hidden">
<div className="h-screen w-full visible pointer-events-auto static overflow-hidden animate-in fade-in slide-in-from-right-4 duration-300">
<HostManager
onSelectView={handleSelectView}
isTopbarOpen={isTopbarOpen}
@@ -171,13 +209,13 @@ function AppContent() {
)}
{showAdmin && (
<div className="h-screen w-full visible pointer-events-auto static overflow-hidden">
<div className="h-screen w-full visible pointer-events-auto static overflow-hidden animate-in fade-in slide-in-from-right-4 duration-300">
<AdminSettings isTopbarOpen={isTopbarOpen} />
</div>
)}
{showProfile && (
<div className="h-screen w-full visible pointer-events-auto static overflow-auto">
<div className="h-screen w-full visible pointer-events-auto static overflow-auto animate-in fade-in slide-in-from-right-4 duration-300">
<UserProfile isTopbarOpen={isTopbarOpen} />
</div>
)}
@@ -189,6 +227,15 @@ function AppContent() {
/>
</LeftSidebar>
)}
{isTransitioning && (
<div
className={`fixed inset-0 bg-background z-[20000] transition-opacity duration-300 ${
transitionPhase === 'fadeOut' ? 'opacity-100' : 'opacity-0'
}`}
/>
)}
<Toaster
position="bottom-right"
richColors={false}