AeThex-OS/client/src/pages/os.tsx

1573 lines
68 KiB
TypeScript

import { useState, useRef, useCallback, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import { motion, AnimatePresence, useMotionValue, useTransform, PanInfo } from "framer-motion";
import { useLocation } from "wouter";
import { useAuth } from "@/lib/auth";
import { useWebSocket } from "@/hooks/use-websocket";
import { getIcon } from "@/lib/iconMap";
import { usePlatformLayout, PlatformSwitch } from "@/hooks/use-platform-layout";
import { useHaptics } from "@/hooks/use-haptics";
import { useMobileNative } from "@/hooks/use-mobile-native";
import { useNativeFeatures } from "@/hooks/use-native-features";
import { useBiometricAuth } from "@/hooks/use-biometric-auth";
import { StatusBar, Style } from '@capacitor/status-bar';
import { MobileQuickActions } from "@/components/MobileQuickActions";
import { Minesweeper } from "@/components/games/Minesweeper";
import { CookieClicker } from "@/components/games/CookieClicker";
import AethexStudio from "@/components/AethexStudio";
import AethexAppStore from "@/components/AethexAppStore";
import { BootSequence } from "@/os/boot/BootSequence";
import { Taskbar } from "@/os/core/Taskbar";
import { TerminalApp } from "@/os/apps/TerminalApp";
import { PassportApp } from "@/os/apps/PassportApp";
import { SettingsApp, WALLPAPERS, ACCENT_COLORS, ThemeSettings, DesktopLayout } from "@/os/apps/SettingsApp";
import { FilesApp } from "@/os/apps/FilesApp";
import { AchievementsApp } from "@/os/apps/AchievementsApp";
import { OpportunitiesApp } from "@/os/apps/OpportunitiesApp";
import { EventsApp } from "@/os/apps/EventsApp";
import { ChatApp } from "@/os/apps/ChatApp";
import { ManifestoApp } from "@/os/apps/ManifestoApp";
import { MusicApp } from "@/os/apps/MusicApp";
import { PitchApp } from "@/os/apps/PitchApp";
import { NetworkMapApp } from "@/os/apps/NetworkMapApp";
import { MetricsDashboardApp } from "@/os/apps/MetricsDashboardApp";
import { CodeEditorApp } from "@/os/apps/CodeEditorApp";
import { NewsFeedApp } from "@/os/apps/NewsFeedApp";
import { ArcadeApp } from "@/os/apps/ArcadeApp";
import { ProfilesApp } from "@/os/apps/ProfilesApp";
import { NetworkNeighborhoodApp } from "@/os/apps/NetworkNeighborhoodApp";
import { FoundryApp } from "@/os/apps/FoundryApp";
import { DevToolsApp } from "@/os/apps/DevToolsApp";
import { IntelApp } from "@/os/apps/IntelApp";
import { DrivesApp } from "@/os/apps/DrivesApp";
import { MissionApp } from "@/os/apps/MissionApp";
import { LeaderboardApp } from "@/os/apps/LeaderboardApp";
import { CalculatorApp } from "@/os/apps/CalculatorApp";
import { NotesApp } from "@/os/apps/NotesApp";
import { SystemMonitorApp } from "@/os/apps/SystemMonitorApp";
import { WebcamApp } from "@/os/apps/WebcamApp";
import {
ProjectsAppWrapper,
MessagingAppWrapper,
MarketplaceAppWrapper,
FileManagerAppWrapper,
CodeGalleryAppWrapper,
NotificationsAppWrapper,
AnalyticsAppWrapper
} from "@/os/apps/IframeWrappers";
import { DesktopWidgets, DraggableWidget, WidgetPosition, WidgetPositions, getDefaultWidgetPositions } from "@/os/components/Widgets";
import { DesktopIcon, ContextMenuComponent, MenuItem } from "@/os/components/Desktop";
import { Window } from "@/os/components/Window";
import { Skeleton, LoadingSkeleton, ParticleField } from "@/os/components/UI";
import { SpotlightSearch, ToastContainer, OnboardingTour } from "@/os/components/Overlays";
import { OSProviders, useWindows, useDesktop, useSettings, useNotifications } from "@/os/contexts";
import {
Terminal, FileText, IdCard, Music, Settings, Globe,
X, Minus, Square, Maximize2, Volume2, Wifi, Battery,
ChevronUp, FolderOpen, Award, MessageCircle, Send,
ExternalLink, User, LogOut, BarChart3, Loader2, Layers,
Presentation, Bell, Image, Monitor, Play, Pause, ChevronRight,
Network, Activity, Code2, Radio, Newspaper, Gamepad2,
Users, Trophy, Calculator, StickyNote, Cpu, Camera,
Eye, Shield, Zap, Skull, Lock, Unlock, Server, Database,
TrendingUp, ArrowUp, ArrowDown, Hash, Key, HardDrive, FolderSearch,
AlertTriangle, Briefcase, CalendarDays, FolderGit2, MessageSquare,
ShoppingCart, Folder, Code, Home, Flag, Cookie, ChevronLeft,
MoreVertical, Search, Mic, ArrowLeft, RefreshCw, Star, Clock, MapPin,
Store, Rocket
} from "lucide-react";
interface WindowState {
id: string;
title: string;
icon: React.ReactNode;
component: string;
x: number;
y: number;
width: number;
height: number;
minimized: boolean;
maximized: boolean;
zIndex: number;
accentColor?: string;
desktopId: number;
iframeUrl?: string;
}
interface Toast {
id: string;
message: string;
type: 'info' | 'success' | 'warning' | 'error';
}
type ClearanceMode = 'foundation' | 'corp';
interface ClearanceTheme {
id: ClearanceMode;
name: string;
title: string;
subtitle: string;
primary: string;
secondary: string;
accent: string;
accentSecondary: string;
wallpaper: string;
borderStyle: string;
fontStyle: string;
}
const DAILY_TIPS = [
{ title: "Quick Launch", tip: "Press Ctrl+Space to open Spotlight search and quickly find apps." },
{ title: "Virtual Desktops", tip: "Use the numbered buttons (1-4) in the taskbar to switch between virtual desktops." },
{ title: "Window Management", tip: "Double-click a window title bar to maximize/restore it." },
{ title: "Keyboard Shortcuts", tip: "Ctrl+T opens Terminal, Ctrl+S opens Settings, Ctrl+F opens Files." },
{ title: "Theme Switching", tip: "Click the Start menu and use 'Switch Clearance' to change between Foundation and Corp modes." },
{ title: "Sound Settings", tip: "Toggle system sounds in Settings to enable audio feedback for actions." },
{ title: "Dock Apps", tip: "Your most-used apps are pinned to the quick-launch dock for easy access." },
{ title: "Right-Click Menu", tip: "Right-click on the desktop to access quick options like refresh and settings." },
{ title: "Calculator", tip: "Need quick math? Open Calculator from the app menu or dock." },
{ title: "Notifications", tip: "Click the bell icon in the taskbar to view system notifications." },
];
const PINNED_APPS = ['terminal', 'networkneighborhood', 'calculator', 'settings'];
const CLEARANCE_THEMES: Record<ClearanceMode, ClearanceTheme> = {
foundation: {
id: 'foundation',
name: 'The Foundation',
title: 'FOUNDATION',
subtitle: 'The Architect\'s Domain',
primary: '#DC2626',
secondary: '#D4AF37',
accent: '#DC2626',
accentSecondary: '#D4AF37',
wallpaper: 'radial-gradient(ellipse at 30% 20%, #4a1515 0%, #1a0505 40%, #0a0202 100%)',
borderStyle: 'border-yellow-600/40',
fontStyle: 'font-mono',
},
corp: {
id: 'corp',
name: 'The Corp',
title: 'CORPORATION',
subtitle: 'Executive Operations',
primary: '#0F172A',
secondary: '#C0C0C0',
accent: '#3B82F6',
accentSecondary: '#C0C0C0',
wallpaper: 'radial-gradient(ellipse at 70% 80%, #1e3a5f 0%, #0f172a 40%, #050a14 100%)',
borderStyle: 'border-slate-400/30',
fontStyle: 'font-sans',
},
};
interface DesktopApp {
id: string;
title: string;
icon: React.ReactNode;
component: string;
defaultWidth: number;
defaultHeight: number;
}
interface ContextMenuState {
x: number;
y: number;
type: 'desktop' | 'icon';
appId?: string;
}
const WALLPAPERS = [
];
const KONAMI_CODE = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
function AeThexOSInner() {
const layout = usePlatformLayout();
const { impact, notification } = useHaptics();
const { keyboardVisible, deviceInfo } = useMobileNative('dark');
const native = useNativeFeatures();
const biometric = useBiometricAuth();
// Context hooks
const windowContext = useWindows();
const {
currentDesktop,
desktopIcons,
showStartMenu,
contextMenu,
mousePosition,
setCurrentDesktop,
setDesktopIcons,
setShowStartMenu,
toggleStartMenu,
setContextMenu,
setMousePosition,
getDesktopWindowCount
} = useDesktop();
const {
theme,
wallpaper,
soundEnabled,
clearanceMode,
clearanceTheme,
isSwitchingClearance,
savedLayouts,
setTheme,
setWallpaper,
setSoundEnabled,
setClearanceMode,
setIsSwitchingClearance,
setSavedLayouts
} = useSettings();
const {
toasts,
notifications,
showNotifications,
addToast,
addNotification,
clearNotification,
clearAllNotifications,
setShowNotifications,
toggleNotifications
} = useNotifications();
// Local state (not moved to context)
const [isBooting, setIsBooting] = useState(!layout.isMobile);
const [bootProgress, setBootProgress] = useState(0);
const [bootStep, setBootStep] = useState('');
const [time, setTime] = useState(new Date());
const [showScreensaver, setShowScreensaver] = useState(false);
const [secretsUnlocked, setSecretsUnlocked] = useState(false);
const [konamiProgress, setKonamiProgress] = useState<string[]>([]);
const [showSpotlight, setShowSpotlight] = useState(false);
const [spotlightQuery, setSpotlightQuery] = useState('');
const [showOnboarding, setShowOnboarding] = useState(false);
const [onboardingStep, setOnboardingStep] = useState(0);
const [showDailyTip, setShowDailyTip] = useState(false);
const [dailyTip, setDailyTip] = useState(DAILY_TIPS[0]);
const audioContextRef = useRef<AudioContext | null>(null);
const desktopRef = useRef<HTMLDivElement>(null);
const idleTimer = useRef<NodeJS.Timeout | null>(null);
const spotlightRef = useRef<HTMLInputElement>(null);
// Motion values for mobile gestures (must be declared before any conditional returns)
const dragX = useMotionValue(0);
const dragOpacity = useTransform(dragX, [-200, 0, 200], [0.5, 1, 0.5]);
const { user, isAuthenticated, logout } = useAuth();
const [, setLocation] = useLocation();
const [activeTrayPanel, setActiveTrayPanel] = useState<'wifi' | 'volume' | 'battery' | 'notifications' | 'upgrade' | null>(null);
const [volume, setVolume] = useState(75);
const [isMuted, setIsMuted] = useState(false);
// WebSocket connection for real-time updates
const {
connected: wsConnected,
metrics: wsMetrics,
alerts: wsAlerts,
achievements: wsAchievements,
notifications: wsNotifications
} = useWebSocket();
const [batteryInfo, setBatteryInfo] = useState<{ level: number; charging: boolean } | null>(null);
useEffect(() => {
let battery: any = null;
let levelChangeHandler: (() => void) | null = null;
let chargingChangeHandler: (() => void) | null = null;
if ('getBattery' in navigator) {
(navigator as any).getBattery().then((bat: any) => {
battery = bat;
setBatteryInfo({ level: Math.round(battery.level * 100), charging: battery.charging });
levelChangeHandler = () => {
setBatteryInfo(prev => prev ? { ...prev, level: Math.round(battery.level * 100) } : null);
};
chargingChangeHandler = () => {
setBatteryInfo(prev => prev ? { ...prev, charging: battery.charging } : null);
};
battery.addEventListener('levelchange', levelChangeHandler);
battery.addEventListener('chargingchange', chargingChangeHandler);
});
}
// Cleanup: remove battery event listeners to prevent memory leak
return () => {
if (battery) {
if (levelChangeHandler) battery.removeEventListener('levelchange', levelChangeHandler);
if (chargingChangeHandler) battery.removeEventListener('chargingchange', chargingChangeHandler);
}
};
}, []);
const { data: weatherData, isFetching: weatherFetching } = useQuery({
queryKey: ['weather'],
queryFn: async () => {
const res = await fetch('https://api.open-meteo.com/v1/forecast?latitude=40.7128&longitude=-74.0060&current_weather=true&temperature_unit=fahrenheit');
return res.json();
},
refetchInterval: 600000,
staleTime: 300000,
});
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
const [isDesktopLocked, setIsDesktopLocked] = useState(true);
const [detectedIdentity, setDetectedIdentity] = useState<{ username?: string; passportId?: string } | null>(null);
const [threatLevel, setThreatLevel] = useState<'scanning' | 'low' | 'medium' | 'high'>('scanning');
const [bootLogs, setBootLogs] = useState<string[]>([]);
useEffect(() => {
const bootSequence = async () => {
const addLog = (text: string) => setBootLogs(prev => [...prev.slice(-8), text]);
// Phase 1: Hardware initialization
const phase1 = [
{ text: 'POST: Power-On Self Test...', progress: 3 },
{ text: 'CPU: AMD Ryzen 9 7950X3D @ 4.2GHz... OK', progress: 5 },
{ text: 'RAM: 64GB DDR5-6000 ECC... OK', progress: 8 },
{ text: 'GPU: Quantum Accelerator v2.1... OK', progress: 10 },
{ text: 'NVME: AeThex Vault 2TB... OK', progress: 12 },
];
for (const step of phase1) {
setBootStep(step.text);
addLog(step.text);
setBootProgress(step.progress);
await new Promise(r => setTimeout(r, 150));
}
// Phase 2: Kernel & filesystem
const phase2 = [
{ text: 'Loading AeThex Kernel v4.2.1...', progress: 18 },
{ text: 'Initializing virtual memory manager...', progress: 22 },
{ text: 'Mounting encrypted file systems...', progress: 26 },
{ text: 'Loading device drivers...', progress: 30 },
];
for (const step of phase2) {
setBootStep(step.text);
addLog(step.text);
setBootProgress(step.progress);
await new Promise(r => setTimeout(r, 200));
}
// Phase 3: Passport Identity Detection
setBootStep('INITIATING AETHEX PASSPORT SUBSYSTEM...');
addLog('▸ PASSPORT: Initializing identity subsystem...');
setBootProgress(35);
await new Promise(r => setTimeout(r, 300));
// Check for existing session/identity
let foundIdentity = false;
try {
const sessionRes = await fetch('/api/auth/session', { credentials: 'include' });
const sessionData = await sessionRes.json();
if (sessionData?.authenticated && sessionData?.user) {
foundIdentity = true;
setDetectedIdentity({
username: sessionData.user.username,
passportId: sessionData.user.id?.slice(0, 8).toUpperCase()
});
addLog(`▸ PASSPORT: Identity token detected`);
setBootStep('PASSPORT: IDENTITY TOKEN DETECTED');
setBootProgress(40);
await new Promise(r => setTimeout(r, 300));
addLog(`▸ PASSPORT: Verifying credentials for ${sessionData.user.username}...`);
setBootStep(`Verifying credentials for ${sessionData.user.username}...`);
setBootProgress(45);
await new Promise(r => setTimeout(r, 400));
addLog(`▸ PASSPORT: Welcome back, ARCHITECT ${sessionData.user.username.toUpperCase()}`);
setBootStep(`WELCOME BACK, ARCHITECT ${sessionData.user.username.toUpperCase()}`);
setBootProgress(50);
await new Promise(r => setTimeout(r, 500));
}
} catch (err) {
// Session fetch failed, continue with guest mode
if (import.meta.env.DEV) console.debug('[Boot] Session check failed:', err);
}
if (!foundIdentity) {
addLog('▸ PASSPORT: No active identity token found');
setBootStep('PASSPORT: NO ACTIVE IDENTITY TOKEN');
setBootProgress(42);
await new Promise(r => setTimeout(r, 300));
addLog('▸ PASSPORT: Guest access mode available');
setBootStep('Guest access mode available');
setBootProgress(48);
await new Promise(r => setTimeout(r, 300));
}
// Phase 4: Aegis Security Layer
addLog('▸ AEGIS: Initializing security layer...');
setBootStep('AEGIS: INITIALIZING SECURITY LAYER...');
setBootProgress(55);
await new Promise(r => setTimeout(r, 300));
addLog('▸ AEGIS: Loading threat detection modules...');
setBootStep('Loading threat detection modules...');
setBootProgress(60);
await new Promise(r => setTimeout(r, 250));
addLog('▸ AEGIS: Scanning network perimeter...');
setBootStep('AEGIS: SCANNING NETWORK PERIMETER...');
setBootProgress(65);
setThreatLevel('scanning');
await new Promise(r => setTimeout(r, 600));
// Simulate threat assessment result
const threatResult = Math.random();
if (threatResult < 0.7) {
setThreatLevel('low');
addLog('▸ AEGIS: Threat level LOW - All systems nominal');
setBootStep('THREAT LEVEL: LOW - ALL SYSTEMS NOMINAL');
} else if (threatResult < 0.95) {
setThreatLevel('medium');
addLog('▸ AEGIS: Threat level MEDIUM - Enhanced monitoring active');
setBootStep('THREAT LEVEL: MEDIUM - MONITORING ACTIVE');
} else {
setThreatLevel('high');
addLog('▸ AEGIS: Threat level ELEVATED - Defensive protocols engaged');
setBootStep('THREAT LEVEL: ELEVATED - PROTOCOLS ENGAGED');
}
// Schedule upgrade alert in system tray once per session
try {
const shown = localStorage.getItem('aethex-upgrade-alert-shown');
if (!shown) {
setTimeout(() => {
try {
if (!localStorage.getItem('aethex-upgrade-alert-shown')) {
setActiveTrayPanel('upgrade');
addToast('⚠️ Architect Access Available — Use tray to upgrade', 'info');
localStorage.setItem('aethex-upgrade-alert-shown', 'true');
}
} catch (err) {
if (import.meta.env.DEV) console.debug('[Boot] localStorage access failed:', err);
}
}, 30000);
}
} catch (err) {
if (import.meta.env.DEV) console.debug('[Boot] Upgrade check failed:', err);
}
setBootProgress(75);
await new Promise(r => setTimeout(r, 400));
// Phase 5: Network & Final
addLog('▸ NEXUS: Connecting to AeThex network...');
setBootStep('Connecting to Nexus network...');
setBootProgress(82);
await new Promise(r => setTimeout(r, 300));
addLog('▸ NEXUS: Syncing with distributed nodes...');
setBootStep('Syncing with distributed nodes...');
setBootProgress(88);
await new Promise(r => setTimeout(r, 250));
addLog('▸ NEXUS: Connection established - 42 peers online');
setBootStep('NEXUS: 42 PEERS ONLINE');
setBootProgress(94);
await new Promise(r => setTimeout(r, 200));
addLog('▸ SYSTEM: AeThex OS ready');
setBootStep('AETHEX OS READY');
setBootProgress(100);
await new Promise(r => setTimeout(r, 500));
setShowLoginPrompt(true);
};
bootSequence();
}, []);
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer);
}, []);
useEffect(() => {
const fetchNotifications = async () => {
try {
const res = await fetch('/api/os/notifications');
const data = await res.json();
if (Array.isArray(data)) {
setNotifications(data.map((n: any) => n.message));
}
} catch (err) {
// Notifications fetch failed, not critical
if (import.meta.env.DEV) console.debug('[OS] Notifications fetch failed:', err);
}
};
fetchNotifications();
const interval = setInterval(fetchNotifications, 60000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const resetIdle = () => {
if (showScreensaver) setShowScreensaver(false);
if (idleTimer.current) clearTimeout(idleTimer.current);
idleTimer.current = setTimeout(() => setShowScreensaver(true), 5 * 60 * 1000);
};
window.addEventListener('mousemove', resetIdle);
window.addEventListener('keydown', resetIdle);
resetIdle();
return () => {
window.removeEventListener('mousemove', resetIdle);
window.removeEventListener('keydown', resetIdle);
if (idleTimer.current) clearTimeout(idleTimer.current);
};
}, [showScreensaver]);
useEffect(() => {
const handleKonami = (e: KeyboardEvent) => {
setKonamiProgress(prev => {
const newProgress = [...prev, e.key].slice(-10);
if (newProgress.length === 10 && newProgress.every((k, i) => k === KONAMI_CODE[i])) {
setSecretsUnlocked(true);
setNotifications(prev => ['🎮 SECRETS UNLOCKED! Check Settings for new wallpapers.', ...prev]);
return [];
}
return newProgress;
});
};
const handleTerminalUnlock = () => {
setSecretsUnlocked(true);
setNotifications(prev => ['🔓 Terminal unlock activated! Check Settings.', ...prev]);
};
window.addEventListener('keydown', handleKonami);
window.addEventListener('aethex-unlock-secrets', handleTerminalUnlock);
return () => {
window.removeEventListener('keydown', handleKonami);
window.removeEventListener('aethex-unlock-secrets', handleTerminalUnlock);
};
}, []);
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 (err) {
// Corrupted localStorage data, ignore and use defaults
if (import.meta.env.DEV) console.debug('[OS] Failed to restore window positions:', err);
}
}
const hasVisited = localStorage.getItem('aethex-visited');
if (!hasVisited) {
setShowOnboarding(true);
localStorage.setItem('aethex-visited', 'true');
}
}, []);
useEffect(() => {
if (windows.length > 0) {
const positions: Record<string, { x: number; y: number; width: number; height: number }> = {};
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 foundationApps: DesktopApp[] = [
{ id: "networkneighborhood", title: "Network Neighborhood", icon: <Network className="w-8 h-8" />, component: "networkneighborhood", defaultWidth: 500, defaultHeight: 450 },
{ id: "mission", title: "README.TXT", icon: <FileText className="w-8 h-8" />, component: "mission", defaultWidth: 500, defaultHeight: 500 },
{ id: "passport", title: "Passport", icon: <Key className="w-8 h-8" />, component: "passport", defaultWidth: 650, defaultHeight: 500 },
{ id: "achievements", title: "Achievements", icon: <Trophy className="w-8 h-8" />, component: "achievements", defaultWidth: 800, defaultHeight: 600 },
{ id: "projects", title: "Projects", icon: <FolderGit2 className="w-8 h-8" />, component: "projects", defaultWidth: 900, defaultHeight: 650 },
{ id: "opportunities", title: "Opportunities", icon: <Briefcase className="w-8 h-8" />, component: "opportunities", defaultWidth: 850, defaultHeight: 650 },
{ id: "events", title: "Events", icon: <CalendarDays className="w-8 h-8" />, component: "events", defaultWidth: 900, defaultHeight: 650 },
{ id: "messaging", title: "Messages", icon: <MessageSquare className="w-8 h-8" />, component: "messaging", defaultWidth: 850, defaultHeight: 600 },
{ id: "marketplace", title: "Marketplace", icon: <ShoppingCart className="w-8 h-8" />, component: "marketplace", defaultWidth: 900, defaultHeight: 650 },
{ id: "foundry", title: "FOUNDRY.EXE", icon: <Award className="w-8 h-8" />, component: "foundry", defaultWidth: 450, defaultHeight: 500 },
{ id: "intel", title: "INTEL", icon: <FolderSearch className="w-8 h-8" />, component: "intel", defaultWidth: 550, defaultHeight: 450 },
{ id: "filemanager", title: "File Manager", icon: <Folder className="w-8 h-8" />, component: "filemanager", defaultWidth: 800, defaultHeight: 600 },
{ id: "codegallery", title: "Code Gallery", icon: <Code className="w-8 h-8" />, component: "codegallery", defaultWidth: 900, defaultHeight: 650 },
{ id: "drives", title: "My Computer", icon: <HardDrive className="w-8 h-8" />, component: "drives", defaultWidth: 450, defaultHeight: 400 },
{ id: "chat", title: "AeThex AI", icon: <MessageCircle className="w-8 h-8" />, component: "chat", defaultWidth: 400, defaultHeight: 500 },
{ id: "terminal", title: "Terminal", icon: <Terminal className="w-8 h-8" />, component: "terminal", defaultWidth: 750, defaultHeight: 500 },
{ id: "notifications", title: "Notifications", icon: <Bell className="w-8 h-8" />, component: "notifications", defaultWidth: 700, defaultHeight: 600 },
{ id: "analytics", title: "Analytics", icon: <BarChart3 className="w-8 h-8" />, component: "analytics", defaultWidth: 1000, defaultHeight: 700 },
{ id: "metrics", title: "System Status", icon: <Activity className="w-8 h-8" />, component: "metrics", defaultWidth: 750, defaultHeight: 550 },
{ id: "devtools", title: "Dev Tools", icon: <Code2 className="w-8 h-8" />, component: "devtools", defaultWidth: 450, defaultHeight: 400 },
{ id: "music", title: "Radio AeThex", icon: <Radio className="w-8 h-8" />, component: "music", defaultWidth: 400, defaultHeight: 350 },
{ id: "codeeditor", title: "The Lab", icon: <Code2 className="w-8 h-8" />, component: "codeeditor", defaultWidth: 700, defaultHeight: 500 },
{ id: "arcade", title: "Snake", icon: <Gamepad2 className="w-8 h-8" />, component: "arcade", defaultWidth: 420, defaultHeight: 520 },
{ id: "minesweeper", title: "Minesweeper", icon: <Flag className="w-8 h-8" />, component: "minesweeper", defaultWidth: 400, defaultHeight: 500 },
{ id: "cookieclicker", title: "Cookie Clicker", icon: <Cookie className="w-8 h-8" />, component: "cookieclicker", defaultWidth: 420, defaultHeight: 600 },
{ id: "aethexstudio", title: "AeThex Studio", icon: <Rocket className="w-8 h-8" />, component: "aethexstudio", defaultWidth: 1200, defaultHeight: 700 },
{ id: "aethexappstore", title: "App Store", icon: <Store className="w-8 h-8" />, component: "aethexappstore", defaultWidth: 1000, defaultHeight: 650 },
{ id: "calculator", title: "Calculator", icon: <Calculator className="w-8 h-8" />, component: "calculator", defaultWidth: 320, defaultHeight: 450 },
{ id: "settings", title: "Settings", icon: <Settings className="w-8 h-8" />, component: "settings", defaultWidth: 550, defaultHeight: 500 },
];
const corpApps: DesktopApp[] = [
{ id: "networkneighborhood", title: "Network Neighborhood", icon: <Network className="w-8 h-8" />, component: "networkneighborhood", defaultWidth: 500, defaultHeight: 450 },
{ id: "mission", title: "README.TXT", icon: <FileText className="w-8 h-8" />, component: "mission", defaultWidth: 500, defaultHeight: 500 },
{ id: "passport", title: "Passport", icon: <Key className="w-8 h-8" />, component: "passport", defaultWidth: 650, defaultHeight: 500 },
{ id: "achievements", title: "Achievements", icon: <Trophy className="w-8 h-8" />, component: "achievements", defaultWidth: 800, defaultHeight: 600 },
{ id: "projects", title: "Projects", icon: <FolderGit2 className="w-8 h-8" />, component: "projects", defaultWidth: 900, defaultHeight: 650 },
{ id: "opportunities", title: "Opportunities", icon: <Briefcase className="w-8 h-8" />, component: "opportunities", defaultWidth: 850, defaultHeight: 650 },
{ id: "events", title: "Events", icon: <CalendarDays className="w-8 h-8" />, component: "events", defaultWidth: 900, defaultHeight: 650 },
{ id: "messaging", title: "Messages", icon: <MessageSquare className="w-8 h-8" />, component: "messaging", defaultWidth: 850, defaultHeight: 600 },
{ id: "marketplace", title: "Marketplace", icon: <ShoppingCart className="w-8 h-8" />, component: "marketplace", defaultWidth: 900, defaultHeight: 650 },
{ id: "foundry", title: "FOUNDRY.EXE", icon: <Award className="w-8 h-8" />, component: "foundry", defaultWidth: 450, defaultHeight: 500 },
{ id: "intel", title: "INTEL", icon: <FolderSearch className="w-8 h-8" />, component: "intel", defaultWidth: 550, defaultHeight: 450 },
{ id: "filemanager", title: "File Manager", icon: <Folder className="w-8 h-8" />, component: "filemanager", defaultWidth: 800, defaultHeight: 600 },
{ id: "codegallery", title: "Code Gallery", icon: <Code className="w-8 h-8" />, component: "codegallery", defaultWidth: 900, defaultHeight: 650 },
{ id: "drives", title: "My Computer", icon: <HardDrive className="w-8 h-8" />, component: "drives", defaultWidth: 450, defaultHeight: 400 },
{ id: "devtools", title: "Dev Tools", icon: <Code2 className="w-8 h-8" />, component: "devtools", defaultWidth: 450, defaultHeight: 400 },
{ id: "notifications", title: "Notifications", icon: <Bell className="w-8 h-8" />, component: "notifications", defaultWidth: 700, defaultHeight: 600 },
{ id: "analytics", title: "Analytics", icon: <BarChart3 className="w-8 h-8" />, component: "analytics", defaultWidth: 1000, defaultHeight: 700 },
{ id: "metrics", title: "System Status", icon: <Activity className="w-8 h-8" />, component: "metrics", defaultWidth: 750, defaultHeight: 550 },
{ id: "network", title: "Global Ops", icon: <Globe className="w-8 h-8" />, component: "network", defaultWidth: 700, defaultHeight: 550 },
{ id: "files", title: "Asset Library", icon: <Database className="w-8 h-8" />, component: "files", defaultWidth: 700, defaultHeight: 500 },
{ id: "pitch", title: "Contracts", icon: <FileText className="w-8 h-8" />, component: "pitch", defaultWidth: 500, defaultHeight: 400 },
{ id: "sysmonitor", title: "Infrastructure", icon: <Server className="w-8 h-8" />, component: "sysmonitor", defaultWidth: 450, defaultHeight: 400 },
{ id: "leaderboard", title: "Performance", icon: <BarChart3 className="w-8 h-8" />, component: "leaderboard", defaultWidth: 500, defaultHeight: 550 },
{ id: "aethexstudio", title: "AeThex Studio", icon: <Rocket className="w-8 h-8" />, component: "aethexstudio", defaultWidth: 1200, defaultHeight: 700 },
{ id: "aethexappstore", title: "App Store", icon: <Store className="w-8 h-8" />, component: "aethexappstore", defaultWidth: 1000, defaultHeight: 650 },
{ id: "calculator", title: "Calculator", icon: <Calculator className="w-8 h-8" />, component: "calculator", defaultWidth: 320, defaultHeight: 450 },
{ id: "settings", title: "Settings", icon: <Settings className="w-8 h-8" />, component: "settings", defaultWidth: 550, defaultHeight: 500 },
];
const apps = clearanceMode === 'foundation' ? foundationApps : corpApps;
// Handle WebSocket notifications
useEffect(() => {
if (wsNotifications && wsNotifications.length > 0) {
wsNotifications.forEach((notification: any) => {
if (notification.message) {
addToast(notification.message, notification.type || 'info');
}
});
}
}, [wsNotifications]);
// Handle WebSocket alerts for admins
useEffect(() => {
if (user?.isAdmin && wsAlerts && wsAlerts.length > 0) {
const newAlertMessages = wsAlerts.map((alert: any) =>
`[AEGIS] ${alert.severity?.toUpperCase()}: ${alert.message}`
);
setNotifications(prev => [...new Set([...newAlertMessages, ...prev])].slice(0, 10));
}
}, [wsAlerts, user?.isAdmin]);
// Show WebSocket connection status
useEffect(() => {
if (wsConnected && !isBooting) {
addToast('Real-time connection established', 'success');
}
}, [wsConnected, isBooting]);
const playSound = useCallback((type: 'open' | 'close' | 'minimize' | 'click' | 'notification' | 'switch') => {
if (!soundEnabled) return;
try {
if (!audioContextRef.current) {
audioContextRef.current = new AudioContext();
}
const ctx = audioContextRef.current;
const oscillator = ctx.createOscillator();
const gainNode = ctx.createGain();
oscillator.connect(gainNode);
gainNode.connect(ctx.destination);
const sounds: Record<string, { freq: number; duration: number; type: OscillatorType }> = {
open: { freq: 523, duration: 0.1, type: 'sine' },
close: { freq: 392, duration: 0.1, type: 'sine' },
minimize: { freq: 330, duration: 0.08, type: 'sine' },
click: { freq: 800, duration: 0.03, type: 'square' },
notification: { freq: 880, duration: 0.15, type: 'sine' },
switch: { freq: 440, duration: 0.2, type: 'sawtooth' },
};
const sound = sounds[type] || sounds.click;
oscillator.type = sound.type;
oscillator.frequency.setValueAtTime(sound.freq, ctx.currentTime);
gainNode.gain.setValueAtTime(0.1, ctx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + sound.duration);
oscillator.start(ctx.currentTime);
oscillator.stop(ctx.currentTime + sound.duration);
} catch (e) {
console.log('Audio not available');
}
}, [soundEnabled]);
const switchClearance = useCallback(() => {
const newMode: ClearanceMode = clearanceMode === 'foundation' ? 'corp' : 'foundation';
setIsSwitchingClearance(true);
setShowStartMenu(false);
playSound('switch');
setTimeout(() => {
setClearanceMode(newMode);
setIsSwitchingClearance(false);
addToast(`Switched to ${CLEARANCE_THEMES[newMode].name}`, 'success');
}, 600);
}, [clearanceMode, addToast, playSound]);
const openApp = useCallback((app: DesktopApp) => {
const appToOpen = (isDesktopLocked && app.id !== 'passport')
? apps.find(a => a.id === 'passport') || app
: app;
playSound('open');
const existingWindow = windowContext.windows.find(w => w.id === appToOpen.id);
if (existingWindow) {
windowContext.focusWindow(appToOpen.id);
return;
}
const offsetX = (windowContext.windows.length % 5) * 40 + 100;
const offsetY = (windowContext.windows.length % 5) * 40 + 50;
const newWindow = {
id: appToOpen.id,
title: appToOpen.title,
icon: appToOpen.icon,
component: appToOpen.component,
x: offsetX,
y: offsetY,
width: appToOpen.defaultWidth,
height: appToOpen.defaultHeight,
minimized: false,
maximized: false,
desktopId: currentDesktop
};
windowContext.openWindow(newWindow);
setShowStartMenu(false);
}, [windowContext, playSound, currentDesktop, isDesktopLocked, apps]);
const closeWindow = useCallback((id: string) => {
playSound('close');
windowContext.closeWindow(id);
}, [windowContext, playSound]);
const minimizeWindow = useCallback((id: string) => {
playSound('minimize');
windowContext.minimizeWindow(id);
}, [windowContext, playSound]);
const toggleMaximize = useCallback((id: string) => {
windowContext.maximizeWindow(id);
}, [windowContext]);
const focusWindow = useCallback((id: string) => {
windowContext.focusWindow(id);
}, [windowContext]);
const handleDesktopClick = (e: React.MouseEvent) => {
setShowStartMenu(false);
setContextMenu(null);
};
const handleDesktopContextMenu = (e: React.MouseEvent) => {
e.preventDefault();
setContextMenu({ x: e.clientX, y: e.clientY, type: 'desktop' });
};
const handleIconContextMenu = (e: React.MouseEvent, appId: string) => {
e.preventDefault();
e.stopPropagation();
setContextMenu({ x: e.clientX, y: e.clientY, type: 'icon', appId });
};
const handleWindowSnap = useCallback((id: string, x: number, y: number, width: number, height: number) => {
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight - 48;
if (x <= 10 || x + width >= screenWidth - 10 || y <= 10) {
return windowContext.snapWindow(id, x, y);
}
return false;
}, [windowContext]);
const handleLogout = async () => {
await logout();
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<string, string> = { '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 <TerminalApp />;
case 'passport': return <PassportApp onLoginSuccess={unlockDesktop} isDesktopLocked={isDesktopLocked} />;
case 'files': return <FilesApp />;
case 'network': return <NetworkMapApp />;
case 'metrics': return <MetricsDashboardApp />;
case 'codeeditor': return <CodeEditorApp />;
case 'newsfeed': return <NewsFeedApp />;
case 'arcade': return <ArcadeApp />;
case 'minesweeper': return <Minesweeper />;
case 'cookieclicker': return <CookieClicker />;
case 'profiles': return <ProfilesApp />;
case 'leaderboard': return <LeaderboardApp />;
case 'calculator': return <CalculatorApp />;
case 'notes': return <NotesApp />;
case 'sysmonitor': return <SystemMonitorApp />;
case 'webcam': return <WebcamApp />;
case 'achievements': return <AchievementsApp />;
case 'projects': return <ProjectsAppWrapper />;
case 'opportunities': return <OpportunitiesApp />;
case 'events': return <EventsApp />;
case 'messaging': return <MessagingAppWrapper />;
case 'marketplace': return <MarketplaceAppWrapper />;
case 'chat': return <ChatApp />;
case 'music': return <MusicApp />;
case 'pitch': return <PitchApp onNavigate={() => setLocation('/pitch')} />;
case 'networkneighborhood': return <NetworkNeighborhoodApp openIframeWindow={openIframeWindow} />;
case 'foundry': return <FoundryApp openIframeWindow={openIframeWindow} />;
case 'devtools': return <DevToolsApp openIframeWindow={openIframeWindow} />;
case 'mission': return <MissionApp />;
case 'intel': return <IntelApp />;
case 'filemanager': return <FileManagerAppWrapper />;
case 'codegallery': return <CodeGalleryAppWrapper />;
case 'notifications': return <NotificationsAppWrapper />;
case 'analytics': return <AnalyticsAppWrapper />;
case 'drives': return <DrivesApp openIframeWindow={openIframeWindow} />;
case 'aethexstudio': return <AethexStudio />;
case 'aethexappstore': return <AethexAppStore />;
case 'iframe': return null;
case 'settings': return <SettingsApp
wallpaper={wallpaper}
setWallpaper={setWallpaper}
soundEnabled={soundEnabled}
setSoundEnabled={setSoundEnabled}
secretsUnlocked={secretsUnlocked}
theme={theme}
setTheme={setTheme}
savedLayouts={savedLayouts}
onSaveLayout={(name) => {
const layout: DesktopLayout = {
name,
windows: windows.map(w => ({ appId: w.component, x: w.x, y: w.y, width: w.width, height: w.height })),
desktop: currentDesktop,
};
setSavedLayouts(prev => [...prev.filter(l => l.name !== name), layout]);
addToast(`Layout "${name}" saved`, 'success');
}}
onLoadLayout={(layout) => {
setWindows([]);
setTimeout(() => {
layout.windows.forEach((w, i) => {
const app = apps.find(a => a.component === w.appId);
if (app) {
const windowId = `${app.id}-${Date.now()}-${i}`;
setWindows(prev => [...prev, {
id: windowId,
title: app.title,
icon: app.icon,
component: app.component,
x: w.x,
y: w.y,
width: w.width,
height: w.height,
minimized: false,
maximized: false,
zIndex: i + 1,
desktopId: layout.desktop
}]);
}
});
setCurrentDesktop(layout.desktop);
addToast(`Layout "${layout.name}" loaded`, 'success');
}, 100);
}}
onDeleteLayout={(name) => {
setSavedLayouts(prev => prev.filter(l => l.name !== name));
addToast(`Layout "${name}" deleted`, 'info');
}}
/>;
default: return null;
}
};
const handleGuestContinue = () => {
setShowLoginPrompt(false);
setIsBooting(false);
setIsDesktopLocked(false);
const randomTip = DAILY_TIPS[Math.floor(Math.random() * DAILY_TIPS.length)];
setDailyTip(randomTip);
setTimeout(() => setShowDailyTip(true), 1000);
};
const handleLoginFromBoot = () => {
setShowLoginPrompt(false);
setIsBooting(false);
// Keep desktop locked until login succeeds
const randomTip = DAILY_TIPS[Math.floor(Math.random() * DAILY_TIPS.length)];
setDailyTip(randomTip);
setTimeout(() => {
setShowDailyTip(true);
const passportApp = apps.find(a => a.id === 'passport');
if (passportApp) openApp(passportApp);
}, 500);
};
const unlockDesktop = () => {
setIsDesktopLocked(false);
};
const openIframeWindow = (url: string, title: string) => {
// Most external sites block iframe embedding - open in new tab instead
window.open(url, '_blank', 'noopener,noreferrer');
};
if (isBooting) {
return (
<BootSequence
onBootComplete={() => setIsBooting(false)}
onLoginClick={() => setLocation('/login')}
onGuestContinue={() => setIsBooting(false)}
/>
);
}
if (showScreensaver) {
return (
<div
className="h-screen w-screen bg-black flex items-center justify-center cursor-pointer"
onClick={() => setShowScreensaver(false)}
>
<motion.div
animate={{
x: [0, 100, -100, 50, -50, 0],
y: [0, -50, 50, -25, 25, 0],
}}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
className="text-center"
>
<div className="text-6xl font-display font-bold bg-gradient-to-r from-cyan-400 via-purple-500 to-pink-500 text-transparent bg-clip-text">
AeThex
</div>
<div className="text-white/30 text-sm mt-2 font-mono">Click to wake</div>
</motion.div>
</div>
);
}
const parallaxX = (mousePosition.x / window.innerWidth - 0.5) * 10;
const parallaxY = (mousePosition.y / window.innerHeight - 0.5) * 10;
// Native Android App Layout
if (layout.isMobile) {
const activeWindows = windows.filter(w => !w.minimized);
const currentWindow = activeWindows[activeWindows.length - 1];
// Hide system navigation bar on mount (Android only)
useEffect(() => {
const setupStatusBar = async () => {
try {
// Hide the status bar for full immersion
await StatusBar.hide();
// Set navigation bar to transparent and hide it
if ((window as any).NavigationBar) {
await (window as any).NavigationBar.backgroundColorByHexString('#00000000', false);
await (window as any).NavigationBar.hide();
}
// Enable edge-to-edge mode
document.documentElement.style.setProperty('--safe-area-inset-bottom', 'env(safe-area-inset-bottom)');
} catch (error) {
console.log('StatusBar not available on this platform');
}
};
setupStatusBar();
return () => {
// Show status bar when leaving
StatusBar.show().catch(() => {});
};
}, []);
return (
<div className="h-screen w-screen bg-black overflow-hidden flex flex-col">
<style>{`
@keyframes scan {
0% { transform: translateY(-100%); }
100% { transform: translateY(100%); }
}
@keyframes pulse-border {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.8; }
}
`}</style>
{/* Ingress Status Bar - Minimal */}
<div className="relative h-8 bg-black/90 border-b border-emerald-500/50 shrink-0">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-emerald-500/5 to-transparent"></div>
<div className="relative flex items-center justify-between px-4 h-full">
<div className="flex items-center gap-3">
<Activity className="w-3.5 h-3.5 text-emerald-400" />
<Wifi className="w-3.5 h-3.5 text-cyan-400" />
<div className="flex items-center gap-0.5">
{[...Array(4)].map((_, i) => (
<div key={i} className="w-0.5 h-1.5 bg-emerald-400 rounded-full" style={{ height: `${(i + 1) * 2}px` }} />
))}
</div>
</div>
<div className="flex items-center gap-4 text-cyan-400 text-xs font-mono font-bold tracking-wider">
<span>{batteryInfo?.level || 100}%</span>
<Battery className="w-4 h-4 text-green-400" />
<span className="font-mono text-cyan-400">{time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
</div>
</div>
</div>
{/* Main Content */}
<div className="flex-1 overflow-hidden relative bg-black">
<AnimatePresence mode="wait">
{currentWindow ? (
// Fullscreen App View with 3D Card Flip
<motion.div
key={currentWindow.id}
initial={{ rotateY: 90, opacity: 0 }}
animate={{ rotateY: 0, opacity: 1 }}
exit={{ rotateY: -90, opacity: 0 }}
transition={{ duration: 0.4, type: "spring" }}
style={{ transformStyle: "preserve-3d" }}
className="h-full w-full flex flex-col relative"
>
{/* Ingress Style - Minimal App Bar */}
<div className="relative h-12 bg-black/95 border-b-2 border-emerald-500/50 shrink-0">
<div className="absolute inset-0" style={{ animation: 'pulse-border 2s ease-in-out infinite' }}>
<div className="absolute inset-x-0 bottom-0 h-[2px] bg-gradient-to-r from-transparent via-cyan-500 to-transparent"></div>
</div>
<div className="relative flex items-center px-3 h-full">
<button
onClick={() => {
impact('light');
closeWindow(currentWindow.id);
}}
className="w-10 h-10 flex items-center justify-center border border-emerald-500/50 active:bg-emerald-500/20"
style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}
>
<ChevronLeft className="w-5 h-5 text-emerald-400" />
</button>
<div className="flex-1 px-4">
<h1 className="text-cyan-400 font-mono font-bold text-lg uppercase tracking-widest">
{currentWindow.title}
</h1>
</div>
<button
className="w-10 h-10 flex items-center justify-center border border-cyan-500/50 active:bg-cyan-500/20"
style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}
>
<MoreVertical className="w-5 h-5 text-cyan-400" />
</button>
</div>
</div>
{/* App Content */}
<div className="flex-1 overflow-auto relative bg-black">
{renderAppContent(currentWindow.component)}
</div>
</motion.div>
) : (
// ULTRA FUTURISTIC LAUNCHER
<motion.div
key="launcher"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="h-full flex flex-col relative"
>
{/* Ingress Style Search Bar */}
<div className="px-4 pt-6 pb-4">
<div className="relative bg-black/80 border border-emerald-500/50 p-3">
<div className="absolute inset-0 border border-cyan-500/30" style={{ clipPath: 'polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px))' }}></div>
<div className="relative flex items-center gap-3">
<Search className="w-5 h-5 text-emerald-400" />
<input
type="text"
placeholder="SCANNER SEARCH..."
className="flex-1 bg-transparent text-emerald-400 placeholder:text-emerald-400/40 outline-none text-sm font-mono uppercase tracking-wide"
onFocus={() => impact('light')}
/>
<Mic className="w-5 h-5 text-cyan-400" />
</div>
</div>
</div>
{/* App Grid - Hexagonal */}
<div className="flex-1 overflow-auto px-4 pb-24">
<div className="mb-6">
<div className="flex items-center gap-2 mb-3 px-2">
<div className="w-2 h-2 bg-emerald-400"></div>
<h2 className="text-emerald-400 text-xs uppercase tracking-widest font-mono font-bold">
Quick Access
</h2>
<div className="flex-1 h-[1px] bg-emerald-500/30"></div>
</div>
<div className="grid grid-cols-4 gap-3">
{apps.slice(0, 8).map((app) => (
<button
key={app.id}
onClick={() => {
impact('medium');
openApp(app);
}}
className="flex flex-col items-center gap-2 p-2 active:bg-emerald-500/10"
>
<div
className="relative w-16 h-16 bg-black border-2 border-emerald-500/50 flex items-center justify-center active:border-cyan-400"
style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}
>
<div className="text-emerald-400 scale-75">{app.icon}</div>
<div className="absolute inset-0 border border-cyan-500/20" style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}></div>
</div>
<span className="text-cyan-400 text-[9px] font-mono text-center line-clamp-2 leading-tight uppercase">
{app.title}
</span>
</button>
))}
</div>
</div>
{/* All Apps - Minimal List */}
<div>
<div className="flex items-center gap-2 mb-3 px-2">
<div className="w-2 h-2 bg-cyan-400"></div>
<h2 className="text-cyan-400 text-xs uppercase tracking-widest font-mono font-bold">
All Systems
</h2>
<div className="flex-1 h-[1px] bg-cyan-500/30"></div>
</div>
<div className="space-y-2">
{apps.slice(8).map((app) => (
<button
key={app.id}
onClick={() => {
impact('medium');
openApp(app);
}}
className="relative w-full flex items-center gap-3 p-3 border border-emerald-500/30 active:bg-emerald-500/10 active:border-cyan-500"
>
<div className="w-10 h-10 bg-black border border-emerald-500/50 flex items-center justify-center shrink-0">
<div className="text-emerald-400 scale-75">{app.icon}</div>
</div>
<span className="text-cyan-400 font-mono text-sm text-left flex-1 uppercase tracking-wide">{app.title}</span>
<ChevronRight className="w-4 h-4 text-emerald-400" />
</button>
))}
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{/* INGRESS STYLE NAVIGATION BAR - Lightweight */}
<div
className="relative bg-black/95 border-t-2 border-emerald-500/50 shrink-0 z-50"
style={{
paddingTop: '0.75rem',
paddingBottom: 'calc(0.75rem + env(safe-area-inset-bottom))',
}}
>
<div className="absolute inset-x-0 top-0 h-[2px] bg-gradient-to-r from-transparent via-cyan-500 to-transparent" style={{ animation: 'pulse-border 2s ease-in-out infinite' }}></div>
<div className="relative flex items-center justify-around px-6">
<button
onClick={() => {
impact('medium');
windows.forEach(w => closeWindow(w.id));
}}
className="relative w-14 h-14 bg-black border-2 border-emerald-500/70 flex items-center justify-center active:bg-emerald-500/20 active:border-cyan-400"
style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}
>
<Home className="w-6 h-6 text-emerald-400" />
</button>
<button
onClick={() => {
impact('medium');
const minimized = windows.filter(w => w.minimized);
if (minimized.length > 0) {
setWindows(prev => prev.map(w =>
w.id === minimized[0].id ? { ...w, minimized: false } : w
));
}
}}
className="relative w-14 h-14 bg-black border-2 border-cyan-500/70 flex items-center justify-center active:bg-cyan-500/20 active:border-emerald-400"
style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}
>
<Square className="w-6 h-6 text-cyan-400" />
{windows.filter(w => w.minimized).length > 0 && (
<span className="absolute -top-1 -right-1 w-5 h-5 bg-emerald-500 text-black text-xs flex items-center justify-center font-bold">
{windows.filter(w => w.minimized).length}
</span>
)}
</button>
<button
onClick={() => {
impact('medium');
if (currentWindow) {
closeWindow(currentWindow.id);
}
}}
className="relative w-14 h-14 bg-black border-2 border-emerald-500/70 flex items-center justify-center active:bg-emerald-500/20 active:border-cyan-400"
style={{ clipPath: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)' }}
>
<ArrowLeft className="w-6 h-6 text-emerald-400" />
</button>
</div>
</div>
{/* Floating Action Button with Orbital Menu */}
<MobileQuickActions />
</div>
);
}
console.log('🖥️ [OS] Rendering DESKTOP layout (isMobile=false)');
// Desktop/Web layout
return (
<div
className="h-screen w-screen overflow-hidden select-none relative transition-all duration-700"
style={{
background: clearanceTheme.wallpaper,
backgroundPosition: `${50 + parallaxX}% ${50 + parallaxY}%`,
backgroundSize: '110% 110%'
}}
>
<div
ref={desktopRef}
className="h-[calc(100vh-48px)] w-full relative"
onClick={handleDesktopClick}
onContextMenu={handleDesktopContextMenu}
>
<div className="absolute inset-0 bg-[linear-gradient(rgba(0,255,170,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(0,255,170,0.02)_1px,transparent_1px)] bg-[size:50px_50px] pointer-events-none" />
<DesktopWidgets time={time} weather={weatherData} notifications={notifications} />
<div className="absolute top-4 left-4 grid grid-cols-2 gap-2 w-48">
{apps.slice(0, 9).map((app) => (
<DesktopIcon
key={app.id}
icon={app.icon}
label={app.title}
onClick={() => openApp(app)}
onContextMenu={(e) => handleIconContextMenu(e, app.id)}
/>
))}
</div>
{isDesktopLocked && windows.length === 0 && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="absolute inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50"
>
<div className="text-center">
<div className="w-20 h-20 mx-auto rounded-full bg-gradient-to-br from-cyan-500 to-purple-600 flex items-center justify-center mb-4">
<Lock className="w-10 h-10 text-white" />
</div>
<h2 className="text-xl font-display text-white uppercase tracking-wider mb-2">Desktop Locked</h2>
<p className="text-white/60 text-sm font-mono mb-6">Sign in with your Passport to continue</p>
<button
onClick={() => {
const passportApp = apps.find(a => a.id === 'passport');
if (passportApp) openApp(passportApp);
}}
className="px-6 py-3 bg-cyan-500 hover:bg-cyan-400 text-black font-mono font-bold uppercase tracking-wider transition-colors"
data-testid="unlock-desktop-btn"
>
Open Passport
</button>
</div>
</motion.div>
)}
<AnimatePresence>
{windows.filter(w => !w.minimized && w.desktopId === currentDesktop).map((window) => (
<Window
key={window.id}
window={window}
isActive={activeWindowId === window.id}
onClose={() => closeWindow(window.id)}
onMinimize={() => minimizeWindow(window.id)}
onMaximize={() => toggleMaximize(window.id)}
onFocus={() => focusWindow(window.id)}
onMove={(x, y) => setWindows(prev => prev.map(w => w.id === window.id ? { ...w, x, y } : w))}
onResize={(width, height) => setWindows(prev => prev.map(w => w.id === window.id ? { ...w, width, height } : w))}
onSnap={(x, y) => handleWindowSnap(window.id, x, y, window.width, window.height)}
content={window.component === 'iframe' && window.iframeUrl ? (
<iframe
src={window.iframeUrl}
className="w-full h-full border-0"
title={window.title}
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
/>
) : renderAppContent(window.component)}
/>
))}
</AnimatePresence>
<AnimatePresence>
{contextMenu && (
<ContextMenuComponent
menu={contextMenu}
apps={apps}
onClose={() => setContextMenu(null)}
onOpenApp={openApp}
onRefresh={() => window.location.reload()}
onChangeWallpaper={() => {
const idx = WALLPAPERS.findIndex(w => w.id === wallpaper.id);
setWallpaper(WALLPAPERS[(idx + 1) % WALLPAPERS.length]);
setContextMenu(null);
}}
/>
)}
</AnimatePresence>
</div>
<Taskbar
windows={windows.filter(w => w.desktopId === currentDesktop)}
activeWindowId={activeWindowId}
apps={apps}
time={time}
showStartMenu={showStartMenu}
user={user}
isAuthenticated={isAuthenticated}
notifications={notifications}
showNotifications={showNotifications}
onToggleStartMenu={() => { setShowStartMenu(!showStartMenu); setActiveTrayPanel(null); }}
onToggleNotifications={() => setShowNotifications(!showNotifications)}
onWindowClick={(id) => {
const window = windows.find(w => w.id === id);
if (window?.minimized) {
setWindows(prev => prev.map(w => w.id === id ? { ...w, minimized: false, zIndex: maxZIndex + 1 } : w));
setMaxZIndex(prev => prev + 1);
}
focusWindow(id);
}}
onAppClick={openApp}
onLogout={handleLogout}
onNavigate={setLocation}
currentDesktop={currentDesktop}
onDesktopChange={(d) => {
setCurrentDesktop(d);
setActiveTrayPanel(null);
}}
clearanceTheme={clearanceTheme}
onSwitchClearance={switchClearance}
activeTrayPanel={activeTrayPanel}
onTrayPanelToggle={(panel) => setActiveTrayPanel(activeTrayPanel === panel ? null : panel)}
volume={volume}
onVolumeChange={setVolume}
isMuted={isMuted}
onMuteToggle={() => setIsMuted(!isMuted)}
batteryInfo={batteryInfo}
onClearNotification={(idx) => setNotifications(prev => prev.filter((_, i) => i !== idx))}
onClearAllNotifications={() => setNotifications([])}
desktopWindowCounts={[0, 1, 2, 3].map(d => windows.filter(w => w.desktopId === d).length)}
openIframeWindow={openIframeWindow}
/>
<AnimatePresence>
{isSwitchingClearance && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
className="fixed inset-0 z-[99999] flex items-center justify-center"
style={{ background: clearanceMode === 'foundation' ? '#0F172A' : '#1a0505' }}
>
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 1.2, opacity: 0 }}
transition={{ duration: 0.5 }}
className="text-center"
>
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
className="w-20 h-20 mx-auto mb-6 rounded-full flex items-center justify-center"
style={{
border: `3px solid ${clearanceMode === 'foundation' ? '#3B82F6' : '#D4AF37'}`,
borderTopColor: 'transparent'
}}
/>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="font-display text-2xl uppercase tracking-[0.3em]"
style={{ color: clearanceMode === 'foundation' ? '#C0C0C0' : '#D4AF37' }}
>
{clearanceMode === 'foundation' ? 'Entering Corp' : 'Entering Foundation'}
</motion.div>
<motion.div
initial={{ width: 0 }}
animate={{ width: '100%' }}
transition={{ duration: 0.8, delay: 0.3 }}
className="h-0.5 mt-4 mx-auto max-w-[200px]"
style={{ background: `linear-gradient(90deg, transparent, ${clearanceMode === 'foundation' ? '#3B82F6' : '#DC2626'}, transparent)` }}
/>
</motion.div>
</motion.div>
)}
</AnimatePresence>
<ParticleField />
<AnimatePresence>
{showSpotlight && (
<SpotlightSearch
query={spotlightQuery}
setQuery={setSpotlightQuery}
apps={apps}
onSelectApp={(app) => { openApp(app); setShowSpotlight(false); }}
onClose={() => setShowSpotlight(false)}
inputRef={spotlightRef}
/>
)}
</AnimatePresence>
<ToastContainer toasts={toasts} />
<AnimatePresence>
{showDailyTip && (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 50 }}
className="fixed bottom-20 right-4 z-[9998] w-80 rounded-xl overflow-hidden shadow-2xl"
style={{
background: clearanceTheme.id === 'foundation' ? 'rgba(26, 5, 5, 0.95)' : 'rgba(15, 23, 42, 0.95)',
border: `1px solid ${clearanceTheme.accent}40`
}}
>
<div
className="px-4 py-3 flex items-center justify-between"
style={{ borderBottom: `1px solid ${clearanceTheme.accent}30` }}
>
<div className="flex items-center gap-2">
<Zap className="w-4 h-4" style={{ color: clearanceTheme.accent }} />
<span className="text-sm font-semibold text-white">Daily Tip</span>
</div>
<button
onClick={() => setShowDailyTip(false)}
className="text-white/40 hover:text-white transition-colors"
data-testid="close-daily-tip"
>
<X className="w-4 h-4" />
</button>
</div>
<div className="p-4">
<div className="text-xs uppercase tracking-wider mb-2" style={{ color: clearanceTheme.accent }}>
{dailyTip.title}
</div>
<p className="text-sm text-white/80 leading-relaxed">
{dailyTip.tip}
</p>
</div>
<div className="px-4 pb-4">
<button
onClick={() => setShowDailyTip(false)}
className="w-full py-2 rounded-lg text-sm font-medium transition-all hover:scale-[1.02]"
style={{
background: `${clearanceTheme.accent}20`,
color: clearanceTheme.accent,
border: `1px solid ${clearanceTheme.accent}40`
}}
data-testid="dismiss-daily-tip"
>
Got it!
</button>
</div>
</motion.div>
)}
</AnimatePresence>
{weatherFetching && (
<div className="fixed top-4 right-4 z-[9999] flex items-center gap-2 px-3 py-2 rounded-lg bg-black/50 backdrop-blur-sm" data-testid="loading-indicator">
<Loader2 className="w-4 h-4 animate-spin" style={{ color: clearanceTheme.accent }} />
<span className="text-xs text-white/60">Syncing...</span>
</div>
)}
<AnimatePresence>
{showOnboarding && (
<OnboardingTour
step={onboardingStep}
onNext={() => setOnboardingStep(s => s + 1)}
onClose={() => setShowOnboarding(false)}
/>
)}
</AnimatePresence>
</div>
);
}
export default function AeThexOS() {
return (
<OSProviders>
<AeThexOSInner />
</OSProviders>
);
}