Add a fallback interface for when the 3D scene cannot be rendered
Updates Scene.tsx to include a fallback UI with realm selection when WebGL is not available, and fixes TypeScript errors in TitleBar.tsx. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 893e1048-aa5f-4dea-8907-56a7ccad680b Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/c8LGG4t Replit-Helium-Checkpoint-Created: true
This commit is contained in:
parent
82de4d9b41
commit
a2805ea740
4 changed files with 173 additions and 5 deletions
4
.replit
4
.replit
|
|
@ -52,6 +52,10 @@ externalPort = 80
|
||||||
localPort = 8044
|
localPort = 8044
|
||||||
externalPort = 3003
|
externalPort = 3003
|
||||||
|
|
||||||
|
[[ports]]
|
||||||
|
localPort = 35519
|
||||||
|
externalPort = 3002
|
||||||
|
|
||||||
[[ports]]
|
[[ports]]
|
||||||
localPort = 38557
|
localPort = 38557
|
||||||
externalPort = 3000
|
externalPort = 3000
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { Canvas, useFrame, useThree } from "@react-three/fiber";
|
import { Canvas, useFrame, useThree } from "@react-three/fiber";
|
||||||
import { Grid, OrbitControls, Text } from "@react-three/drei";
|
import { Grid, OrbitControls, Text } from "@react-three/drei";
|
||||||
|
import * as THREE from "three";
|
||||||
import { MathUtils, Vector3 } from "three";
|
import { MathUtils, Vector3 } from "three";
|
||||||
import React, { useMemo, useRef, useState } from "react";
|
import React, { useMemo, useRef, useState, useEffect } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
|
|
||||||
type Gateway = {
|
type Gateway = {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -241,7 +242,145 @@ function SceneContent() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FallbackUI() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "100vw",
|
||||||
|
height: "100vh",
|
||||||
|
background: "linear-gradient(135deg, #030712 0%, #0f172a 50%, #1e1b4b 100%)",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
fontFamily: "Inter, sans-serif",
|
||||||
|
color: "#e5e7eb",
|
||||||
|
padding: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.6 }}
|
||||||
|
style={{ textAlign: "center", marginBottom: 40 }}
|
||||||
|
>
|
||||||
|
<h1 style={{ fontSize: 48, fontWeight: 700, marginBottom: 8, letterSpacing: "0.05em" }}>
|
||||||
|
AeThex OS
|
||||||
|
</h1>
|
||||||
|
<p style={{ fontSize: 16, opacity: 0.7, letterSpacing: "0.1em" }}>
|
||||||
|
Select Your Realm
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
|
||||||
|
gap: 16,
|
||||||
|
maxWidth: 900,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{gateways.map((gw, i) => (
|
||||||
|
<motion.div
|
||||||
|
key={gw.label}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.4, delay: i * 0.1 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
to={gw.route}
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
padding: "24px 20px",
|
||||||
|
borderRadius: 16,
|
||||||
|
border: `2px solid ${gw.color}40`,
|
||||||
|
background: `${gw.color}10`,
|
||||||
|
textDecoration: "none",
|
||||||
|
color: "#e5e7eb",
|
||||||
|
textAlign: "center",
|
||||||
|
transition: "all 0.3s ease",
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = gw.color;
|
||||||
|
e.currentTarget.style.transform = "translateY(-4px)";
|
||||||
|
e.currentTarget.style.boxShadow = `0 8px 30px ${gw.color}30`;
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = `${gw.color}40`;
|
||||||
|
e.currentTarget.style.transform = "translateY(0)";
|
||||||
|
e.currentTarget.style.boxShadow = "none";
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 600,
|
||||||
|
letterSpacing: "0.1em",
|
||||||
|
color: gw.color,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{gw.label}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.6, delay: 0.5 }}
|
||||||
|
style={{ marginTop: 40 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
to="/login"
|
||||||
|
style={{
|
||||||
|
padding: "12px 24px",
|
||||||
|
borderRadius: 10,
|
||||||
|
border: "1px solid #38bdf8",
|
||||||
|
background: "rgba(14, 165, 233, 0.12)",
|
||||||
|
color: "#e0f2fe",
|
||||||
|
fontWeight: 600,
|
||||||
|
textDecoration: "none",
|
||||||
|
backdropFilter: "blur(6px)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Connect Passport
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkWebGLSupport(): boolean {
|
||||||
|
try {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||||
|
return !!gl;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Scene() {
|
export default function Scene() {
|
||||||
|
const [webglSupported, setWebglSupported] = useState<boolean | null>(null);
|
||||||
|
const [webglError, setWebglError] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setWebglSupported(checkWebGLSupport());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (webglSupported === null) {
|
||||||
|
return (
|
||||||
|
<div style={{ width: "100vw", height: "100vh", background: "#030712" }} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!webglSupported || webglError) {
|
||||||
|
return <FallbackUI />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -299,6 +438,11 @@ export default function Scene() {
|
||||||
shadows
|
shadows
|
||||||
camera={{ position: [0, 3, 12], fov: 50, near: 0.1, far: 100 }}
|
camera={{ position: [0, 3, 12], fov: 50, near: 0.1, far: 100 }}
|
||||||
gl={{ antialias: true }}
|
gl={{ antialias: true }}
|
||||||
|
onCreated={({ gl }) => {
|
||||||
|
gl.domElement.addEventListener("webglcontextlost", () => {
|
||||||
|
setWebglError(true);
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SceneContent />
|
<SceneContent />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState } from "react";
|
import { useState, CSSProperties } from "react";
|
||||||
|
|
||||||
export default function TitleBar() {
|
export default function TitleBar() {
|
||||||
const [pinned, setPinned] = useState(false);
|
const [pinned, setPinned] = useState(false);
|
||||||
|
|
@ -19,11 +19,12 @@ export default function TitleBar() {
|
||||||
padding: "0 12px",
|
padding: "0 12px",
|
||||||
background: "#050814",
|
background: "#050814",
|
||||||
color: "#9ca3af",
|
color: "#9ca3af",
|
||||||
|
// @ts-ignore - Electron-specific property
|
||||||
WebkitAppRegion: "drag",
|
WebkitAppRegion: "drag",
|
||||||
borderBottom: "1px solid #0f172a",
|
borderBottom: "1px solid #0f172a",
|
||||||
letterSpacing: "0.08em",
|
letterSpacing: "0.08em",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
}}
|
} as CSSProperties}
|
||||||
>
|
>
|
||||||
<div style={{ fontFamily: "Space Mono, monospace" }}>AeThex Terminal</div>
|
<div style={{ fontFamily: "Space Mono, monospace" }}>AeThex Terminal</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -31,8 +32,9 @@ export default function TitleBar() {
|
||||||
marginLeft: "auto",
|
marginLeft: "auto",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
|
// @ts-ignore - Electron-specific property
|
||||||
WebkitAppRegion: "no-drag",
|
WebkitAppRegion: "no-drag",
|
||||||
}}
|
} as CSSProperties}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => call("togglePin")}
|
onClick={() => call("togglePin")}
|
||||||
|
|
|
||||||
18
replit.md
18
replit.md
|
|
@ -142,6 +142,24 @@ https://supabase.aethex.tech/auth/v1/callback
|
||||||
- `https://aethex.foundation/**`
|
- `https://aethex.foundation/**`
|
||||||
- `https://supabase.aethex.tech/auth/v1/callback`
|
- `https://supabase.aethex.tech/auth/v1/callback`
|
||||||
|
|
||||||
|
## Recent Changes (December 5, 2025)
|
||||||
|
- ✅ **Electron Desktop App Support**: Added desktop application framework
|
||||||
|
- `electron/main.js` - Main Electron process with window management
|
||||||
|
- `electron/preload.js` - Secure IPC bridge for frontend communication
|
||||||
|
- `electron-builder.yml` - Build configuration for packaging
|
||||||
|
- `client/components/DesktopShell.tsx` - Desktop wrapper with title bar
|
||||||
|
- `client/components/TitleBar.tsx` - Custom title bar with pin/minimize/maximize/close
|
||||||
|
- `client/pages/Overlay.tsx` - File watcher overlay for development tools
|
||||||
|
- ✅ **3D Scene Landing Page**: New immersive realm selector
|
||||||
|
- `client/components/Scene.tsx` - Three.js/React Three Fiber 3D scene
|
||||||
|
- Animated gateway meshes for each realm (Nexus, GameForge, Foundation, Labs, Corp)
|
||||||
|
- Camera rig with smooth transitions on realm selection
|
||||||
|
- WebGL fallback UI when 3D rendering isn't available
|
||||||
|
- ✅ **Utility Services**: New backend services
|
||||||
|
- `services/pii-scrub.js` - PII scrubbing utility for privacy
|
||||||
|
- `services/watcher.js` - File watcher for development workflow
|
||||||
|
- ✅ **TypeScript Fixes**: Fixed THREE namespace import and WebkitAppRegion typing
|
||||||
|
|
||||||
## Recent Changes (December 4, 2025)
|
## Recent Changes (December 4, 2025)
|
||||||
- ✅ **RealmSwitcher Alignment Fix**: Fixed realm IDs to match ARMS taxonomy
|
- ✅ **RealmSwitcher Alignment Fix**: Fixed realm IDs to match ARMS taxonomy
|
||||||
- Old IDs (`game_developer`, `client`, `community_member`, `customer`) replaced with ARMS IDs (`labs`, `gameforge`, `corp`, `foundation`, `devlink`, `nexus`, `staff`)
|
- Old IDs (`game_developer`, `client`, `community_member`, `customer`) replaced with ARMS IDs (`labs`, `gameforge`, `corp`, `foundation`, `devlink`, `nexus`, `staff`)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue