import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import {
Sun as SunIcon,
Orbit,
Thermometer,
Scale,
ArrowRight,
X,
Compass,
Moon as MoonIcon,
Globe,
Sparkles,
Rocket,
Loader2,
BookOpen,
Save,
CheckCircle2,
ZoomIn,
ZoomOut,
Maximize,
Move,
ChevronRight,
Users
} from 'lucide-react';
// Firebase Imports
import { initializeApp } from 'firebase/app';
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from 'firebase/auth';
import { getFirestore, doc, setDoc, getDoc, collection, onSnapshot } from 'firebase/firestore';
// --- Configuration & Constants ---
const firebaseConfig = JSON.parse(__firebase_config || '{}');
const appId = typeof __app_id !== 'undefined' ? __app_id : 'solar-system-guide';
const apiKey = ""; // Gemini API Key
const GEN_MODEL = "gemini-2.5-flash-preview-09-2025";
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const SUN_DATA = {
name: "The Sun",
color: "#ffcc00",
style: "radial-gradient(circle at center, #ffffff 0%, #fff9c4 15%, #ffeb3b 40%, #f57c00 80%, #e65100 100%)",
size: 96,
description: "The Sun is the star at the center of our Solar System. It is a nearly perfect sphere of hot plasma, heated to incandescence by nuclear fusion reactions in its core.",
diameter: "1.39M km",
distance: "System Center",
gravity: "274 m/s²",
temp: "5,505°C (Surface)",
moons: "8 Primary Planets",
type: "Yellow Dwarf (G2V)"
};
const PLANET_DATA = [
{ name: "Mercury", color: "#8c8c8c", style: "radial-gradient(circle at 30% 30%, #b0b0b0, #666666 60%, #333333)", size: 10, description: "The smallest planet, Mercury is a cratered world scorched by the Sun.", diameter: "4,879 km", distance: "57.9M km", gravity: "3.7 m/s²", temp: "167°C", moons: 0, type: "Terrestrial" },
{ name: "Venus", color: "#e3bb76", style: "radial-gradient(circle at 30% 30%, #fff5d1, #e3bb76 50%, #8a6a3d)", size: 16, description: "Venus is Earth's sister planet, with a runaway greenhouse effect.", diameter: "12,104 km", distance: "108.2M km", gravity: "8.9 m/s²", temp: "464°C", moons: 0, type: "Terrestrial" },
{ name: "Earth", color: "#2271b3", style: "radial-gradient(circle at 30% 30%, #4facfe 0%, #00f2fe 10%, #2271b3 40%, #0a3d62 80%, #000 100%)", size: 18, description: "Our home, uniquely rich in liquid water and life.", diameter: "12,756 km", distance: "149.6M km", gravity: "9.8 m/s²", temp: "15°C", moons: 1, type: "Terrestrial" },
{ name: "Mars", color: "#e27b58", style: "radial-gradient(circle at 30% 30%, #ff9a7b, #e27b58 50%, #8b3a1a 90%, #000)", size: 12, description: "The Red Planet, home to ancient riverbeds and massive volcanoes.", diameter: "6,792 km", distance: "227.9M km", gravity: "3.7 m/s²", temp: "-65°C", moons: 2, type: "Terrestrial" },
{ name: "Jupiter", color: "#d39c7e", style: "linear-gradient(170deg, #6b4c3a 0%, #d39c7e 20%, #f3e5ab 40%, #d39c7e 60%, #6b4c3a 80%, #2b1d14 100%)", size: 48, description: "The king of planets, a gas giant massive enough to hold 1,300 Earths.", diameter: "142,984 km", distance: "778.6M km", gravity: "24.8 m/s²", temp: "-110°C", moons: 95, type: "Gas Giant" },
{ name: "Saturn", color: "#c5ab6e", style: "linear-gradient(180deg, #8a7b52 0%, #c5ab6e 30%, #e8d5a7 50%, #c5ab6e 70%, #6b5d3a 100%)", size: 42, hasRings: true, description: "Famous for its spectacular ring system made of ice and rock particles.", diameter: "120,536 km", distance: "1.4B km", gravity: "10.4 m/s²", temp: "-140°C", moons: 146, type: "Gas Giant" },
{ name: "Uranus", color: "#bbe1e4", style: "radial-gradient(circle at 30% 30%, #e0f7f8, #bbe1e4 50%, #7da9ac 90%, #000)", size: 28, description: "An ice giant that rotates on its side. Atmosphere contains hydrogen and helium.", diameter: "51,118 km", distance: "2.9B km", gravity: "8.7 m/s²", temp: "-195°C", moons: 28, type: "Ice Giant" },
{ name: "Neptune", color: "#6081ff", style: "radial-gradient(circle at 30% 30%, #a2b5ff, #6081ff 50%, #2d4499 90%, #000)", size: 27, description: "A dark, cold world whipped by supersonic winds, the farthest major planet.", diameter: "49,528 km", distance: "4.5B km", gravity: "11.0 m/s²", temp: "-201°C", moons: 16, type: "Ice Giant" }
];
// --- Utilities ---
async function callGemini(prompt, systemInstruction = "") {
let delay = 1000;
for (let i = 0; i < 5; i++) {
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${GEN_MODEL}:generateContent?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
systemInstruction: systemInstruction ? { parts: [{ text: systemInstruction }] } : undefined
})
});
if (!response.ok) throw new Error(`Status: ${response.status}`);
const data = await response.json();
return data.candidates?.[0]?.content?.parts?.[0]?.text || "Communication failed.";
} catch (error) {
if (i === 4) return "Network error. Deep space uplink lost.";
await new Promise(r => setTimeout(r, delay));
delay *= 2;
}
}
}
// Clean and format AI Text
const FormattedAIResponse = ({ text }) => {
if (!text) return null;
const stripHtml = (str) => str.replace(/<[^>]*>?/gm, '');
const cleanMarkdown = (str) => str.replace(/\*\*(.*?)\*\*/g, '$1').replace(/\*(.*?)\*/g, '$1').replace(/###?\s+(.*)/g, '$1').replace(/__/g, '');
const lines = text.split('\n');
return (
{lines.map((line, idx) => {
const cleaned = cleanMarkdown(stripHtml(line)).trim();
if (!cleaned) return
;
const isHeader = line.startsWith('#') || (cleaned.length < 40 && cleaned.endsWith(':'));
const isBullet = line.trim().startsWith('*') || line.trim().startsWith('-');
if (isHeader) return
{cleaned.replace(':', '')} ;
if (isBullet) return (
{cleaned.replace(/^[*-]\s*/, '')}
);
return
{cleaned}
;
})}
);
};
// --- Components ---
const PlanetInfoCard = ({ planet, onClose, user }) => {
const [aiInsight, setAiInsight] = useState("");
const [loading, setLoading] = useState(false);
const [mode, setMode] = useState("data");
const [teamLog, setTeamLog] = useState("");
const [lastSaved, setLastSaved] = useState(false);
// Collaboration Logic: Sync with Firestore
useEffect(() => {
if (!planet || !user) return;
const path = doc(db, 'artifacts', appId, 'public', 'data', `team_log_${planet.name.replace(/\s+/g, '_')}`);
const unsubscribe = onSnapshot(path, (snapshot) => {
if (snapshot.exists()) setTeamLog(snapshot.data().text || "");
}, (err) => console.error("Sync error:", err));
setAiInsight("");
setMode("data");
return () => unsubscribe();
}, [planet, user]);
if (!planet) return null;
const handleAI = async (type) => {
setLoading(true);
setMode(type);
const p = type === 'guide'
? `Provide a unique scientific discovery about ${planet.name}. IMPORTANT: Output plain text only. No HTML tags or Markdown asterisks.`
: `Draft a space agency mission brief for ${planet.name}. IMPORTANT: Output plain text only. No HTML tags or Markdown asterisks.`;
const res = await callGemini(p, "You are a professional astronomer.");
setAiInsight(res);
setLoading(false);
};
const handleLogChange = async (e) => {
const val = e.target.value;
setTeamLog(val);
if (!user) return;
try {
const path = doc(db, 'artifacts', appId, 'public', 'data', `team_log_${planet.name.replace(/\s+/g, '_')}`);
await setDoc(path, { text: val, lastUpdated: new Date().toISOString(), lastEditedBy: user.uid });
setLastSaved(true);
setTimeout(() => setLastSaved(false), 2000);
} catch (err) { console.error("Log save failed", err); }
};
return (
{planet.name}
{planet.type}
setMode("data")} className={`flex-1 py-2 text-[10px] font-bold uppercase transition-all ${mode === 'data' ? 'text-blue-400 border-b-2 border-blue-400' : 'text-slate-500'}`}>Specs
handleAI('guide')} className={`flex-1 py-2 text-[10px] font-bold uppercase transition-all ${mode === 'guide' ? 'text-purple-400 border-b-2 border-purple-400' : 'text-slate-500'}`}>✨ Guide
handleAI('mission')} className={`flex-1 py-2 text-[10px] font-bold uppercase transition-all ${mode === 'mission' ? 'text-orange-400 border-b-2 border-orange-400' : 'text-slate-500'}`}>🚀 Mission
setMode("team")} className={`flex-1 py-2 text-[10px] font-bold uppercase transition-all ${mode === 'team' ? 'text-emerald-400 border-b-2 border-emerald-400' : 'text-slate-500'}`}>🤝 Team Log
{mode === "data" ? (
{planet.description}
Distance
{planet.distance}
Diameter
{planet.diameter}
Celestial Objects
{planet.moons}
) : mode === "team" ? (
Collaboration Hub
{lastSaved && SYNCED }
Shared in real-time with all explorers viewing this planet.
) : (
{loading ? (
establishing secure uplink...
) : (
)}
)}
);
};
export default function App() {
const [selectedPlanet, setSelectedPlanet] = useState(null);
const [zoom, setZoom] = useState(1);
const [viewRotation, setViewRotation] = useState({ x: 60, y: 0 });
const [isDragging, setIsDragging] = useState(false);
const [user, setUser] = useState(null);
const lastMousePos = useRef({ x: 0, y: 0 });
const orbitalOffsets = useMemo(() => PLANET_DATA.map(() => Math.random()), []);
const stars = useMemo(() => Array.from({ length: 300 }).map((_, i) => ({
id: i, left: Math.random() * 100, top: Math.random() * 100,
size: Math.random() * 1.5 + 0.5, dur: Math.random() * 4 + 2
})), []);
// Firebase Auth Setup
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (err) { console.error("Auth init failed", err); }
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
// Interaction Handlers
const onMouseDown = (e) => {
if (e.button !== 0) return;
setIsDragging(true);
lastMousePos.current = { x: e.clientX, y: e.clientY };
};
useEffect(() => {
const onMouseMove = (e) => {
if (!isDragging) return;
const deltaX = e.clientX - lastMousePos.current.x;
const deltaY = e.clientY - lastMousePos.current.y;
setViewRotation(prev => ({
x: Math.min(Math.max(prev.x - deltaY * 0.4, 10), 90),
y: prev.y + deltaX * 0.4
}));
lastMousePos.current = { x: e.clientX, y: e.clientY };
};
const onMouseUp = () => setIsDragging(false);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
return () => { window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('mouseup', onMouseUp); };
}, [isDragging]);
useEffect(() => {
const handleWheel = (e) => {
if (selectedPlanet) return;
e.preventDefault();
setZoom(prev => Math.min(Math.max(0.4, prev + e.deltaY * -0.001), 4));
};
window.addEventListener('wheel', handleWheel, { passive: false });
return () => window.removeEventListener('wheel', handleWheel);
}, [selectedPlanet]);
return (
{/* Background stars */}
{stars.map(s => (
))}
{/* App Header */}
MB'S SOLAR SYSTEM GUIDE
interactive 3D spatial database • team enabled
{/* 3D Visualization */}
{/* THE SUN */}
{ e.stopPropagation(); setSelectedPlanet(SUN_DATA); }}
onMouseDown={(e) => e.stopPropagation()}
>
{/* Subtle Refined Glow */}
{/* PLANETS */}
{PLANET_DATA.map((p, i) => {
const radius = 140 + (i+1)*80;
const dur = 25 * (i + 1.2);
const delay = orbitalOffsets[i] * dur;
return (
{/* Planet Zone (Large Hit Area) */}
{ e.stopPropagation(); setSelectedPlanet(p); }}
onMouseDown={(e) => e.stopPropagation()}
className="absolute cursor-pointer transform-style-3d group flex items-center justify-center pointer-events-auto"
style={{ left: '50%', top: '0', width: '50px', height: '50px', transform: `translate(-50%, -50%)`, animation: `counter-spin-dynamic ${dur}s linear infinite`, animationDelay: `-${delay}s`, animationPlayState: selectedPlanet ? 'paused' : 'running' }}
>
{p.hasRings && (
)}
{p.name}
);
})}
setSelectedPlanet(null)} user={user} />
{/* Interface Overlays */}
setZoom(z => Math.min(z + 0.2, 4))} className="p-3 bg-slate-900/80 border border-slate-700 rounded-xl hover:bg-slate-800 transition-all text-[10px] font-black uppercase text-slate-300 hover:text-white">
setZoom(z => Math.max(z - 0.2, 0.4))} className="p-3 bg-slate-900/80 border border-slate-700 rounded-xl hover:bg-slate-800 transition-all text-[10px] font-black uppercase text-slate-300 hover:text-white">
{ setZoom(1); setViewRotation({x: 60, y: 0}); }} className="p-3 bg-slate-900/80 border border-slate-700 rounded-xl hover:bg-slate-800 transition-all text-[10px] font-black uppercase text-slate-300 hover:text-white">
Zoom: {Math.round(zoom * 100)}%
Tilt: {Math.round(viewRotation.x)}°
{user ? `Cloud Connection: Established` : `Linking to Team Database...`}
);
}