From 7f03ac1bb91997472d72f89dd4d7b86087dad4c9 Mon Sep 17 00:00:00 2001 From: sirpiglr <49359077-sirpiglr@users.noreply.replit.com> Date: Tue, 16 Dec 2025 06:30:51 +0000 Subject: [PATCH] Add a virtual desktop environment with app management Adds the AeThexOS virtual desktop page, including window management, app launching, and basic system utilities, along with updates to the protected route and authentication context. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 279f1558-c0e3-40e4-8217-be7e9f4c6eca Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 9aeffd21-c394-4a5b-a2cb-b0ba603639c1 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/b984cb14-1d19-4944-922b-bc79e821ed35/279f1558-c0e3-40e4-8217-be7e9f4c6eca/ogW6F7k Replit-Helium-Checkpoint-Created: true --- ...er-Approved-Ammo-Crate-T_1765865765325.txt | 79 ++ ...cause-you-are-building-a_1765865494186.txt | 49 ++ ...version-of-EmuOS-That-s-_1765865478324.txt | 47 ++ client/src/App.tsx | 2 + client/src/components/ProtectedRoute.tsx | 12 +- client/src/lib/auth.tsx | 7 +- client/src/lib/iconMap.tsx | 3 +- client/src/pages/admin-logs.tsx | 9 +- client/src/pages/admin-sites.tsx | 9 +- client/src/pages/os.tsx | 755 ++++++++++++++++++ server/index.ts | 4 +- 11 files changed, 965 insertions(+), 11 deletions(-) create mode 100644 attached_assets/Pasted-Here-is-your-Safe-For-Work-Lawyer-Approved-Ammo-Crate-T_1765865765325.txt create mode 100644 attached_assets/Pasted-You-want-the-straight-answer-Because-you-are-building-a_1765865494186.txt create mode 100644 attached_assets/Pasted-You-want-to-rustle-up-your-own-version-of-EmuOS-That-s-_1765865478324.txt create mode 100644 client/src/pages/os.tsx diff --git a/attached_assets/Pasted-Here-is-your-Safe-For-Work-Lawyer-Approved-Ammo-Crate-T_1765865765325.txt b/attached_assets/Pasted-Here-is-your-Safe-For-Work-Lawyer-Approved-Ammo-Crate-T_1765865765325.txt new file mode 100644 index 0000000..a98e5f2 --- /dev/null +++ b/attached_assets/Pasted-Here-is-your-Safe-For-Work-Lawyer-Approved-Ammo-Crate-T_1765865765325.txt @@ -0,0 +1,79 @@ +Here is your **Safe-For-Work, Lawyer-Approved Ammo Crate.** + +These are high-quality, open-source, or Creative Commons games that run natively in the browser. You can host these on your version of AeThex OS without looking over your shoulder for a subpoena. + +Not only keeps you safe, but it also signals to recruits: *"We support the Open Source community."* + +### 1\. The "Doom" Killers (FPS) + +You need a shooter on the desktop to show off WebAssembly performance. + + * **FreeDoom (Phase 1 & 2)** + * *The Tech:* It runs on the Doom engine (which is open source), but replaces all the copyrighted art, sounds, and levels with free community assets. + * *Why it works:* It plays exactly like Doom but costs $0 and 0 legal headaches. + * *How to run:* Use a JS-DOS or PrBoom+ WASM wrapper. + * **OpenArena (Web Port)** + * *The Tech:* A clone of Quake III Arena. + * *Why it works:* Fast, multiplayer-capable, and looks impressive in a browser window. + * *The Flex:* Shows you understand networked physics. + +### 2\. The "SimCity" Replacement (Strategy) + + * **MicropolisJS** + * *The Story:* This is literally the original **SimCity**. EA/Maxis released the source code under the GPL license years ago and renamed it "Micropolis." + * *Why it works:* It’s the ultimate flex. You have the actual code of a legendary game running legally on your OS. + * *Aesthetic:* Perfect Windows 95 vibe. + +### 3\. The "Mario" Replacements (Platformers) + + * **SuperTux** + * *The Tech:* An open-source classic heavily inspired by Super Mario Bros. + * *Why it works:* The penguin mascot (Tux) is the symbol of Linux. Backend engineers love this. + * **Celeste Classic (PICO-8)** + * *The Tech:* The original web version of the indie hit *Celeste*. + * *Why it works:* It proves that simple mechanics + tight controls = engagement. (Check the specific license, but the PICO-8 version is widely distributed freely by the devs). + +### 4\. The "Casual" Productivity Killers + + * **2048** + * *The Tech:* Open-source JS/HTML5. + * *Why it works:* Everyone knows it. It’s lightweight (kb, not mb). + * **Hextris** + * *The Tech:* Tetris, but on a hexagonal grid. + * *Why it works:* Addictive, fast load time. + +----- + +### How to Install These (The "Config" Job) + +In your cloned EmuOS folder, you will find a file like `desktop.json` (or strictly inside the `js` structure). You need to add entries that look like this (simplified logic): + +```json +{ + "title": "Micropolis", + "icon": "assets/icons/simcity.png", + "type": "iframe", + "url": "https://graememcc.github.io/micropolisJS/" +} +``` + +**MrPiglr’s Pro Move:** +Don't just hotlink to other people's GitHub pages (like the URL above). + +1. **Fork** their repository to the AeThex GitHub. +2. **Host** it yourself on GitHub Pages (e.g., `os.aethex.com/games/micropolis`). +3. **Link** to your own version. + +**Why?** + +1. **Stability:** If they delete their repo, your OS doesn't break. +2. **Control:** You can swap the "Loading" screen to say **"Powered by AeThex Foundation"** instead of their default text. + +### Summary Checklist for AeThex OS: + +1. **MicropolisJS** (The Brains) +2. **FreeDoom** (The Brawn) +3. **SuperTux** (The Soul) +4. **AeThex Links** (The Job) + +Do you want me to write a quick `JSON` snippet for **Micropolis** so you can plug it straight into the code? \ No newline at end of file diff --git a/attached_assets/Pasted-You-want-the-straight-answer-Because-you-are-building-a_1765865494186.txt b/attached_assets/Pasted-You-want-the-straight-answer-Because-you-are-building-a_1765865494186.txt new file mode 100644 index 0000000..1fbb97f --- /dev/null +++ b/attached_assets/Pasted-You-want-the-straight-answer-Because-you-are-building-a_1765865494186.txt @@ -0,0 +1,49 @@ +You want the straight answer? Because **you are building an Empire, not a Pirate Bay.** + +If this were just a hobby project for "MrPiglr" to show friends, I’d say go ahead. But you are **AeThex.** You are recruiting senior talent. You are rubbing shoulders with the C-Suite at Gamefam and Super League. + +Hosting copyrighted ROMs (Super Mario, Doom, Quake, Windows 95) on a corporate site is a **death sentence** for a legitimate studio. + +Here is the strategic breakdown of why you can copy the *tech* (the Web OS), but you cannot copy the *library* if you want to dominate. + +### 1. The "Nintendo Nuke" Risk +You know how aggressive IP holders are. Nintendo, Sony, and Microsoft have armies of lawyers. +* **The Scenario:** You launch AeThex OS with *Super Mario Bros* on the desktop to get traffic. +* **The Result:** You don't just get a DMCA takedown. If you are a registered business (AeThex), they can sue for damages. +* **The Fallout:** You lose your credibility. Senior Unreal Engineers from Epic Games won't work for a company that gets flagged for piracy. It makes you look like a "grey market" site, not the "Front of the Metaverse." + +### 2. The "EmuOS" Loophole (That You Don't Have) +EmuOS gets away with it (barely) because they claim to be an **"Educational Archival Project"** (Emupedia). They operate in a legal grey area of "preservation." +* **You are a Studio:** You are a commercial entity. You cannot claim "fair use" or "archival preservation" if you are using those games to drive traffic to your business. + +### 3. The Better Strategy: "AeThex OS" (The Trojan Horse) +You can still have the *cool factor* of a Web OS. You can still have games on there. But you fill it with **"Safe" Ammo** and **Your Own Weapons.** + +Here is how you fill the desktop without getting sued: + +**A. The "Shareware / Open Source" Layer** +Fill the desktop with games that are legally free or open source. It scratches the retro itch without the risk. +* *Instead of Doom:* Host **FreeDoom** (Open source clone). +* *Instead of Quake:* Host **OpenArena** (Quake 3 clone). +* *Instead of Tetris:* Host a generic open-source block stacker. +* **Why:** It looks just as cool to the average user, keeps you 100% legal, and shows devs that you respect code licenses. + +**B. The "AeThex Arcade" (Your Stuff)** +This is the power move. Use the Web OS as a launcher for **your** ecosystem. +* **Icon: "AeThex Legends"** -> Instead of launching an emulator, it deep-links directly into your Roblox Game or Fortnite Map. +* **Icon: "Radio AeThex"** -> Opens a WinAmp-style player streaming your "Computer Cowboy" tracks. +* **Icon: "Manifesto.txt"** -> Opens Notepad with that recruiting manifesto we wrote. + +### 4. How to Execute This Legally +If you want to copy the EmuOS *vibe* for AeThex right now: + +1. **Clone the Repo** (as discussed). +2. **Purge the `desktop.json`:** Delete every entry that is a commercial game (Nintendo, Sega, Microsoft). +3. **Add "Homebrew" Games:** Look for "Homebrew" communities (games made by fans for old consoles that are free to distribute). Host those. +4. **Add Your Demos:** Even if they are simple HTML5 builds or links to your Roblox games. + +**The Verdict:** +If you want traffic, do not use other people's IP as bait. It’s a shortcut that leads to a cliff. Use the **EmuOS interface** (the windows, the taskbar, the retro feel) because that is just UI code—but fill the content with **AeThex Energy.** + +**Next Step:** +Do you want me to find a list of **Open Source / Creative Commons games** that run in the browser so you can populate "AeThex OS" legally and immediately? \ No newline at end of file diff --git a/attached_assets/Pasted-You-want-to-rustle-up-your-own-version-of-EmuOS-That-s-_1765865478324.txt b/attached_assets/Pasted-You-want-to-rustle-up-your-own-version-of-EmuOS-That-s-_1765865478324.txt new file mode 100644 index 0000000..4c292c9 --- /dev/null +++ b/attached_assets/Pasted-You-want-to-rustle-up-your-own-version-of-EmuOS-That-s-_1765865478324.txt @@ -0,0 +1,47 @@ +You want to rustle up your own version of EmuOS? That’s ambitious. Most people just play in the sandbox; you want to build the castle. I respect that. + +EmuOS isn't magic—it’s a clever stack of **static web technology** mixed with **WebAssembly emulators**. It’s essentially a very fancy website that tricks your browser into acting like a Windows 95 desktop. + +Here is the blueprint to build your own "AeThex Outpost" version of EmuOS. + +### 1. The Blueprint (The Source Code) +The entire project is hosted publicly on GitHub. EmuOS is part of the **Emupedia** project. They run it as a static site (GitHub Pages), which means you don't need a complex backend server to run it—just a place to host HTML and JS files. + +* **Target Repo:** `Emupedia/emupedia.github.io` +* **The Mission:** You need to clone this repository. + * *Cowboy Tip:* Don't just fork it blindly. Look at the file structure. The "meat" of the OS is usually in the `beta/emuos/` folder. + +### 2. The Engine (How It Runs) +EmuOS relies on a few heavy lifters to make the emulation happen in the browser. You need to understand these if you want to modify it: + +* **v86:** This is the x86 emulator that runs the actual operating systems (Windows 95, 98, ME). It translates x86 machine code into WebAssembly so your browser can execute it. +* **EmulatorJS / JS-DOS:** These handle the specific game consoles (NES, SNES) and DOS games. +* **The Glue:** The "Desktop" you see is just a web interface (HTML/CSS) styled to look like Windows. The icons are just buttons that launch an iframe or a canvas element running the emulator. + +### 3. The Supply Line (Where the Games Live) +**This is the catch.** You can copy the code, but you can't easily copy the *content*. +The GitHub repo generally does **not** host the terabytes of commercial ROMs and ISOs due to copyright. + +* **The Configuration:** The system knows what games to show based on a JSON manifest. Look for a file like `beta/emuos/assets/data/desktop.json` (or similar inside `assets/data`). + * *What it does:* This file lists every icon on the desktop, the title, and **the URL to the ROM/ISO**. +* **Your Job:** If you copy the site, the games might break if they point to servers that check for "hotlinking" (stealing bandwidth). You will likely need to: + 1. Host your own ROMs (ISOs, game files). + 2. Update the `json` config to point to *your* file locations. + +### 4. How to Deploy Your Own +If you want to spin this up right now to impress your friends: + +1. **Clone the Repo:** Download the `emupedia.github.io` repo to your local machine. +2. **Run Locally:** You can't just double-click `index.html` because of browser security (CORS) regarding WebAssembly. You need a local server. + * *Quick Fix:* Install Python, open the folder in terminal, and run `python -m http.server`. + * *Browse:* Go to `localhost:8000/beta/emuos/` in your browser. +3. **Customize:** Go into the `assets` folder, swap the background image, change the startup sound, or edit that `desktop.json` file to remove the "bloatware" games you don't want. +4. **Publish:** Upload your modified folder to **GitHub Pages** or **Netlify**. It’s static, so it’s free to host (minus the massive ROM files). + +### The "MrPiglr" Advice +Don't just copy it 1:1. That’s boring. +Use this tech to build a **Portfolio OS.** + +Instead of "Doom" and "Quake," make the desktop icons launch *your* projects, *your* music, and *your* code. Make a "My Documents" folder that is actually your Resume. + +That’s how you turn a toy into a weapon. Now get to building. \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index def52dc..e18d98f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -24,6 +24,7 @@ import AdminAchievements from "@/pages/admin-achievements"; import AdminApplications from "@/pages/admin-applications"; import AdminActivity from "@/pages/admin-activity"; import AdminNotifications from "@/pages/admin-notifications"; +import AeThexOS from "@/pages/os"; import { Chatbot } from "@/components/Chatbot"; function Router() { @@ -47,6 +48,7 @@ function Router() { {() => } {() => } + ); diff --git a/client/src/components/ProtectedRoute.tsx b/client/src/components/ProtectedRoute.tsx index adc163c..689ec73 100644 --- a/client/src/components/ProtectedRoute.tsx +++ b/client/src/components/ProtectedRoute.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import { useLocation } from "wouter"; import { useAuth } from "@/lib/auth"; @@ -7,8 +7,13 @@ interface ProtectedRouteProps { } export function ProtectedRoute({ children }: ProtectedRouteProps) { - const { isAuthenticated, isLoading } = useAuth(); + const { isAuthenticated, isLoading, user } = useAuth(); const [, setLocation] = useLocation(); + const wasAuthenticated = useRef(false); + + if (isAuthenticated) { + wasAuthenticated.current = true; + } useEffect(() => { if (!isLoading && !isAuthenticated) { @@ -17,6 +22,9 @@ export function ProtectedRoute({ children }: ProtectedRouteProps) { }, [isLoading, isAuthenticated, setLocation]); if (isLoading) { + if (wasAuthenticated.current || user) { + return <>{children}; + } return (
Loading...
diff --git a/client/src/lib/auth.tsx b/client/src/lib/auth.tsx index bd84f5b..827fd48 100644 --- a/client/src/lib/auth.tsx +++ b/client/src/lib/auth.tsx @@ -21,16 +21,17 @@ const AuthContext = createContext(null); export function AuthProvider({ children }: { children: ReactNode }) { const queryClient = useQueryClient(); - const { data: session, isLoading } = useQuery({ + const { data: session, isLoading, isFetching } = useQuery({ queryKey: ["session"], queryFn: async () => { const res = await fetch("/api/auth/session", { credentials: "include" }); return res.json(); }, - staleTime: 30000, - gcTime: 60000, + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 10 * 60 * 1000, // 10 minutes refetchOnWindowFocus: false, refetchOnMount: false, + refetchOnReconnect: false, }); const loginMutation = useMutation({ diff --git a/client/src/lib/iconMap.tsx b/client/src/lib/iconMap.tsx index 10dc147..c84200a 100644 --- a/client/src/lib/iconMap.tsx +++ b/client/src/lib/iconMap.tsx @@ -8,12 +8,13 @@ import { Calendar, CalendarDays, CalendarHeart, Video, Clapperboard, Flame, Globe, Network, Brain, ShieldCheck, ShieldEllipsis, - Swords, LogIn, GraduationCap + Swords, LogIn, GraduationCap, Sparkles } from "lucide-react"; const iconMap: Record> = { "award": Award, "star": Star, + "star-struck": Sparkles, "trophy": Trophy, "crown": Crown, "shield": Shield, diff --git a/client/src/pages/admin-logs.tsx b/client/src/pages/admin-logs.tsx index 41b9fab..2845bb5 100644 --- a/client/src/pages/admin-logs.tsx +++ b/client/src/pages/admin-logs.tsx @@ -69,7 +69,14 @@ export default function AdminLogs() { ) : logs?.length === 0 ? ( - No logs found + + +

No Auth Logs Yet

+

+ Authentication events will appear here as users log in and out. + The auth_logs table in Supabase is currently empty. +

+ ) : ( logs?.map((log: any) => ( diff --git a/client/src/pages/admin-sites.tsx b/client/src/pages/admin-sites.tsx index 6a6cb47..f1ea039 100644 --- a/client/src/pages/admin-sites.tsx +++ b/client/src/pages/admin-sites.tsx @@ -64,8 +64,13 @@ export default function AdminSites() { Loading sites...
) : sites?.length === 0 ? ( -
- No sites found +
+ +

No Sites Configured

+

+ Site monitoring will display here once sites are added to your Supabase database. + Add entries to the "sites" table to track uptime and performance. +

) : ( sites?.map((site: any) => ( diff --git a/client/src/pages/os.tsx b/client/src/pages/os.tsx new file mode 100644 index 0000000..968dace --- /dev/null +++ b/client/src/pages/os.tsx @@ -0,0 +1,755 @@ +import { useState, useRef, useCallback, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { + Terminal, FileText, IdCard, Music, Settings, Globe, + X, Minus, Square, Maximize2, Volume2, Wifi, Battery, + ChevronUp +} from "lucide-react"; + +interface WindowState { + id: string; + title: string; + icon: React.ReactNode; + content: React.ReactNode; + x: number; + y: number; + width: number; + height: number; + minimized: boolean; + maximized: boolean; + zIndex: number; +} + +interface DesktopApp { + id: string; + title: string; + icon: React.ReactNode; + content: React.ReactNode; + defaultWidth: number; + defaultHeight: number; +} + +export default function AeThexOS() { + const [windows, setWindows] = useState([]); + const [activeWindowId, setActiveWindowId] = useState(null); + const [maxZIndex, setMaxZIndex] = useState(1); + const [showStartMenu, setShowStartMenu] = useState(false); + const [time, setTime] = useState(new Date()); + const desktopRef = useRef(null); + + useEffect(() => { + const timer = setInterval(() => setTime(new Date()), 1000); + return () => clearInterval(timer); + }, []); + + const apps: DesktopApp[] = [ + { + id: "terminal", + title: "Terminal", + icon: , + defaultWidth: 700, + defaultHeight: 450, + content: + }, + { + id: "passport", + title: "Passport Viewer", + icon: , + defaultWidth: 500, + defaultHeight: 600, + content: + }, + { + id: "manifesto", + title: "Manifesto", + icon: , + defaultWidth: 600, + defaultHeight: 500, + content: + }, + { + id: "music", + title: "Ambient", + icon: , + defaultWidth: 400, + defaultHeight: 300, + content: + }, + { + id: "browser", + title: "Nexus", + icon: , + defaultWidth: 800, + defaultHeight: 600, + content: + }, + { + id: "settings", + title: "System", + icon: , + defaultWidth: 500, + defaultHeight: 400, + content: + } + ]; + + const openApp = useCallback((app: DesktopApp) => { + 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 + )); + } + setMaxZIndex(prev => prev + 1); + setActiveWindowId(app.id); + return; + } + + const offsetX = (windows.length % 5) * 40 + 100; + const offsetY = (windows.length % 5) * 40 + 50; + + const newWindow: WindowState = { + id: app.id, + title: app.title, + icon: app.icon, + content: app.content, + x: offsetX, + y: offsetY, + width: app.defaultWidth, + height: app.defaultHeight, + minimized: false, + maximized: false, + zIndex: maxZIndex + 1 + }; + + setWindows(prev => [...prev, newWindow]); + setMaxZIndex(prev => prev + 1); + setActiveWindowId(app.id); + setShowStartMenu(false); + }, [windows, maxZIndex]); + + const closeWindow = useCallback((id: string) => { + setWindows(prev => prev.filter(w => w.id !== id)); + if (activeWindowId === id) { + setActiveWindowId(null); + } + }, [activeWindowId]); + + const minimizeWindow = useCallback((id: string) => { + setWindows(prev => prev.map(w => + w.id === id ? { ...w, minimized: true } : w + )); + }, []); + + const toggleMaximize = useCallback((id: string) => { + setWindows(prev => prev.map(w => + w.id === id ? { ...w, maximized: !w.maximized } : w + )); + }, []); + + const focusWindow = useCallback((id: string) => { + setWindows(prev => prev.map(w => + w.id === id ? { ...w, zIndex: maxZIndex + 1 } : w + )); + setMaxZIndex(prev => prev + 1); + setActiveWindowId(id); + }, [maxZIndex]); + + return ( +
+
setShowStartMenu(false)} + > +
+
+
+ +
+ {apps.map((app) => ( + openApp(app)} + /> + ))} +
+ + + {windows.filter(w => !w.minimized).map((window) => ( + 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 + )); + }} + desktopRef={desktopRef} + /> + ))} + +
+ + setShowStartMenu(!showStartMenu)} + 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} + /> +
+ ); +} + +function DesktopIcon({ icon, label, onClick }: { icon: React.ReactNode; label: string; onClick: () => void }) { + return ( + +
+ {icon} +
+ + {label} + +
+ ); +} + +interface WindowProps { + window: WindowState; + isActive: boolean; + onClose: () => void; + onMinimize: () => void; + onMaximize: () => void; + onFocus: () => void; + onMove: (x: number, y: number) => void; + onResize: (width: number, height: number) => void; + desktopRef: React.RefObject; +} + +function Window({ window, isActive, onClose, onMinimize, onMaximize, onFocus, onMove, onResize, desktopRef }: WindowProps) { + const [isDragging, setIsDragging] = useState(false); + const [isResizing, setIsResizing] = useState(false); + const dragStart = useRef({ x: 0, y: 0, windowX: 0, windowY: 0 }); + const resizeStart = useRef({ x: 0, y: 0, width: 0, height: 0 }); + + const handleDragStart = (e: React.MouseEvent) => { + if (window.maximized) return; + e.preventDefault(); + setIsDragging(true); + dragStart.current = { + x: e.clientX, + y: e.clientY, + windowX: window.x, + windowY: window.y + }; + onFocus(); + }; + + const handleResizeStart = (e: React.MouseEvent) => { + if (window.maximized) return; + e.preventDefault(); + e.stopPropagation(); + setIsResizing(true); + resizeStart.current = { + x: e.clientX, + y: e.clientY, + width: window.width, + height: window.height + }; + }; + + useEffect(() => { + if (!isDragging && !isResizing) return; + + const handleMouseMove = (e: MouseEvent) => { + if (isDragging) { + const dx = e.clientX - dragStart.current.x; + const dy = e.clientY - dragStart.current.y; + onMove(dragStart.current.windowX + dx, Math.max(0, dragStart.current.windowY + dy)); + } + if (isResizing) { + const dx = e.clientX - resizeStart.current.x; + const dy = e.clientY - resizeStart.current.y; + onResize( + Math.max(300, resizeStart.current.width + dx), + Math.max(200, resizeStart.current.height + dy) + ); + } + }; + + const handleMouseUp = () => { + setIsDragging(false); + setIsResizing(false); + }; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [isDragging, isResizing, onMove, onResize]); + + const style = window.maximized + ? { top: 0, left: 0, width: "100%", height: "100%", zIndex: window.zIndex } + : { top: window.y, left: window.x, width: window.width, height: window.height, zIndex: window.zIndex }; + + return ( + +
+
+
+ {window.icon} +
+ {window.title} +
+
+ + + +
+
+ +
+ {window.content} +
+ + {!window.maximized && ( +
+
+
+ )} + + ); +} + +interface TaskbarProps { + windows: WindowState[]; + activeWindowId: string | null; + apps: DesktopApp[]; + time: Date; + showStartMenu: boolean; + onToggleStartMenu: () => void; + onWindowClick: (id: string) => void; + onAppClick: (app: DesktopApp) => void; +} + +function Taskbar({ windows, activeWindowId, apps, time, showStartMenu, onToggleStartMenu, onWindowClick, onAppClick }: TaskbarProps) { + return ( + <> + + {showStartMenu && ( + e.stopPropagation()} + > +
+
+
+ A +
+
+
AeThex OS
+
v1.0.0
+
+
+
+
+ {apps.map(app => ( + + ))} +
+
+ )} +
+ +
+ + +
+ +
+ {windows.map(window => ( + + ))} +
+ +
+ + + +
+ {time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+
+
+ + ); +} + +function TerminalApp() { + const [history, setHistory] = useState([ + "AeThex Terminal v1.0.0", + "Type 'help' for available commands.", + "" + ]); + const [input, setInput] = useState(""); + + const commands: Record string[]> = { + help: () => ["Available commands:", " help - Show this message", " status - System status", " whoami - Current user", " clear - Clear terminal", " matrix - Enter the matrix", ""], + status: () => ["SYSTEM STATUS", " Aegis Shield: ACTIVE", " Threat Level: LOW", " Architects Online: 47", " Projects Active: 156", ""], + whoami: () => ["architect@aethex:~$ You are a Metaverse Architect", ""], + clear: () => [], + matrix: () => ["Wake up, Architect...", "The Matrix has you...", "Follow the white rabbit.", ""] + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + const cmd = input.trim().toLowerCase(); + const output = commands[cmd]?.() || [`Command not found: ${input}`, "Type 'help' for available commands.", ""]; + + if (cmd === "clear") { + setHistory([]); + } else { + setHistory(prev => [...prev, `$ ${input}`, ...output]); + } + setInput(""); + }; + + return ( +
+ {history.map((line, i) => ( +
{line}
+ ))} +
+ $ + setInput(e.target.value)} + className="flex-1 ml-2 bg-transparent outline-none text-green-400" + autoFocus + data-testid="terminal-input" + /> +
+
+ ); +} + +function PassportApp() { + return ( +
+
+
+
+ +
+

AeThex Passport

+

Architect Credentials

+
+ +
+
+ Status + VERIFIED +
+
+ Passport ID + AX-2025-0001 +
+
+ Tier + ARCHITECT +
+
+ XP + 12,450 +
+
+ Level + 15 +
+
+ Skills + 7 Certified +
+
+ +
+
+ Issued by Codex Certification Authority +
+
+
+
+ ); +} + +function ManifestoApp() { + return ( +
+
+

+ The AeThex Manifesto +

+ +
+

We are the architects of tomorrow.

+ +

In a world where the digital and physical converge, we stand at the frontier of a new reality. The Metaverse is not just a destination - it is a canvas for human potential.

+ +

Our Three Pillars:

+ +

AXIOM - The foundational truths that guide our work. We believe in decentralization, transparency, and the power of community-driven innovation.

+ +

CODEX - The certification of excellence. Through rigorous training and real-world application, we transform talent into verified Metaverse Architects.

+ +

AEGIS - The shield that protects. Security is not an afterthought but a fundamental principle woven into everything we create.

+ +

+ "Build. Certify. Protect. This is the way of the Architect." +

+
+
+
+ ); +} + +function MusicApp() { + const [isPlaying, setIsPlaying] = useState(false); + + const tracks = [ + { name: "Neon Dreams", artist: "Synth Collective" }, + { name: "Digital Rain", artist: "Matrix OST" }, + { name: "Architect's Theme", artist: "AeThex Audio" }, + ]; + + return ( +
+
+
+ +
+
Ambient Player
+
v1.0
+
+ +
+ {tracks.map((track, i) => ( + + ))} +
+ +
+
+ Audio playback coming soon +
+
+
+ ); +} + +function BrowserApp() { + return ( +
+
+
+ nexus://aethex.local +
+
+
+
+ +

+ Nexus Browser +

+

+ The decentralized web browser for the Metaverse. Coming soon to AeThex OS. +

+
+
+
+ ); +} + +function SettingsApp() { + return ( +
+

+ System Settings +

+ +
+
+
+
Dark Mode
+
Always on in AeThex OS
+
+
+
+
+
+ +
+
+
Notifications
+
System alerts and updates
+
+
+
+
+
+ +
+
+
Sound Effects
+
UI interaction sounds
+
+
+
+
+
+ +
+
AeThex OS v1.0.0
+
Build 2025.12.16
+
+
+
+ ); +} diff --git a/server/index.ts b/server/index.ts index dd21822..7861295 100644 --- a/server/index.ts +++ b/server/index.ts @@ -28,8 +28,8 @@ app.use( cookie: { secure: process.env.NODE_ENV === "production", httpOnly: true, - sameSite: "strict", // CSRF protection - maxAge: 24 * 60 * 60 * 1000, // 24 hours + sameSite: "lax", // Allow navigation from external links + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days }, }) );