Spaces:
No application file
No application file
Download frontend/src/components/ControlPanel.tsx from dimensionalpulsar/adn: direct link, hf CLI and curl.
- Browser
- Download file 6.24 kB
-
https://huggingface.co/spaces/dimensionalpulsar/adn/resolve/main/frontend/src/components/ControlPanel.tsx
- Command line
-
hf download hf://spaces/dimensionalpulsar/adn/frontend/src/components/ControlPanel.tsx
-
curl -L -o ControlPanel.tsx https://huggingface.co/spaces/dimensionalpulsar/adn/resolve/main/frontend/src/components/ControlPanel.tsx
6.24 kB
| import { useStore } from '../store'; | |
| import { useAIAdaptation } from '../hooks/useAIAdaptation'; | |
| interface SliderProps { | |
| label: string; | |
| value: number; | |
| min: number; | |
| max: number; | |
| step: number; | |
| unit: string; | |
| onChange: (v: number) => void; | |
| colorClass?: string; | |
| } | |
| function Slider({ label, value, min, max, step, unit, onChange, colorClass }: SliderProps) { | |
| return ( | |
| <div className="flex flex-col gap-1"> | |
| <div className="flex justify-between text-xs text-blue-200 font-mono"> | |
| <span>{label}</span> | |
| <span className={`font-bold ${colorClass ?? 'text-cyan-300'}`}> | |
| {value.toFixed(1)} {unit} | |
| </span> | |
| </div> | |
| <input | |
| type="range" | |
| min={min} | |
| max={max} | |
| step={step} | |
| value={value} | |
| onChange={(e) => onChange(parseFloat(e.target.value))} | |
| className="slider-thumb w-full h-1 appearance-none rounded cursor-pointer | |
| bg-gradient-to-r from-blue-900 to-cyan-500 accent-cyan-400" | |
| /> | |
| <div className="flex justify-between text-[10px] text-blue-600"> | |
| <span>{min}{unit}</span> | |
| <span>{max}{unit}</span> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function Badge({ children, color }: { children: React.ReactNode; color: string }) { | |
| return ( | |
| <span className={`px-2 py-0.5 rounded text-xs font-bold ${color}`}> | |
| {children} | |
| </span> | |
| ); | |
| } | |
| export default function ControlPanel() { | |
| useAIAdaptation(); // attach AI calls | |
| const { | |
| waveAmplitude, setWaveAmplitude, | |
| temperature, setTemperature, | |
| waveExpansion, setWaveExpansion, | |
| finScale, finDesign, resistance, designNote, | |
| isLoadingAI, aiSource, | |
| } = useStore(); | |
| return ( | |
| <div | |
| className="absolute top-4 left-4 z-10 flex flex-col gap-4 p-5 rounded-2xl | |
| backdrop-blur-md bg-[rgba(8,20,45,0.82)] border border-blue-800 | |
| shadow-2xl text-white select-none" | |
| style={{ width: 280 }} | |
| > | |
| {/* Header */} | |
| <div className="flex items-center gap-2 border-b border-blue-800 pb-3"> | |
| <span className="text-2xl">🚢</span> | |
| <div> | |
| <h1 className="text-sm font-bold text-cyan-300 leading-none">NavSim 3D</h1> | |
| <p className="text-[10px] text-blue-400">Simulación Hidrodinámica</p> | |
| </div> | |
| {isLoadingAI && ( | |
| <span className="ml-auto text-[10px] text-yellow-300 animate-pulse">⟳ IA activa</span> | |
| )} | |
| </div> | |
| {/* Sliders */} | |
| <Slider | |
| label="🌊 Oleaje (Amplitud)" | |
| value={waveAmplitude} | |
| min={0} max={8} step={0.1} | |
| unit="m" | |
| onChange={setWaveAmplitude} | |
| colorClass="text-cyan-300" | |
| /> | |
| <Slider | |
| label="🌡 Temperatura" | |
| value={temperature} | |
| min={0} max={40} step={0.5} | |
| unit="°C" | |
| onChange={setTemperature} | |
| colorClass="text-orange-300" | |
| /> | |
| <Slider | |
| label="↔ Expansión de Olas" | |
| value={waveExpansion} | |
| min={0.1} max={4.0} step={0.1} | |
| unit="×" | |
| onChange={setWaveExpansion} | |
| colorClass="text-purple-300" | |
| /> | |
| {/* AI Results */} | |
| <div className="border-t border-blue-800 pt-3 flex flex-col gap-2"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-xs text-blue-300 font-semibold">🤖 IA Adaptativa</span> | |
| {aiSource === 'huggingface' && <Badge color="bg-green-900 text-green-300">HuggingFace</Badge>} | |
| {aiSource === 'algorithmic' && <Badge color="bg-yellow-900 text-yellow-300">Algorítmico</Badge>} | |
| </div> | |
| {/* Fin Scale meter */} | |
| <div className="flex items-center gap-2"> | |
| <span className="text-[10px] text-blue-400 w-20">Aletas</span> | |
| <div className="flex-1 bg-blue-950 rounded h-2"> | |
| <div | |
| className="h-2 rounded bg-gradient-to-r from-blue-500 to-cyan-400 transition-all duration-700" | |
| style={{ width: `${((finScale - 0.5) / 1.5) * 100}%` }} | |
| /> | |
| </div> | |
| <span className="text-[11px] text-cyan-300 font-mono w-8">{finScale.toFixed(2)}×</span> | |
| </div> | |
| {/* Fin Design */} | |
| {finDesign && ( | |
| <div className="bg-blue-950 rounded-lg p-2 text-[10px] text-blue-300 font-mono grid grid-cols-2 gap-x-3 gap-y-0.5"> | |
| <span>Perfil:</span><span className="text-cyan-200">{finDesign.profile}</span> | |
| <span>Envergadura:</span><span className="text-cyan-200">{finDesign.span} m</span> | |
| <span>Flecha:</span><span className="text-cyan-200">{finDesign.sweepAngle}°</span> | |
| <span>Espesor:</span><span className="text-cyan-200">{(finDesign.thickness * 100).toFixed(1)}%</span> | |
| <span>Cantidad:</span><span className="text-cyan-200">{finDesign.quantity}</span> | |
| </div> | |
| )} | |
| {/* Resistance */} | |
| {resistance && ( | |
| <div className="bg-blue-950 rounded-lg p-2 text-[10px] font-mono"> | |
| <p className="text-blue-400 mb-1 font-semibold">Resistencia (N)</p> | |
| <div className="grid grid-cols-2 gap-x-3 gap-y-0.5"> | |
| <span className="text-blue-300">Oleaje:</span> | |
| <span className="text-red-300">{resistance.waveResistance.toLocaleString()}</span> | |
| <span className="text-blue-300">Viscosa:</span> | |
| <span className="text-orange-300">{resistance.viscousResistance.toLocaleString()}</span> | |
| <span className="text-blue-300">Aletas:</span> | |
| <span className="text-yellow-300">{resistance.finPenalty.toLocaleString()}</span> | |
| <span className="text-blue-200 font-bold">Total:</span> | |
| <span className="text-white font-bold">{resistance.totalResistance.toLocaleString()}</span> | |
| <span className="text-blue-300">Fn:</span> | |
| <span className="text-purple-300">{resistance.froudeNumber}</span> | |
| <span className="text-blue-300">Re:</span> | |
| <span className="text-purple-300">{resistance.reynoldsNumber}</span> | |
| </div> | |
| </div> | |
| )} | |
| {/* Design note */} | |
| <p className="text-[9px] text-blue-500 italic leading-tight">{designNote}</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |