diff --git a/client/src/pages/os.tsx b/client/src/pages/os.tsx index 11b6509..cc1ccf8 100644 --- a/client/src/pages/os.tsx +++ b/client/src/pages/os.tsx @@ -28,8 +28,24 @@ interface WindowState { minimized: boolean; maximized: boolean; zIndex: number; + accentColor?: string; } +interface Toast { + id: string; + message: string; + type: 'info' | 'success' | 'warning' | 'error'; +} + +const ACCENT_COLORS = [ + { id: 'cyan', name: 'Cyan', color: '#06b6d4', ring: 'ring-cyan-400/50', shadow: 'shadow-cyan-500/20', bg: 'bg-cyan-500' }, + { id: 'purple', name: 'Purple', color: '#a855f7', ring: 'ring-purple-400/50', shadow: 'shadow-purple-500/20', bg: 'bg-purple-500' }, + { id: 'green', name: 'Green', color: '#22c55e', ring: 'ring-green-400/50', shadow: 'shadow-green-500/20', bg: 'bg-green-500' }, + { id: 'orange', name: 'Orange', color: '#f97316', ring: 'ring-orange-400/50', shadow: 'shadow-orange-500/20', bg: 'bg-orange-500' }, + { id: 'pink', name: 'Pink', color: '#ec4899', ring: 'ring-pink-400/50', shadow: 'shadow-pink-500/20', bg: 'bg-pink-500' }, + { id: 'red', name: 'Red', color: '#ef4444', ring: 'ring-red-400/50', shadow: 'shadow-red-500/20', bg: 'bg-red-500' }, +]; + interface DesktopApp { id: string; title: string; @@ -75,11 +91,25 @@ export default function AeThexOS() { const [showNotifications, setShowNotifications] = useState(false); const [secretsUnlocked, setSecretsUnlocked] = useState(false); const [konamiProgress, setKonamiProgress] = useState([]); + const [toasts, setToasts] = useState([]); + const [showSpotlight, setShowSpotlight] = useState(false); + const [spotlightQuery, setSpotlightQuery] = useState(''); + const [currentDesktop, setCurrentDesktop] = useState(0); + const [showOnboarding, setShowOnboarding] = useState(false); + const [onboardingStep, setOnboardingStep] = useState(0); + const [desktopIcons, setDesktopIcons] = useState([]); const desktopRef = useRef(null); const idleTimer = useRef(null); + const spotlightRef = useRef(null); const { user, isAuthenticated, logout } = useAuth(); const [, setLocation] = useLocation(); + const addToast = useCallback((message: string, type: Toast['type'] = 'info') => { + const id = Date.now().toString(); + setToasts(prev => [...prev, { id, message, type }]); + setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 4000); + }, []); + useEffect(() => { const bootSequence = async () => { const steps = [ @@ -167,6 +197,40 @@ export default function AeThexOS() { }; }, []); + useEffect(() => { + if (showSpotlight && spotlightRef.current) { + spotlightRef.current.focus(); + } + }, [showSpotlight]); + + useEffect(() => { + const saved = localStorage.getItem('aethex-window-positions'); + if (saved) { + try { + const positions = JSON.parse(saved); + setWindows(prev => prev.map(w => { + const savedPos = positions[w.id]; + return savedPos ? { ...w, ...savedPos } : w; + })); + } catch {} + } + const hasVisited = localStorage.getItem('aethex-visited'); + if (!hasVisited) { + setShowOnboarding(true); + localStorage.setItem('aethex-visited', 'true'); + } + }, []); + + useEffect(() => { + if (windows.length > 0) { + const positions: Record = {}; + windows.forEach(w => { + positions[w.id] = { x: w.x, y: w.y, width: w.width, height: w.height }; + }); + localStorage.setItem('aethex-window-positions', JSON.stringify(positions)); + } + }, [windows]); + const apps: DesktopApp[] = [ { id: "terminal", title: "Terminal", icon: , component: "terminal", defaultWidth: 750, defaultHeight: 500 }, { id: "passport", title: "Passport", icon: , component: "passport", defaultWidth: 500, defaultHeight: 600 }, @@ -300,6 +364,36 @@ export default function AeThexOS() { setLocation("/"); }; + useEffect(() => { + const handleGlobalKeys = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === ' ') { + e.preventDefault(); + setShowSpotlight(prev => !prev); + setSpotlightQuery(''); + } + if (e.key === 'Escape') { + setShowSpotlight(false); + setShowStartMenu(false); + setContextMenu(null); + } + if ((e.metaKey || e.ctrlKey) && !e.shiftKey) { + const shortcuts: Record = { 't': 'terminal', 'n': 'notes', 'e': 'codeeditor', 'p': 'passport', 'm': 'metrics' }; + if (shortcuts[e.key]) { + e.preventDefault(); + const app = apps.find(a => a.id === shortcuts[e.key]); + if (app) openApp(app); + } + } + if ((e.metaKey || e.ctrlKey) && e.key >= '1' && e.key <= '4') { + e.preventDefault(); + setCurrentDesktop(parseInt(e.key) - 1); + addToast(`Switched to Desktop ${e.key}`, 'info'); + } + }; + window.addEventListener('keydown', handleGlobalKeys); + return () => window.removeEventListener('keydown', handleGlobalKeys); + }, [apps, openApp, addToast]); + const renderAppContent = (component: string) => { switch (component) { case 'terminal': return ; @@ -466,7 +560,36 @@ export default function AeThexOS() { onAppClick={openApp} onLogout={handleLogout} onNavigate={setLocation} + currentDesktop={currentDesktop} + onDesktopChange={setCurrentDesktop} /> + + + + + {showSpotlight && ( + { openApp(app); setShowSpotlight(false); }} + onClose={() => setShowSpotlight(false)} + inputRef={spotlightRef} + /> + )} + + + + + + {showOnboarding && ( + setOnboardingStep(s => s + 1)} + onClose={() => setShowOnboarding(false)} + /> + )} + ); } @@ -723,9 +846,187 @@ interface TaskbarProps { onAppClick: (app: DesktopApp) => void; onLogout: () => void; onNavigate: (path: string) => void; + currentDesktop: number; + onDesktopChange: (d: number) => void; } -function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, user, isAuthenticated, notifications, showNotifications, onToggleStartMenu, onToggleNotifications, onWindowClick, onAppClick, onLogout, onNavigate }: TaskbarProps) { +function ParticleField() { + const particles = Array.from({ length: 30 }, (_, i) => ({ + id: i, + x: Math.random() * 100, + y: Math.random() * 100, + size: Math.random() * 2 + 1, + duration: Math.random() * 20 + 10, + delay: Math.random() * 5, + })); + + return ( +
+ {particles.map(p => ( + + ))} +
+ ); +} + +function SpotlightSearch({ query, setQuery, apps, onSelectApp, onClose, inputRef }: { + query: string; + setQuery: (q: string) => void; + apps: DesktopApp[]; + onSelectApp: (app: DesktopApp) => void; + onClose: () => void; + inputRef: React.RefObject; +}) { + const filtered = apps.filter(a => a.title.toLowerCase().includes(query.toLowerCase())); + + return ( + +
e.stopPropagation()} + > +
+ + setQuery(e.target.value)} + placeholder="Search apps... (Ctrl+Space)" + className="flex-1 bg-transparent text-white text-lg outline-none placeholder:text-white/30" + autoFocus + /> + ESC +
+
+ {filtered.length === 0 ? ( +
No apps found
+ ) : ( + filtered.map(app => ( + + )) + )} +
+
+ Ctrl+T Terminal • Ctrl+N Notes • Ctrl+E Code • Ctrl+P Passport +
+
+
+ ); +} + +function ToastContainer({ toasts }: { toasts: Toast[] }) { + const colors = { + info: 'border-cyan-500/50 bg-cyan-500/10', + success: 'border-green-500/50 bg-green-500/10', + warning: 'border-yellow-500/50 bg-yellow-500/10', + error: 'border-red-500/50 bg-red-500/10', + }; + + return ( +
+ + {toasts.map(toast => ( + + {toast.message} + + ))} + +
+ ); +} + +function OnboardingTour({ step, onNext, onClose }: { step: number; onNext: () => void; onClose: () => void }) { + const steps = [ + { title: 'Welcome to AeThex OS', content: 'Your operating system for the Metaverse. Double-click icons to open apps.' }, + { title: 'Desktop Navigation', content: 'Use Ctrl+Space to open Spotlight search. Press Ctrl+1-4 to switch desktops.' }, + { title: 'Keyboard Shortcuts', content: 'Ctrl+T for Terminal, Ctrl+N for Notes, Ctrl+E for Code Editor.' }, + { title: 'Discover Secrets', content: 'Try the Konami code or explore Terminal commands. There are hidden surprises!' }, + ]; + + if (step >= steps.length) { + onClose(); + return null; + } + + const current = steps[step]; + + return ( + + +
+
+ +
+

{current.title}

+
+

{current.content}

+
+
+ {steps.map((_, i) => ( +
+ ))} +
+
+ + +
+
+ + + ); +} + +function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, user, isAuthenticated, notifications, showNotifications, onToggleStartMenu, onToggleNotifications, onWindowClick, onAppClick, onLogout, onNavigate, currentDesktop, onDesktopChange }: TaskbarProps) { return ( <> @@ -790,18 +1091,41 @@ function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, user, isA
{windows.map(window => ( - ))}