diff --git a/attached_assets/image_1766286925377.png b/attached_assets/image_1766286925377.png new file mode 100644 index 0000000..0d438c6 Binary files /dev/null and b/attached_assets/image_1766286925377.png differ diff --git a/client/public/opengraph.jpg b/client/public/opengraph.jpg index b317e74..747b745 100644 Binary files a/client/public/opengraph.jpg and b/client/public/opengraph.jpg differ diff --git a/client/src/pages/os.tsx b/client/src/pages/os.tsx index 51fb99d..2667d2d 100644 --- a/client/src/pages/os.tsx +++ b/client/src/pages/os.tsx @@ -29,6 +29,7 @@ interface WindowState { maximized: boolean; zIndex: number; accentColor?: string; + desktopId: number; } interface Toast { @@ -193,6 +194,24 @@ export default function AeThexOS() { const spotlightRef = useRef(null); const { user, isAuthenticated, logout } = useAuth(); const [, setLocation] = useLocation(); + const [activeTrayPanel, setActiveTrayPanel] = useState<'wifi' | 'volume' | 'battery' | 'notifications' | null>(null); + const [volume, setVolume] = useState(75); + const [isMuted, setIsMuted] = useState(false); + const [batteryInfo, setBatteryInfo] = useState<{ level: number; charging: boolean } | null>(null); + + useEffect(() => { + if ('getBattery' in navigator) { + (navigator as any).getBattery().then((battery: any) => { + setBatteryInfo({ level: Math.round(battery.level * 100), charging: battery.charging }); + battery.addEventListener('levelchange', () => { + setBatteryInfo(prev => prev ? { ...prev, level: Math.round(battery.level * 100) } : null); + }); + battery.addEventListener('chargingchange', () => { + setBatteryInfo(prev => prev ? { ...prev, charging: battery.charging } : null); + }); + }); + } + }, []); const { data: weatherData, isFetching: weatherFetching } = useQuery({ queryKey: ['weather'], @@ -440,15 +459,9 @@ export default function AeThexOS() { playSound('open'); const existingWindow = windows.find(w => w.id === app.id); if (existingWindow) { - if (existingWindow.minimized) { - setWindows(prev => prev.map(w => - w.id === app.id ? { ...w, minimized: false, zIndex: maxZIndex + 1 } : w - )); - } else { - setWindows(prev => prev.map(w => - w.id === app.id ? { ...w, zIndex: maxZIndex + 1 } : w - )); - } + setWindows(prev => prev.map(w => + w.id === app.id ? { ...w, minimized: false, zIndex: maxZIndex + 1, desktopId: currentDesktop } : w + )); setMaxZIndex(prev => prev + 1); setActiveWindowId(app.id); return; @@ -468,14 +481,15 @@ export default function AeThexOS() { height: app.defaultHeight, minimized: false, maximized: false, - zIndex: maxZIndex + 1 + zIndex: maxZIndex + 1, + desktopId: currentDesktop }; setWindows(prev => [...prev, newWindow]); setMaxZIndex(prev => prev + 1); setActiveWindowId(app.id); setShowStartMenu(false); - }, [windows, maxZIndex, playSound]); + }, [windows, maxZIndex, playSound, currentDesktop]); const closeWindow = useCallback((id: string) => { playSound('close'); @@ -616,8 +630,9 @@ export default function AeThexOS() { 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: `${app.id}-${Date.now()}-${i}`, + id: windowId, title: app.title, icon: app.icon, component: app.component, @@ -628,6 +643,7 @@ export default function AeThexOS() { minimized: false, maximized: false, zIndex: i + 1, + desktopId: layout.desktop }]); } }); @@ -734,7 +750,7 @@ export default function AeThexOS() { - {windows.filter(w => !w.minimized).map((window) => ( + {windows.filter(w => !w.minimized && w.desktopId === currentDesktop).map((window) => ( w.desktopId === currentDesktop)} activeWindowId={activeWindowId} apps={apps} time={time} @@ -779,7 +795,7 @@ export default function AeThexOS() { isAuthenticated={isAuthenticated} notifications={notifications} showNotifications={showNotifications} - onToggleStartMenu={() => setShowStartMenu(!showStartMenu)} + onToggleStartMenu={() => { setShowStartMenu(!showStartMenu); setActiveTrayPanel(null); }} onToggleNotifications={() => setShowNotifications(!showNotifications)} onWindowClick={(id) => { const window = windows.find(w => w.id === id); @@ -793,9 +809,22 @@ export default function AeThexOS() { onLogout={handleLogout} onNavigate={setLocation} currentDesktop={currentDesktop} - onDesktopChange={setCurrentDesktop} + 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)} /> @@ -1250,6 +1279,16 @@ interface TaskbarProps { onDesktopChange: (d: number) => void; clearanceTheme: ClearanceTheme; onSwitchClearance: () => void; + activeTrayPanel: 'wifi' | 'volume' | 'battery' | 'notifications' | null; + onTrayPanelToggle: (panel: 'wifi' | 'volume' | 'battery' | 'notifications') => void; + volume: number; + onVolumeChange: (v: number) => void; + isMuted: boolean; + onMuteToggle: () => void; + batteryInfo: { level: number; charging: boolean } | null; + onClearNotification: (index: number) => void; + onClearAllNotifications: () => void; + desktopWindowCounts: number[]; } function Skeleton({ className = "", animate = true }: { className?: string; animate?: boolean }) { @@ -1457,7 +1496,7 @@ function OnboardingTour({ step, onNext, onClose }: { step: number; onNext: () => ); } -function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, user, isAuthenticated, notifications, showNotifications, onToggleStartMenu, onToggleNotifications, onWindowClick, onAppClick, onLogout, onNavigate, currentDesktop, onDesktopChange, clearanceTheme, onSwitchClearance }: TaskbarProps) { +function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, user, isAuthenticated, notifications, showNotifications, onToggleStartMenu, onToggleNotifications, onWindowClick, onAppClick, onLogout, onNavigate, currentDesktop, onDesktopChange, clearanceTheme, onSwitchClearance, activeTrayPanel, onTrayPanelToggle, volume, onVolumeChange, isMuted, onMuteToggle, batteryInfo, onClearNotification, onClearAllNotifications, desktopWindowCounts }: TaskbarProps) { return ( <> @@ -1681,19 +1720,26 @@ function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, user, isA ))} -
- - - - + + +
{time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
+ + + {activeTrayPanel === 'notifications' && ( + e.stopPropagation()} + > +
+ Notifications + {notifications.length > 0 && ( + + )} +
+
+ {notifications.length === 0 ? ( +
No notifications
+ ) : ( + notifications.map((notif, idx) => ( +
+ +

{notif}

+ +
+ )) + )} +
+
+ )} + + {activeTrayPanel === 'wifi' && ( + e.stopPropagation()} + > +
+ Network Status +
+
+
+
+ +
+
+
AeThex Network
+
Connected
+
+
+
+
+ Signal Strength + Excellent +
+
+ Protocol + AEGIS-256 +
+
+ Latency + 2ms +
+
+ Node + AXIOM-CORE-01 +
+
+
+
+
+ Secure Connection Active +
+
+
+ + )} + + {activeTrayPanel === 'volume' && ( + e.stopPropagation()} + > +
+ Sound +
+
+
+ +
+
{isMuted ? 'Muted' : 'Volume'}
+
{isMuted ? 'Click to unmute' : `${volume}%`}
+
+
+
+ onVolumeChange(parseInt(e.target.value))} + className="w-full h-2 bg-white/10 rounded-lg appearance-none cursor-pointer accent-cyan-500" + style={{ accentColor: '#06b6d4' }} + /> +
+ 0 + 50 + 100 +
+
+
+
+ OS Sounds + {isMuted ? 'OFF' : 'ON'} +
+
+
+
+ )} + + {activeTrayPanel === 'battery' && ( + e.stopPropagation()} + > +
+ Power +
+
+
+
+ +
+
+
+ {batteryInfo ? `${batteryInfo.level}%` : 'Unknown'} +
+
+ {batteryInfo?.charging ? 'Charging' : 'On Battery'} +
+
+
+ {batteryInfo && ( +
+
+
50 ? 'bg-green-500' : + batteryInfo.level > 20 ? 'bg-yellow-500' : 'bg-red-500' + }`} + style={{ width: `${batteryInfo.level}%` }} + /> +
+
+ 0% + 50% + 100% +
+
+ )} + {!batteryInfo && ( +
+ Battery API not available +
+ )} +
+
+ Power Mode + Balanced +
+
+ Status + Optimal +
+
+
+ + )} +