diff --git a/client/index.html b/client/index.html index d36e27c..1237aee 100644 --- a/client/index.html +++ b/client/index.html @@ -15,6 +15,11 @@ + + + + + diff --git a/client/public/manifest.json b/client/public/manifest.json new file mode 100644 index 0000000..ad6d847 --- /dev/null +++ b/client/public/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "AeThex OS", + "short_name": "AeThex", + "description": "The Operating System for the Metaverse. Cross-platform identity and certification.", + "start_url": "/", + "display": "standalone", + "background_color": "#0F172A", + "theme_color": "#06B6D4", + "orientation": "any", + "icons": [ + { + "src": "/favicon.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + } + ], + "categories": ["productivity", "utilities", "developer"], + "prefer_related_applications": false +} diff --git a/client/public/opengraph.jpg b/client/public/opengraph.jpg index 747b745..dd895df 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 fab6f5c..07c7deb 100644 --- a/client/src/pages/os.tsx +++ b/client/src/pages/os.tsx @@ -30,6 +30,7 @@ interface WindowState { zIndex: number; accentColor?: string; desktopId: number; + iframeUrl?: string; } interface Toast { @@ -250,6 +251,7 @@ export default function AeThexOS() { }, []); const [showLoginPrompt, setShowLoginPrompt] = useState(false); + const [isDesktopLocked, setIsDesktopLocked] = useState(true); useEffect(() => { const bootSequence = async () => { @@ -461,14 +463,18 @@ export default function AeThexOS() { }, [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 = windows.find(w => w.id === app.id); + const existingWindow = windows.find(w => w.id === appToOpen.id); if (existingWindow) { setWindows(prev => prev.map(w => - w.id === app.id ? { ...w, minimized: false, zIndex: maxZIndex + 1, desktopId: currentDesktop } : w + w.id === appToOpen.id ? { ...w, minimized: false, zIndex: maxZIndex + 1, desktopId: currentDesktop } : w )); setMaxZIndex(prev => prev + 1); - setActiveWindowId(app.id); + setActiveWindowId(appToOpen.id); return; } @@ -476,14 +482,14 @@ export default function AeThexOS() { const offsetY = (windows.length % 5) * 40 + 50; const newWindow: WindowState = { - id: app.id, - title: app.title, - icon: app.icon, - component: app.component, + id: appToOpen.id, + title: appToOpen.title, + icon: appToOpen.icon, + component: appToOpen.component, x: offsetX, y: offsetY, - width: app.defaultWidth, - height: app.defaultHeight, + width: appToOpen.defaultWidth, + height: appToOpen.defaultHeight, minimized: false, maximized: false, zIndex: maxZIndex + 1, @@ -492,9 +498,9 @@ export default function AeThexOS() { setWindows(prev => [...prev, newWindow]); setMaxZIndex(prev => prev + 1); - setActiveWindowId(app.id); + setActiveWindowId(appToOpen.id); setShowStartMenu(false); - }, [windows, maxZIndex, playSound, currentDesktop]); + }, [windows, maxZIndex, playSound, currentDesktop, isDesktopLocked, apps]); const closeWindow = useCallback((id: string) => { playSound('close'); @@ -590,7 +596,7 @@ export default function AeThexOS() { const renderAppContent = (component: string) => { switch (component) { case 'terminal': return ; - case 'passport': return ; + case 'passport': return ; case 'files': return ; case 'network': return ; case 'metrics': return ; @@ -607,12 +613,13 @@ export default function AeThexOS() { case 'chat': return ; case 'music': return ; case 'pitch': return setLocation('/pitch')} />; - case 'networkneighborhood': return ; - case 'foundry': return ; - case 'devtools': return ; + case 'networkneighborhood': return ; + case 'foundry': return ; + case 'devtools': return ; case 'mission': return ; case 'intel': return ; - case 'drives': return ; + case 'drives': return ; + case 'iframe': return null; case 'settings': return { setShowLoginPrompt(false); setIsBooting(false); + setIsDesktopLocked(false); const randomTip = DAILY_TIPS[Math.floor(Math.random() * DAILY_TIPS.length)]; setDailyTip(randomTip); setTimeout(() => setShowDailyTip(true), 1000); @@ -678,6 +686,7 @@ export default function AeThexOS() { 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(() => { @@ -687,6 +696,31 @@ export default function AeThexOS() { }, 500); }; + const unlockDesktop = () => { + setIsDesktopLocked(false); + }; + + const openIframeWindow = (url: string, title: string) => { + const windowId = `iframe-${Date.now()}`; + setWindows(prev => [...prev, { + id: windowId, + title, + icon: , + component: 'iframe', + x: 100 + Math.random() * 100, + y: 100 + Math.random() * 100, + width: 900, + height: 600, + minimized: false, + maximized: false, + zIndex: maxZIndex + 1, + desktopId: currentDesktop, + iframeUrl: url + }]); + setMaxZIndex(prev => prev + 1); + setActiveWindowId(windowId); + }; + if (isBooting) { return (
@@ -807,6 +841,32 @@ export default function AeThexOS() { ))}
+ {isDesktopLocked && windows.length === 0 && ( + +
+
+ +
+

Desktop Locked

+

Sign in with your Passport to continue

+ +
+
+ )} + {windows.filter(w => !w.minimized && w.desktopId === currentDesktop).map((window) => ( 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={renderAppContent(window.component)} + content={window.component === 'iframe' && window.iframeUrl ? ( +