Skip to content

Commit 9cabf8a

Browse files
committed
Add impressive features: loading screen, scroll-to-top, keyboard shortcuts, animated stats, interactive timeline, and enhanced visuals
1 parent 73e2c2f commit 9cabf8a

File tree

7 files changed

+435
-59
lines changed

7 files changed

+435
-59
lines changed

src/App.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,30 @@ import Auth from "./pages/Auth";
1414
import Contact from "./pages/Contact";
1515
import NotFound from "./pages/NotFound";
1616
import Starfield from "@/components/ui/Starfield";
17+
import LoadingScreen from "@/components/ui/LoadingScreen";
18+
import KeyboardShortcuts from "@/components/ui/KeyboardShortcuts";
19+
import { useState, useEffect } from "react";
1720

1821

1922

2023
const queryClient = new QueryClient();
2124

22-
const App = () => (
25+
const App = () => {
26+
const [isLoading, setIsLoading] = useState(true);
27+
28+
useEffect(() => {
29+
const timer = setTimeout(() => {
30+
setIsLoading(false);
31+
}, 2000);
32+
33+
return () => clearTimeout(timer);
34+
}, []);
35+
36+
if (isLoading) {
37+
return <LoadingScreen />;
38+
}
39+
40+
return (
2341
<QueryClientProvider client={queryClient}>
2442
<TooltipProvider>
2543
<AuthProvider>
@@ -28,6 +46,7 @@ const App = () => (
2846
{/* global SPA starfield overlay (behind content) */}
2947
<Starfield />
3048
<Sonner />
49+
<KeyboardShortcuts />
3150
<HashRouter>
3251
<Routes>
3352
<Route path="/" element={<Home />} />
@@ -45,6 +64,7 @@ const App = () => (
4564
</AuthProvider>
4665
</TooltipProvider>
4766
</QueryClientProvider>
48-
);
67+
);
68+
};
4969

5070
export default App;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { useEffect, useState } from "react";
2+
import { Card } from "./card";
3+
import { Command, X } from "lucide-react";
4+
5+
const KeyboardShortcuts = () => {
6+
const [isOpen, setIsOpen] = useState(false);
7+
8+
useEffect(() => {
9+
const handleKeyPress = (e: KeyboardEvent) => {
10+
// Toggle shortcuts panel with Ctrl+K or Cmd+K
11+
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
12+
e.preventDefault();
13+
setIsOpen(prev => !prev);
14+
}
15+
16+
// Close with Escape
17+
if (e.key === 'Escape' && isOpen) {
18+
setIsOpen(false);
19+
}
20+
21+
// Navigation shortcuts (only when shortcuts panel is closed)
22+
if (!isOpen) {
23+
if ((e.ctrlKey || e.metaKey) && e.key === 'h') {
24+
e.preventDefault();
25+
window.location.hash = '#/';
26+
}
27+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
28+
e.preventDefault();
29+
window.location.hash = '#/simulation';
30+
}
31+
if ((e.ctrlKey || e.metaKey) && e.key === 't') {
32+
e.preventDefault();
33+
window.location.hash = '#/theory';
34+
}
35+
if ((e.ctrlKey || e.metaKey) && e.key === 'q') {
36+
e.preventDefault();
37+
window.location.hash = '#/quiz';
38+
}
39+
}
40+
};
41+
42+
window.addEventListener('keydown', handleKeyPress);
43+
return () => window.removeEventListener('keydown', handleKeyPress);
44+
}, [isOpen]);
45+
46+
if (!isOpen) {
47+
return (
48+
<button
49+
onClick={() => setIsOpen(true)}
50+
className="fixed bottom-8 left-8 z-50 bg-secondary/80 hover:bg-secondary backdrop-blur-sm p-3 rounded-full shadow-lg hover:shadow-xl transition-all group"
51+
title="Keyboard Shortcuts (Ctrl+K)"
52+
>
53+
<Command className="w-5 h-5 text-white group-hover:scale-110 transition-transform" />
54+
</button>
55+
);
56+
}
57+
58+
return (
59+
<div className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
60+
<Card className="bg-card/95 backdrop-blur-md border-primary/30 p-6 max-w-2xl w-full shadow-2xl">
61+
<div className="flex items-center justify-between mb-6">
62+
<div className="flex items-center gap-3">
63+
<Command className="w-6 h-6 text-primary" />
64+
<h2 className="text-2xl font-bold gradient-text">Keyboard Shortcuts</h2>
65+
</div>
66+
<button
67+
onClick={() => setIsOpen(false)}
68+
className="text-muted-foreground hover:text-white transition-colors"
69+
>
70+
<X className="w-5 h-5" />
71+
</button>
72+
</div>
73+
74+
<div className="space-y-4">
75+
<div>
76+
<h3 className="text-sm font-semibold text-muted-foreground mb-3">NAVIGATION</h3>
77+
<div className="space-y-2">
78+
{[
79+
{ keys: ['Ctrl', 'H'], desc: 'Go to Home' },
80+
{ keys: ['Ctrl', 'S'], desc: 'Go to Simulation' },
81+
{ keys: ['Ctrl', 'T'], desc: 'Go to Theory' },
82+
{ keys: ['Ctrl', 'Q'], desc: 'Go to Quiz' },
83+
].map((shortcut, index) => (
84+
<div key={index} className="flex items-center justify-between p-3 bg-background/40 rounded-lg hover:bg-background/60 transition-colors">
85+
<span className="text-white/90">{shortcut.desc}</span>
86+
<div className="flex gap-2">
87+
{shortcut.keys.map((key, i) => (
88+
<kbd key={i} className="px-3 py-1.5 bg-primary/20 text-primary text-sm font-semibold rounded border border-primary/30">
89+
{key}
90+
</kbd>
91+
))}
92+
</div>
93+
</div>
94+
))}
95+
</div>
96+
</div>
97+
98+
<div>
99+
<h3 className="text-sm font-semibold text-muted-foreground mb-3">GENERAL</h3>
100+
<div className="space-y-2">
101+
{[
102+
{ keys: ['Ctrl', 'K'], desc: 'Toggle Shortcuts Panel' },
103+
{ keys: ['Esc'], desc: 'Close Modals' },
104+
].map((shortcut, index) => (
105+
<div key={index} className="flex items-center justify-between p-3 bg-background/40 rounded-lg hover:bg-background/60 transition-colors">
106+
<span className="text-white/90">{shortcut.desc}</span>
107+
<div className="flex gap-2">
108+
{shortcut.keys.map((key, i) => (
109+
<kbd key={i} className="px-3 py-1.5 bg-accent/20 text-accent text-sm font-semibold rounded border border-accent/30">
110+
{key}
111+
</kbd>
112+
))}
113+
</div>
114+
</div>
115+
))}
116+
</div>
117+
</div>
118+
119+
<p className="text-xs text-muted-foreground text-center pt-4">
120+
Use <kbd className="px-2 py-1 bg-secondary/20 text-secondary text-xs rounded">Cmd</kbd> instead of <kbd className="px-2 py-1 bg-secondary/20 text-secondary text-xs rounded">Ctrl</kbd> on Mac
121+
</p>
122+
</div>
123+
</Card>
124+
</div>
125+
);
126+
};
127+
128+
export default KeyboardShortcuts;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useEffect, useState } from "react";
2+
import { Atom } from "lucide-react";
3+
4+
const LoadingScreen = () => {
5+
const [progress, setProgress] = useState(0);
6+
7+
useEffect(() => {
8+
const interval = setInterval(() => {
9+
setProgress((prev) => {
10+
if (prev >= 100) {
11+
clearInterval(interval);
12+
return 100;
13+
}
14+
return prev + 2;
15+
});
16+
}, 20);
17+
18+
return () => clearInterval(interval);
19+
}, []);
20+
21+
return (
22+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black">
23+
<div className="starfield" />
24+
<div className="relative z-10 text-center space-y-8">
25+
<div className="relative">
26+
<Atom className="w-24 h-24 text-primary mx-auto animate-spin" style={{ animationDuration: '3s' }} />
27+
<div className="absolute inset-0 flex items-center justify-center">
28+
<div className="w-32 h-32 rounded-full border-4 border-primary/20 border-t-primary animate-spin" />
29+
</div>
30+
</div>
31+
32+
<div className="space-y-3">
33+
<h2 className="text-3xl font-bold gradient-text">
34+
Initializing Quantum System
35+
</h2>
36+
<p className="text-muted-foreground text-lg">
37+
Preparing BB84 Simulator...
38+
</p>
39+
</div>
40+
41+
<div className="w-64 mx-auto">
42+
<div className="h-2 bg-secondary/20 rounded-full overflow-hidden">
43+
<div
44+
className="h-full bg-gradient-to-r from-primary via-accent to-primary transition-all duration-300"
45+
style={{ width: `${progress}%` }}
46+
/>
47+
</div>
48+
<p className="text-sm text-muted-foreground mt-2">{progress}%</p>
49+
</div>
50+
</div>
51+
</div>
52+
);
53+
};
54+
55+
export default LoadingScreen;

src/components/ui/ScrollToTop.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useEffect, useState } from "react";
2+
import { ArrowUp } from "lucide-react";
3+
import { Button } from "./button";
4+
5+
const ScrollToTop = () => {
6+
const [isVisible, setIsVisible] = useState(false);
7+
8+
useEffect(() => {
9+
const toggleVisibility = () => {
10+
if (window.scrollY > 300) {
11+
setIsVisible(true);
12+
} else {
13+
setIsVisible(false);
14+
}
15+
};
16+
17+
window.addEventListener("scroll", toggleVisibility);
18+
return () => window.removeEventListener("scroll", toggleVisibility);
19+
}, []);
20+
21+
const scrollToTop = () => {
22+
window.scrollTo({
23+
top: 0,
24+
behavior: "smooth",
25+
});
26+
};
27+
28+
return (
29+
<>
30+
{isVisible && (
31+
<Button
32+
onClick={scrollToTop}
33+
size="icon"
34+
className="fixed bottom-8 right-8 z-50 bg-gradient-quantum hover:shadow-glow-primary transition-all duration-300 animate-bounce-slow"
35+
aria-label="Scroll to top"
36+
>
37+
<ArrowUp className="w-5 h-5" />
38+
</Button>
39+
)}
40+
</>
41+
);
42+
};
43+
44+
export default ScrollToTop;

src/index.css

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ All colors MUST be HSL.
5656
--glow-primary: 0 0 20px hsl(230 85% 60% / 0.5);
5757
--glow-accent: 0 0 20px hsl(190 95% 60% / 0.5);
5858
--glow-success: 0 0 20px hsl(140 70% 55% / 0.5);
59+
5960
/* Polarization colors (primary hex + translucent bg variants) */
6061
--polar-vertical: #00f0ff;
6162
--polar-vertical-bg: rgba(0,240,255,0.08);
@@ -68,6 +69,46 @@ All colors MUST be HSL.
6869
}
6970
}
7071

72+
/* Custom animations */
73+
@keyframes bounce-slow {
74+
0%, 100% {
75+
transform: translateY(0);
76+
}
77+
50% {
78+
transform: translateY(-10px);
79+
}
80+
}
81+
82+
@keyframes float {
83+
0%, 100% {
84+
transform: translateY(0px) rotate(0deg);
85+
}
86+
50% {
87+
transform: translateY(-20px) rotate(5deg);
88+
}
89+
}
90+
91+
@keyframes pulse-glow {
92+
0%, 100% {
93+
box-shadow: 0 0 20px rgba(96, 165, 250, 0.4);
94+
}
95+
50% {
96+
box-shadow: 0 0 40px rgba(96, 165, 250, 0.8);
97+
}
98+
}
99+
100+
.animate-bounce-slow {
101+
animation: bounce-slow 3s ease-in-out infinite;
102+
}
103+
104+
.animate-float {
105+
animation: float 6s ease-in-out infinite;
106+
}
107+
108+
.animate-pulse-glow {
109+
animation: pulse-glow 2s ease-in-out infinite;
110+
}
111+
71112
@layer base {
72113
* {
73114
@apply border-border;

0 commit comments

Comments
 (0)