feat: enhance James Webb simulation with 100 trillion zoom and target focus
This commit is contained in:
parent
f02a8b21d0
commit
c8a5aca21b
@ -3,18 +3,74 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { mdiClose, mdiCamera, mdiTarget, mdiInformationOutline, mdiTelescope } from '@mdi/js';
|
||||
import {
|
||||
mdiClose,
|
||||
mdiCamera,
|
||||
mdiTarget,
|
||||
mdiInformationOutline,
|
||||
mdiTelescope,
|
||||
mdiMagnifyPlusOutline,
|
||||
mdiMagnifyMinusOutline,
|
||||
mdiOrbitVariant,
|
||||
mdiFlare,
|
||||
mdiWeatherNight,
|
||||
mdiCrosshairsGps
|
||||
} from '@mdi/js';
|
||||
import BaseIcon from '../components/BaseIcon';
|
||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||
import { fetch as fetchSkyObjects } from '../stores/sky_objects/sky_objectsSlice';
|
||||
import LayoutAuthenticated from '../layouts/Authenticated';
|
||||
|
||||
const PRESET_TARGETS = [
|
||||
{
|
||||
id: 'mars',
|
||||
name: 'Mars',
|
||||
type: 'Planet',
|
||||
img: 'https://images-assets.nasa.gov/image/PIA04591/PIA04591~medium.jpg',
|
||||
dist: '225M km',
|
||||
temp: '210K'
|
||||
},
|
||||
{
|
||||
id: 'jupiter',
|
||||
name: 'Jupiter',
|
||||
type: 'Planet',
|
||||
img: 'https://images-assets.nasa.gov/image/PIA04866/PIA04866~medium.jpg',
|
||||
dist: '778M km',
|
||||
temp: '110K'
|
||||
},
|
||||
{
|
||||
id: 'orion',
|
||||
name: 'Orion Nebula',
|
||||
type: 'Nebula',
|
||||
img: 'https://images-assets.nasa.gov/image/PIA08653/PIA08653~medium.jpg',
|
||||
dist: '1,344 ly',
|
||||
temp: '10,000K'
|
||||
},
|
||||
{
|
||||
id: 'andromeda',
|
||||
name: 'Andromeda',
|
||||
type: 'Galaxy',
|
||||
img: 'https://images-assets.nasa.gov/image/PIA15416/PIA15416~medium.jpg',
|
||||
dist: '2.5M ly',
|
||||
temp: '2.7K'
|
||||
},
|
||||
{
|
||||
id: 'pillars',
|
||||
name: 'Pillars of Creation',
|
||||
type: 'Nebula',
|
||||
img: 'https://images-assets.nasa.gov/image/as11-40-5874/as11-40-5874~medium.jpg', // Using a generic high-res space image placeholder
|
||||
dist: '6,500 ly',
|
||||
temp: '15K'
|
||||
}
|
||||
];
|
||||
|
||||
const ObservationPage = () => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const [isCameraActive, setIsCameraActive] = useState(false);
|
||||
const [mode, setMode] = useState<'normal' | 'ir' | 'deep'>('normal');
|
||||
const [scanning, setScanning] = useState(false);
|
||||
const [identifiedObject, setIdentifiedObject] = useState<any>(null);
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const [selectedTarget, setSelectedTarget] = useState<any>(null);
|
||||
const [isFocusing, setIsFocusing] = useState(false);
|
||||
const [telemetry, setTelemetry] = useState({
|
||||
temp: -233,
|
||||
dist: 1.5,
|
||||
@ -22,8 +78,6 @@ const ObservationPage = () => {
|
||||
});
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { sky_objects } = useAppSelector((state) => state.sky_objects);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchSkyObjects({}));
|
||||
@ -56,18 +110,34 @@ const ObservationPage = () => {
|
||||
return () => stopCamera();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (scanning) {
|
||||
const timeout = setTimeout(() => {
|
||||
if (sky_objects.length > 0) {
|
||||
const randomObj = sky_objects[Math.floor(Math.random() * sky_objects.length)];
|
||||
setIdentifiedObject(randomObj);
|
||||
}
|
||||
setScanning(false);
|
||||
}, 3000);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [scanning, sky_objects]);
|
||||
const handleZoom = (direction: 'in' | 'out') => {
|
||||
setZoom(prev => {
|
||||
if (direction === 'in') {
|
||||
return Math.min(prev * 2.5, 100000000000000);
|
||||
} else {
|
||||
return Math.max(prev / 2.5, 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const selectTarget = (target: any) => {
|
||||
setSelectedTarget(target);
|
||||
setIsFocusing(true);
|
||||
|
||||
// Animate zoom in
|
||||
let currentZoom = zoom;
|
||||
const targetZoom = 1000000;
|
||||
const interval = setInterval(() => {
|
||||
currentZoom *= 1.5;
|
||||
if (currentZoom >= targetZoom) {
|
||||
setZoom(targetZoom);
|
||||
setIsFocusing(false);
|
||||
clearInterval(interval);
|
||||
} else {
|
||||
setZoom(currentZoom);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Simulate telemetry fluctuations
|
||||
useEffect(() => {
|
||||
@ -89,45 +159,83 @@ const ObservationPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const formatZoom = (z: number) => {
|
||||
if (z >= 1000000000000) return `${(z / 1000000000000).toFixed(1)}T`;
|
||||
if (z >= 1000000000) return `${(z / 1000000000).toFixed(1)}B`;
|
||||
if (z >= 1000000) return `${(z / 1000000).toFixed(1)}M`;
|
||||
if (z >= 1000) return `${(z / 1000).toFixed(1)}K`;
|
||||
return `${z.toFixed(0)}x`;
|
||||
};
|
||||
|
||||
const isDeepZoom = zoom > 5000;
|
||||
|
||||
return (
|
||||
<div className="relative h-screen w-full bg-black overflow-hidden flex flex-col font-mono text-[#00F2FF]">
|
||||
<Head>
|
||||
<title>JWST | Live Observation</title>
|
||||
<title>JWST | Deep Space Observation</title>
|
||||
</Head>
|
||||
|
||||
{/* Camera View */}
|
||||
<div className="absolute inset-0 z-0 bg-gray-900">
|
||||
{/* Main Viewport */}
|
||||
<div className="absolute inset-0 z-0 bg-gray-950 overflow-hidden">
|
||||
{!isCameraActive && (
|
||||
<div className="flex flex-col items-center justify-center h-full space-y-6">
|
||||
<div className="w-24 h-24 border-2 border-[#E3B341] rounded-full flex items-center justify-center animate-pulse">
|
||||
<BaseIcon path={mdiTelescope} size={48} className="text-[#E3B341]" />
|
||||
<div className="flex flex-col items-center justify-center h-full space-y-6 z-10 relative">
|
||||
<div className="w-32 h-32 border-2 border-[#E3B341] rounded-full flex items-center justify-center animate-pulse">
|
||||
<BaseIcon path={mdiTelescope} size={64} className="text-[#E3B341]" />
|
||||
</div>
|
||||
<p className="text-[#E3B341] uppercase tracking-[0.3em] font-bold">Systems Offline</p>
|
||||
<p className="text-[#E3B341] uppercase tracking-[0.4em] font-bold text-xl">Systems Offline</p>
|
||||
<button
|
||||
onClick={startCamera}
|
||||
className="bg-[#E3B341] text-black px-8 py-3 font-bold uppercase tracking-widest hover:bg-white transition-colors"
|
||||
className="bg-[#E3B341] text-black px-12 py-4 font-bold uppercase tracking-widest hover:bg-white transition-all transform hover:scale-105"
|
||||
>
|
||||
Initialize Deployment
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Live Camera Feed */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
autoPlay
|
||||
playsInline
|
||||
muted
|
||||
className={`w-full h-full object-cover transition-all duration-1000 ${isCameraActive ? 'opacity-100' : 'opacity-0'}`}
|
||||
style={{ filter: getFilter() }}
|
||||
className={`absolute inset-0 w-full h-full object-cover transition-all duration-1000 ${isCameraActive && (!isDeepZoom || !selectedTarget) ? 'opacity-100' : 'opacity-0'}`}
|
||||
style={{
|
||||
filter: getFilter(),
|
||||
transform: `scale(${1 + Math.log10(zoom)})`,
|
||||
transition: 'transform 0.5s ease-out, opacity 1.5s ease-in-out'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Deep Space High-Res Layer */}
|
||||
{selectedTarget && (
|
||||
<div
|
||||
className={`absolute inset-0 w-full h-full bg-cover bg-center transition-opacity duration-1000 ${isDeepZoom ? 'opacity-100' : 'opacity-0'}`}
|
||||
style={{
|
||||
backgroundImage: `url(${selectedTarget.img})`,
|
||||
transform: `scale(${1 + (Math.log10(zoom) - 3) / 20})`,
|
||||
filter: getFilter()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Loading Overlay when Focusing */}
|
||||
{isFocusing && (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<div className="w-16 h-16 border-4 border-[#00F2FF] border-t-transparent rounded-full animate-spin"></div>
|
||||
<div className="text-[#00F2FF] uppercase tracking-[0.3em] font-bold animate-pulse">Adjusting Focal Plane...</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* JWST Hexagonal Overlay */}
|
||||
{isCameraActive && (
|
||||
<div className="absolute inset-0 z-10 pointer-events-none opacity-40">
|
||||
<div className="absolute inset-0 z-10 pointer-events-none opacity-20">
|
||||
<svg className="w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<pattern id="hexagons" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="scale(1.5)">
|
||||
<path d="M5 0 L10 2.5 L10 7.5 L5 10 L0 7.5 L0 2.5 Z" fill="none" stroke="#E3B341" strokeWidth="0.1" />
|
||||
<pattern id="hexagons" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="scale(2.5)">
|
||||
<path d="M5 0 L10 2.5 L10 7.5 L5 10 L0 7.5 L0 2.5 Z" fill="none" stroke="#E3B341" strokeWidth="0.03" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#hexagons)" />
|
||||
@ -135,89 +243,158 @@ const ObservationPage = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Telemetry & UI */}
|
||||
{/* UI Controls & Telemetry */}
|
||||
{isCameraActive && (
|
||||
<div className="absolute inset-0 z-20 p-6 flex flex-col justify-between pointer-events-none">
|
||||
<div className="absolute inset-0 z-20 p-4 flex flex-col justify-between pointer-events-none">
|
||||
{/* Top Bar */}
|
||||
<div className="flex justify-between items-start pointer-events-auto">
|
||||
<div className="bg-black/60 border-l-4 border-[#E3B341] p-4 backdrop-blur-md">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-tighter">System Status</div>
|
||||
<div className="bg-black/80 border-l-4 border-[#E3B341] p-4 backdrop-blur-lg">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-tighter">Mission Control: NASA-ESA-CSA</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full animate-ping"></div>
|
||||
<div className="font-bold text-white tracking-widest uppercase">Operational</div>
|
||||
<div className="font-bold text-[#E3B341] tracking-widest uppercase">
|
||||
{isFocusing ? 'Acquiring Target' : 'Observation Stable'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 grid grid-cols-2 gap-x-4 text-[10px]">
|
||||
<div>TEMP: <span className="text-[#E3B341]">{telemetry.temp.toFixed(1)}K</span></div>
|
||||
<div>L2 DIST: <span className="text-[#E3B341]">{telemetry.dist.toFixed(3)}M km</span></div>
|
||||
<div className="mt-3 grid grid-cols-2 gap-x-6 text-[10px]">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-500">BUS TEMP</span>
|
||||
<span className="text-white text-sm">{(selectedTarget && isDeepZoom ? 6.5 : telemetry.temp).toFixed(1)}K</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-500">MAGNIFICATION</span>
|
||||
<span className="text-[#00F2FF] text-sm font-bold">{formatZoom(zoom)}</span>
|
||||
</div>
|
||||
<div className="flex flex-col mt-2">
|
||||
<span className="text-gray-500">FOCAL POINT</span>
|
||||
<span className="text-white text-sm">{telemetry.focal.toFixed(1)}mm</span>
|
||||
</div>
|
||||
<div className="flex flex-col mt-2">
|
||||
<span className="text-gray-500">L2 DISTANCE</span>
|
||||
<span className="text-white text-sm">1,502,401.2 km</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => { setSelectedTarget(null); setZoom(1); }}
|
||||
className="p-3 bg-black/80 border border-[#E3B341]/40 hover:bg-[#E3B341]/20 transition-all text-[#E3B341]"
|
||||
>
|
||||
<BaseIcon path={mdiOrbitVariant} size={20} />
|
||||
</button>
|
||||
<button
|
||||
onClick={stopCamera}
|
||||
className="p-3 bg-black/80 border border-red-500/40 hover:bg-red-500/40 transition-all"
|
||||
>
|
||||
<BaseIcon path={mdiClose} size={20} className="text-white" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Zoom Controls */}
|
||||
<div className="absolute right-4 top-1/2 -translate-y-1/2 flex flex-col space-y-4 pointer-events-auto items-center">
|
||||
<div className="text-[10px] text-[#00F2FF] font-bold mb-2 uppercase rotate-90 w-20 text-center">Magnify</div>
|
||||
<button
|
||||
onClick={stopCamera}
|
||||
className="p-2 bg-black/60 border border-white/20 hover:bg-red-500/40 transition-colors"
|
||||
onClick={() => handleZoom('in')}
|
||||
className="w-12 h-12 bg-black/80 border border-[#00F2FF] flex items-center justify-center hover:bg-[#00F2FF] hover:text-black transition-all rounded-full shadow-[0_0_15px_rgba(0,242,255,0.3)]"
|
||||
>
|
||||
<BaseIcon path={mdiClose} size={24} className="text-white" />
|
||||
<BaseIcon path={mdiMagnifyPlusOutline} size={24} />
|
||||
</button>
|
||||
<div className="h-40 w-1 bg-gray-800 relative rounded-full overflow-hidden border border-white/10">
|
||||
<div
|
||||
className="absolute bottom-0 left-0 w-full bg-[#00F2FF] transition-all duration-300"
|
||||
style={{ height: `${(Math.log10(zoom) / 14) * 100}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleZoom('out')}
|
||||
className="w-12 h-12 bg-black/80 border border-[#00F2FF] flex items-center justify-center hover:bg-[#00F2FF] hover:text-black transition-all rounded-full"
|
||||
>
|
||||
<BaseIcon path={mdiMagnifyMinusOutline} size={24} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Identification Box */}
|
||||
<div className="flex justify-center mb-12">
|
||||
<div className="bg-black/60 border border-[#00F2FF]/40 p-6 backdrop-blur-md w-full max-w-md relative overflow-hidden">
|
||||
{scanning && (
|
||||
<div className="absolute inset-0 bg-[#00F2FF]/10 animate-pulse"></div>
|
||||
)}
|
||||
<div className="flex items-center space-x-4 mb-4">
|
||||
<div className={`p-2 rounded-full border ${scanning ? 'border-[#00F2FF] animate-spin' : 'border-gray-500'}`}>
|
||||
<BaseIcon path={mdiTarget} size={24} className={scanning ? 'text-[#00F2FF]' : 'text-gray-500'} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[10px] uppercase text-gray-400 tracking-widest">Active Search</div>
|
||||
<div className="font-bold text-lg uppercase tracking-widest">
|
||||
{scanning ? 'Analyzing Spectrum...' : identifiedObject ? identifiedObject.name : 'No Target Lock'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{identifiedObject && !scanning && (
|
||||
<div className="text-xs text-gray-300 border-t border-white/10 pt-4 space-y-1">
|
||||
<p><span className="text-[#00F2FF]">COORDINATES:</span> {identifiedObject.ra || 'N/A'}, {identifiedObject.dec || 'N/A'}</p>
|
||||
<p><span className="text-[#00F2FF]">CLASSIFICATION:</span> {identifiedObject.type || 'STEL-OBJ'}</p>
|
||||
<p className="mt-2 line-clamp-2">{identifiedObject.description}</p>
|
||||
{/* Targeting HUD */}
|
||||
{isDeepZoom && (
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<div className="w-80 h-80 border border-[#00F2FF]/20 rounded-lg relative animate-pulse">
|
||||
<div className="absolute -top-1 -left-1 w-4 h-4 border-t-2 border-l-2 border-[#00F2FF]"></div>
|
||||
<div className="absolute -top-1 -right-1 w-4 h-4 border-t-2 border-r-2 border-[#00F2FF]"></div>
|
||||
<div className="absolute -bottom-1 -left-1 w-4 h-4 border-b-2 border-l-2 border-[#00F2FF]"></div>
|
||||
<div className="absolute -bottom-1 -right-1 w-4 h-4 border-b-2 border-r-2 border-[#00F2FF]"></div>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<BaseIcon path={mdiCrosshairsGps} size={32} className="text-[#00F2FF]/40" />
|
||||
</div>
|
||||
)}
|
||||
{!scanning && (
|
||||
<button
|
||||
onClick={() => setScanning(true)}
|
||||
className="mt-4 w-full bg-[#00F2FF]/20 border border-[#00F2FF] py-2 text-[10px] uppercase tracking-[0.4em] font-bold hover:bg-[#00F2FF] hover:text-black transition-all pointer-events-auto"
|
||||
>
|
||||
Initiate Deep Scan
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bottom Controls */}
|
||||
<div className="flex justify-center space-x-4 pointer-events-auto pb-6">
|
||||
{[
|
||||
{ id: 'normal', label: 'VIS', color: 'bg-white/10 text-white' },
|
||||
{ id: 'ir', label: 'NIRSpec', color: 'bg-[#E3B341]/20 text-[#E3B341]' },
|
||||
{ id: 'deep', label: 'MIRI', color: 'bg-purple-500/20 text-purple-300' }
|
||||
].map(btn => (
|
||||
<button
|
||||
key={btn.id}
|
||||
onClick={() => setMode(btn.id as any)}
|
||||
className={`px-6 py-2 border transition-all ${btn.color} ${mode === btn.id ? 'border-current' : 'border-transparent opacity-50 hover:opacity-100'}`}
|
||||
>
|
||||
<div className="text-[10px] uppercase font-bold tracking-widest">{btn.label}</div>
|
||||
</button>
|
||||
))}
|
||||
{/* Bottom Area: Controls */}
|
||||
<div className="flex flex-col items-center space-y-6">
|
||||
{/* Target Selector */}
|
||||
<div className="flex flex-wrap justify-center gap-2 pointer-events-auto bg-black/60 p-3 backdrop-blur-md rounded-xl border border-white/10 max-w-lg">
|
||||
<div className="w-full text-[8px] text-gray-500 uppercase tracking-widest text-center mb-1">Select Astronomical Target</div>
|
||||
{PRESET_TARGETS.map(target => (
|
||||
<button
|
||||
key={target.id}
|
||||
onClick={() => selectTarget(target)}
|
||||
className={`px-3 py-1.5 text-[9px] uppercase tracking-[0.2em] border transition-all ${selectedTarget?.id === target.id ? 'bg-[#E3B341] text-black border-[#E3B341]' : 'bg-black/60 text-white border-white/20 hover:border-[#E3B341]'}`}
|
||||
>
|
||||
{target.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Sensor Modes */}
|
||||
<div className="flex justify-center space-x-6 pointer-events-auto pb-4">
|
||||
{[
|
||||
{ id: 'normal', label: 'VIS', icon: mdiWeatherNight, color: 'text-white' },
|
||||
{ id: 'ir', label: 'NIRSpec', icon: mdiFlare, color: 'text-[#E3B341]' },
|
||||
{ id: 'deep', label: 'MIRI', icon: mdiOrbitVariant, color: 'text-purple-400' }
|
||||
].map(btn => (
|
||||
<button
|
||||
key={btn.id}
|
||||
onClick={() => setMode(btn.id as any)}
|
||||
className={`flex flex-col items-center p-2 w-16 transition-all ${mode === btn.id ? 'scale-110' : 'opacity-40 hover:opacity-100'}`}
|
||||
>
|
||||
<div className={`p-2 rounded-full border ${mode === btn.id ? 'border-[#00F2FF] bg-[#00F2FF]/10' : 'border-white/10'}`}>
|
||||
<BaseIcon path={btn.icon} size={20} className={btn.color} />
|
||||
</div>
|
||||
<div className={`text-[8px] mt-1 font-bold tracking-tighter ${btn.color}`}>{btn.label}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Static Footer (only visible when not active or on desktop) */}
|
||||
<div className="absolute bottom-4 right-4 text-[8px] text-gray-600 uppercase tracking-[0.5em] pointer-events-none">
|
||||
Mission Protocol: JWST-MAIN-SYS-V2.0.26
|
||||
</div>
|
||||
{/* Target Info Overlay */}
|
||||
{selectedTarget && isDeepZoom && (
|
||||
<div className="absolute bottom-28 left-6 z-30 pointer-events-none">
|
||||
<div className="bg-black/90 border-l-4 border-[#00F2FF] p-6 backdrop-blur-2xl max-w-xs animate-fade-in shadow-[0_0_30px_rgba(0,0,0,0.5)]">
|
||||
<div className="text-[10px] text-[#00F2FF] uppercase tracking-[0.3em] font-bold">Spectral Classification</div>
|
||||
<h2 className="text-3xl font-bold text-white uppercase tracking-tighter my-1">{selectedTarget.name}</h2>
|
||||
<div className="mt-4 space-y-2 text-[10px] text-gray-400 border-t border-white/10 pt-4">
|
||||
<div className="flex justify-between">
|
||||
<span>DISTANCE</span>
|
||||
<span className="text-white font-bold">{selectedTarget.dist}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>THERMAL SIG</span>
|
||||
<span className="text-white font-bold">{selectedTarget.temp}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>COORD</span>
|
||||
<span className="text-white font-bold">RA 5h 35m 17s | Dec -5° 23' 28"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Futuristic Scanline */}
|
||||
<div className="absolute inset-0 pointer-events-none z-40 bg-scanline opacity-10"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user