From 93c3ce29c7c91c717a381c018333ffc25ed8f3fc Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 26 Feb 2026 18:51:55 +0000 Subject: [PATCH] 5 --- frontend/src/pages/observation.tsx | 317 +++++++++++++---------------- 1 file changed, 144 insertions(+), 173 deletions(-) diff --git a/frontend/src/pages/observation.tsx b/frontend/src/pages/observation.tsx index 225e6f8..cb32dda 100644 --- a/frontend/src/pages/observation.tsx +++ b/frontend/src/pages/observation.tsx @@ -18,6 +18,8 @@ import { mdiStarCircle, mdiChatProcessingOutline, mdiSend, + mdiEarth, + mdiWebcam, } from '@mdi/js'; import BaseIcon from '../components/BaseIcon'; import { useAppDispatch, useAppSelector } from '../stores/hooks'; @@ -66,14 +68,6 @@ const CELEBRITIES = [ personality: 'Passionate about the environment and exploration. Intense and focused.', reaction: 'I\'m the king of the world... or at least this telescope!' }, - { - id: 'streep', - name: 'Meryl Streep', - category: 'Actress', - img: 'https://images.pexels.com/photos/2836486/pexels-photo-2836486.jpeg?auto=compress&cs=tinysrgb&w=400', - personality: 'Sophisticated, masterful, and appreciates fine detail and craft.', - reaction: 'The performance of these celestial bodies is award-worthy.' - }, { id: 'davinci', name: 'Leonardo da Vinci', @@ -81,23 +75,27 @@ const CELEBRITIES = [ img: 'https://images.pexels.com/photos/33152/european-rari-da-vinci-mona-lisa.jpg?auto=compress&cs=tinysrgb&w=400', personality: 'Scientific, curious, observant. Mentions geometry and anatomy of the universe.', reaction: 'The proportions of this universe are divine. A true masterpiece.' - }, - { - id: 'vangogh', - name: 'Vincent van Gogh', - category: 'Painter', - img: 'https://images.pexels.com/photos/161154/vincents-bedroom-in-arles-vincent-van-gogh-artist-painting-161154.jpeg?auto=compress&cs=tinysrgb&w=400', - personality: 'Emotional, sensitive to light and color. Sees swirls in the sky.', - reaction: 'The starry night... it\'s even more vibrant than I painted it!' } ]; +const REAL_PEOPLE_AVATARS = [ + 'https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/774909/pexels-photo-774909.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/1239291/pexels-photo-1239291.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/91227/pexels-photo-91227.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/712513/pexels-photo-712513.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/1181686/pexels-photo-1181686.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/1043471/pexels-photo-1043471.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/1542085/pexels-photo-1542085.jpeg?auto=compress&cs=tinysrgb&w=200', + 'https://images.pexels.com/photos/1040880/pexels-photo-1040880.jpeg?auto=compress&cs=tinysrgb&w=200' +]; + const PRESET_TARGETS = [ { id: 'mars', name: 'Mars', type: 'Planet', img: 'https://images-assets.nasa.gov/image/PIA04591/PIA04591~medium.jpg', dist: '225M km' }, { id: 'jupiter', name: 'Jupiter', type: 'Planet', img: 'https://images-assets.nasa.gov/image/PIA04866/PIA04866~medium.jpg', dist: '778M km' }, { id: 'orion', name: 'Orion Nebula', type: 'Nebula', img: 'https://images-assets.nasa.gov/image/PIA08653/PIA08653~medium.jpg', dist: '1,344 ly' }, - { id: 'andromeda', name: 'Andromeda', type: 'Galaxy', img: 'https://images-assets.nasa.gov/image/PIA15416/PIA15416~medium.jpg', dist: '2.5M ly' }, - { id: 'pillars', name: 'Pillars of Creation', type: 'Nebula', img: 'https://images-assets.nasa.gov/image/as11-40-5874/as11-40-5874~medium.jpg', dist: '6,500 ly' } + { id: 'andromeda', name: 'Andromeda', type: 'Galaxy', img: 'https://images-assets.nasa.gov/image/PIA15416/PIA15416~medium.jpg', dist: '2.5M ly' } ]; const ObservationPage = () => { @@ -108,7 +106,6 @@ const ObservationPage = () => { const [selectedTarget, setSelectedTarget] = useState(null); const [isFocusing, setIsFocusing] = useState(false); const [isSharpnessMax, setIsSharpnessMax] = useState(false); - const [telemetry, setTelemetry] = useState({ temp: -233, dist: 1.5, focal: 131 }); // Simulation States const [audienceCount, setAudienceCount] = useState(0); @@ -116,6 +113,7 @@ const ObservationPage = () => { const [activeCelebrities, setActiveCelebrities] = useState([]); const [chatMessages, setChatMessages] = useState([]); const [showSimPanel, setShowSimPanel] = useState(false); + const [activeViewers, setActiveViewers] = useState([]); // Interaction State const [userQuery, setUserQuery] = useState(''); @@ -128,7 +126,6 @@ const ObservationPage = () => { const [videoUrl, setVideoUrl] = useState(null); const dispatch = useAppDispatch(); - const { gptResponse } = useAppSelector((state) => state.openAi); useEffect(() => { dispatch(fetchSkyObjects({})); @@ -145,7 +142,7 @@ const ObservationPage = () => { setIsCameraActive(true); } } catch (err) { - console.error("Camera/Audio access denied:", err); + console.error("Camera access denied:", err); } }; @@ -157,78 +154,63 @@ const ObservationPage = () => { } }; - // Audience Simulation Logic + // Audience & Viewer Simulation Logic useEffect(() => { let interval: any; if (isSimActive) { interval = setInterval(() => { setAudienceCount(prev => { const target = 1000000; - return prev < target ? Math.min(prev + Math.floor(Math.random() * 8000) + 2000, target) : target; + return prev < target ? Math.min(prev + Math.floor(Math.random() * 15000) + 5000, target) : target; }); - if (activeCelebrities.length > 0 && Math.random() > 0.85) { + // Rotate random "Real People" viewers + if (Math.random() > 0.7) { + const randomAvatar = REAL_PEOPLE_AVATARS[Math.floor(Math.random() * REAL_PEOPLE_AVATARS.length)]; + setActiveViewers(prev => [...prev.slice(-5), { id: Date.now(), img: randomAvatar }]); + } + + if (activeCelebrities.length > 0 && Math.random() > 0.8) { const randomCelebId = activeCelebrities[Math.floor(Math.random() * activeCelebrities.length)]; const celeb = CELEBRITIES.find(c => c.id === randomCelebId); if (celeb) { setChatMessages(prev => [{ id: Date.now(), name: celeb.name, - category: celeb.category, text: celeb.reaction, time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) - }, ...prev].slice(0, 10)); + }, ...prev].slice(0, 8)); } } - }, 1500); + }, 1000); } else { setAudienceCount(0); setChatMessages([]); + setActiveViewers([]); } return () => clearInterval(interval); }, [isSimActive, activeCelebrities]); const handleAskCelebrities = async () => { if (!userQuery.trim() || activeCelebrities.length === 0) return; - setIsAsking(true); const selectedNames = activeCelebrities.map(id => CELEBRITIES.find(c => c.id === id)?.name).join(', '); - const celebContext = activeCelebrities.map(id => { - const c = CELEBRITIES.find(x => x.id === id); - return `${c?.name} (${c?.personality})`; - }).join('; '); - - const prompt = `You are simulating a live conversation between the user and these celebrities: ${selectedNames}. - User is observing ${selectedTarget ? selectedTarget.name : 'the cosmos'} through the James Webb Telescope. - Celebrity Contexts: ${celebContext}. - User asks: "${userQuery}". - Respond as one or more of these celebrities, staying perfectly in character. Keep it short, real-time, and exciting. - Format: [Name]: "Message"`; + const prompt = `Simulate a live reaction from: ${selectedNames}. The user is observing ${selectedTarget?.name || 'deep space'}. User asks: "${userQuery}". Stay in character. Short response.`; try { const resultAction = await dispatch(askGpt(prompt)); if (askGpt.fulfilled.match(resultAction)) { const response = resultAction.payload.data; - const nameMatch = response.match(/\\\[(.*?)\\\]:\s*"(.*?)\\/); - const newMessage = { id: Date.now(), - name: nameMatch ? nameMatch[1] : (activeCelebrities.length > 0 ? CELEBRITIES.find(c => c.id === activeCelebrities[0])?.name : 'AI'), - category: 'Interactive Response', - text: nameMatch ? nameMatch[2] : response, - time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }), + name: CELEBRITIES.find(c => c.id === activeCelebrities[0])?.name || 'Celebrity', + text: response, isAi: true }; - - setChatMessages(prev => [newMessage, ...prev].slice(0, 15)); + setChatMessages(prev => [newMessage, ...prev].slice(0, 10)); setLastResponse(newMessage); } - } catch (err) { - console.error(err); - } finally { - setIsAsking(false); - setUserQuery(''); - } + } catch (err) { console.error(err); } finally { setIsAsking(false); setUserQuery(''); } }; const handleZoom = (direction: 'in' | 'out') => { @@ -253,10 +235,9 @@ const ObservationPage = () => { }; const startRecording = () => { - if (!videoRef.current || !videoRef.current.srcObject) return; + if (!videoRef.current?.srcObject) return; const stream = videoRef.current.srcObject as MediaStream; - const options = { mimeType: 'video/webm;codecs=vp8,opus' }; - const recorder = new MediaRecorder(stream, MediaRecorder.isTypeSupported(options.mimeType) ? options : { mimeType: 'video/webm' }); + const recorder = new MediaRecorder(stream, { mimeType: 'video/webm' }); const chunks: Blob[] = []; recorder.ondataavailable = (e) => chunks.push(e.data); recorder.onstop = () => setVideoUrl(URL.createObjectURL(new Blob(chunks, { type: 'video/webm' }))); @@ -267,15 +248,6 @@ const ObservationPage = () => { const stopRecording = () => { mediaRecorder?.stop(); setIsRecording(false); }; - const downloadVideo = () => { - if (videoUrl) { - const a = document.createElement('a'); - a.href = videoUrl; - a.download = `JWST-INTERACTIVE-REC-${Date.now()}.webm`; - a.click(); - } - }; - const formatZoom = (z: number) => { if (z >= 1e12) return `${(z / 1e12).toFixed(1)}T`; if (z >= 1e9) return `${(z / 1e9).toFixed(1)}B`; @@ -285,138 +257,146 @@ const ObservationPage = () => { return (
- JWST | Interactive Global Live + JWST | Global Live Simulation - {/* Main Viewport */} + {/* Main Viewport */}
{!isCameraActive && (
-

Telescope Standby

- +

System Standby

+
)} -