world-model-planner / index.html
Vikingdude81's picture
Upload folder using huggingface_hub
df07e22 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Robust World Model Planner</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.animate-fade-in { animation: fadeIn 0.5s ease-out; }
</style>
</head>
<body class="bg-slate-950">
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef, useCallback, useMemo } = React;
// Inline SVG Icons
const Play = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>;
const Pause = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>;
const RefreshCw = () => <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>;
const BarChart2 = () => <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg>;
const MapIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line></svg>;
const Zap = () => <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>;
const Layers = () => <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg>;
const AlertTriangle = () => <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>;
const Sliders = () => <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="4" y1="21" x2="4" y2="14"></line><line x1="4" y1="10" x2="4" y2="3"></line><line x1="12" y1="21" x2="12" y2="12"></line><line x1="12" y1="8" x2="12" y2="3"></line><line x1="20" y1="21" x2="20" y2="16"></line><line x1="20" y1="12" x2="20" y2="3"></line></svg>;
const Database = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>;
const Terminal = () => <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></svg>;
const Grid = () => <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>;
const ZapSmall = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>;
// Simple SVG Line Chart Component
const SimpleLineChart = ({ data, width = 500, height = 250 }) => {
const padding = { top: 30, right: 20, bottom: 30, left: 50 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
const maxY = Math.max(...data.flatMap(d => [d.DINO, d.Online, d.Adversarial]));
const minY = Math.min(...data.flatMap(d => [d.DINO, d.Online, d.Adversarial]));
const getX = (i) => padding.left + (i / (data.length - 1)) * chartWidth;
const getY = (val) => padding.top + chartHeight - ((val - minY) / (maxY - minY)) * chartHeight;
const makePath = (key) => {
return data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i)} ${getY(d[key])}`).join(' ');
};
return (
<svg width={width} height={height} className="overflow-visible">
{/* Grid lines */}
{[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
<line key={i} x1={padding.left} x2={width - padding.right}
y1={padding.top + t * chartHeight} y2={padding.top + t * chartHeight}
stroke="#1e293b" strokeDasharray="3,3" />
))}
{/* Lines */}
<path d={makePath('DINO')} fill="none" stroke="#d946ef" strokeWidth="2" style={{filter: 'drop-shadow(0 0 4px #d946ef)'}} />
<path d={makePath('Online')} fill="none" stroke="#3b82f6" strokeWidth="2" style={{filter: 'drop-shadow(0 0 4px #3b82f6)'}} />
<path d={makePath('Adversarial')} fill="none" stroke="#22d3ee" strokeWidth="3" style={{filter: 'drop-shadow(0 0 6px #22d3ee)'}} />
{/* Legend */}
<g transform={`translate(${padding.left}, 10)`}>
<circle cx="0" cy="0" r="4" fill="#d946ef" />
<text x="10" y="4" fill="#94a3b8" fontSize="10" fontFamily="monospace">DINO</text>
<circle cx="70" cy="0" r="4" fill="#3b82f6" />
<text x="80" y="4" fill="#94a3b8" fontSize="10" fontFamily="monospace">Online</text>
<circle cx="150" cy="0" r="4" fill="#22d3ee" />
<text x="160" y="4" fill="#94a3b8" fontSize="10" fontFamily="monospace">Adversarial</text>
</g>
{/* Y axis label */}
<text x="15" y={padding.top + chartHeight/2} fill="#64748b" fontSize="10" fontFamily="monospace"
transform={`rotate(-90, 15, ${padding.top + chartHeight/2})`} textAnchor="middle">Loss</text>
</svg>
);
};
// Simple SVG Bar Chart Component
const SimpleBarChart = ({ data, width = 500, height = 250 }) => {
const padding = { top: 40, right: 20, bottom: 40, left: 50 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
const maxY = Math.max(...data.flatMap(d => [d.GD, d.Online_GD, d.Adversarial_GD]));
const barGroupWidth = chartWidth / data.length;
const barWidth = barGroupWidth / 4;
const gap = barWidth * 0.3;
const getY = (val) => chartHeight - (val / maxY) * chartHeight;
const getHeight = (val) => (val / maxY) * chartHeight;
return (
<svg width={width} height={height} className="overflow-visible">
{/* Grid lines */}
{[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
<g key={i}>
<line x1={padding.left} x2={width - padding.right}
y1={padding.top + t * chartHeight} y2={padding.top + t * chartHeight}
stroke="#1e293b" strokeDasharray="3,3" />
<text x={padding.left - 10} y={padding.top + t * chartHeight + 4}
fill="#64748b" fontSize="10" fontFamily="monospace" textAnchor="end">
{Math.round(maxY * (1 - t))}%
</text>
</g>
))}
{/* Bars */}
{data.map((d, i) => {
const groupX = padding.left + i * barGroupWidth + barGroupWidth / 2;
return (
<g key={i}>
<rect x={groupX - barWidth * 1.5 - gap} y={padding.top + getY(d.GD)}
width={barWidth} height={getHeight(d.GD)} fill="#d946ef" rx="2"
style={{filter: 'drop-shadow(0 0 4px #d946ef)'}} />
<rect x={groupX - barWidth * 0.5} y={padding.top + getY(d.Online_GD)}
width={barWidth} height={getHeight(d.Online_GD)} fill="#3b82f6" rx="2"
style={{filter: 'drop-shadow(0 0 4px #3b82f6)'}} />
<rect x={groupX + barWidth * 0.5 + gap} y={padding.top + getY(d.Adversarial_GD)}
width={barWidth} height={getHeight(d.Adversarial_GD)} fill="#22d3ee" rx="2"
style={{filter: 'drop-shadow(0 0 6px #22d3ee)'}} />
<text x={groupX} y={height - 15} fill="#94a3b8" fontSize="10"
fontFamily="monospace" textAnchor="middle">{d.name}</text>
</g>
);
})}
{/* Legend */}
<g transform={`translate(${padding.left}, 15)`}>
<rect x="0" y="-6" width="12" height="12" fill="#d946ef" rx="2" />
<text x="18" y="4" fill="#94a3b8" fontSize="10" fontFamily="monospace">DINO</text>
<rect x="70" y="-6" width="12" height="12" fill="#3b82f6" rx="2" />
<text x="88" y="4" fill="#94a3b8" fontSize="10" fontFamily="monospace">Online</text>
<rect x="150" y="-6" width="12" height="12" fill="#22d3ee" rx="2" />
<text x="168" y="4" fill="#94a3b8" fontSize="10" fontFamily="monospace">Adversarial</text>
</g>
</svg>
);
};
const WorldModelDashboard = () => {
const [activeTab, setActiveTab] = useState('simulation');
const [isRunning, setIsRunning] = useState(false);
const [modelType, setModelType] = useState('adversarial');
const [plannerType, setPlannerType] = useState('gd');
const [mapType, setMapType] = useState('maze');
const [status, setStatus] = useState('Ready');
const [showVectors, setShowVectors] = useState(false);
const [params, setParams] = useState({
steerStrength: 3.0,
lookahead: 70,
noise: 0
});
const canvasRef = useRef(null);
const [agentPos, setAgentPos] = useState({ x: 50, y: 50 });
const [goalPos, setGoalPos] = useState({ x: 350, y: 250 });
const [trajectory, setTrajectory] = useState([]);
const [obstacles, setObstacles] = useState([]);
useEffect(() => {
if (plannerType === 'cem') {
setParams(p => ({ ...p, steerStrength: 2.0, noise: 2, lookahead: 60 }));
} else {
if (modelType === 'adversarial') {
setParams(p => ({ ...p, steerStrength: 3.5, noise: 0, lookahead: 80 }));
} else if (modelType === 'online') {
setParams(p => ({ ...p, steerStrength: 2.5, noise: 1.0, lookahead: 70 }));
} else {
setParams(p => ({ ...p, steerStrength: 0.5, noise: 5, lookahead: 50 }));
}
}
}, [modelType, plannerType]);
const performanceData = [
{ name: 'PushT', GD: 56, CEM: 54, Online_GD: 34, Adversarial_GD: 56 },
{ name: 'PointMaze', GD: 12, CEM: 24, Online_GD: 20, Adversarial_GD: 32 },
{ name: 'Wall', GD: 2, CEM: 10, Online_GD: 16, Adversarial_GD: 32 },
];
const lossLandscapeData = useMemo(() => Array.from({ length: 50 }, (_, i) => {
const x = i / 5;
const dinoLoss = Math.sin(x * 3) * 0.5 + 1.5 + (x - 5) ** 2 * 0.1;
const onlineLoss = (x - 5) ** 2 * 0.1 + 0.5 + 1;
const advLoss = (x - 5) ** 2 * 0.08 + 0.5;
return { x, DINO: dinoLoss, Online: onlineLoss, Adversarial: advLoss };
}), []);
const resetSim = useCallback(() => {
setAgentPos({ x: 50, y: 50 });
setTrajectory([]);
setIsRunning(false);
setStatus('Ready');
}, []);
useEffect(() => {
let newObstacles = [];
if (mapType === 'simple') {
newObstacles = [{ x: 200, y: 100, w: 20, h: 100 }];
} else if (mapType === 'wall') {
newObstacles = [{ x: 180, y: 50, w: 40, h: 200 }];
} else if (mapType === 'maze') {
newObstacles = [
{ x: 120, y: 0, w: 20, h: 220 },
{ x: 240, y: 80, w: 20, h: 220 },
{ x: 320, y: 0, w: 20, h: 100 }
];
}
setObstacles(newObstacles);
resetSim();
}, [mapType, resetSim]);
useEffect(() => {
if (!isRunning) return;
const interval = setInterval(() => {
setAgentPos((prev) => {
const dx = goalPos.x - prev.x;
const dy = goalPos.y - prev.y;
const distToGoal = Math.sqrt(dx * dx + dy * dy);
if (distToGoal < 10) {
setIsRunning(false);
setStatus('Success');
return prev;
}
let vx = dx / (distToGoal || 1);
let vy = dy / (distToGoal || 1);
const avoidanceRadius = params.lookahead;
obstacles.forEach(obs => {
const closestX = Math.max(obs.x, Math.min(prev.x, obs.x + obs.w));
const closestY = Math.max(obs.y, Math.min(prev.y, obs.y + obs.h));
const distX = prev.x - closestX;
const distY = prev.y - closestY;
const dist = Math.sqrt(distX * distX + distY * distY);
if (dist < avoidanceRadius) {
let repX = distX / (dist || 1);
let repY = distY / (dist || 1);
const repulsionStrength = (avoidanceRadius - dist) / avoidanceRadius;
const steerFactor = params.steerStrength;
vx += repX * repulsionStrength * steerFactor;
vy += repY * repulsionStrength * steerFactor;
}
});
const margin = 20;
if (prev.x < margin) vx += (margin - prev.x) * 0.5;
if (prev.x > 400 - margin) vx -= (prev.x - (400 - margin)) * 0.5;
if (prev.y < margin) vy += (margin - prev.y) * 0.5;
if (prev.y > 300 - margin) vy -= (prev.y - (300 - margin)) * 0.5;
const speed = 5;
const finalMag = Math.sqrt(vx * vx + vy * vy);
vx = (vx / finalMag) * speed;
vy = (vy / finalMag) * speed;
if (params.noise > 0) {
vx += (Math.random() - 0.5) * params.noise;
vy += (Math.random() - 0.5) * params.noise;
}
const nextX = Math.max(5, Math.min(395, prev.x + vx));
const nextY = Math.max(5, Math.min(295, prev.y + vy));
const hitObstacle = obstacles.some(obs =>
nextX > obs.x - 5 && nextX < obs.x + obs.w + 5 &&
nextY > obs.y - 5 && nextY < obs.y + obs.h + 5
);
if (hitObstacle) {
setIsRunning(false);
setStatus('Crashed');
return prev;
}
setTrajectory(t => [...t, { x: nextX, y: nextY }]);
return { x: nextX, y: nextY };
});
}, 40);
return () => clearInterval(interval);
}, [isRunning, goalPos, obstacles, params]);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, 400, 300);
ctx.fillStyle = '#0f172a';
ctx.fillRect(0, 0, 400, 300);
ctx.strokeStyle = '#1e293b';
ctx.lineWidth = 1;
for(let i=0; i<400; i+=40) { ctx.beginPath(); ctx.moveTo(i,0); ctx.lineTo(i,300); ctx.stroke(); }
for(let i=0; i<300; i+=40) { ctx.beginPath(); ctx.moveTo(0,i); ctx.lineTo(400,i); ctx.stroke(); }
if (showVectors) {
const gridSize = 25;
const arrowLen = 10;
for(let x = 10; x < 400; x += gridSize) {
for(let y = 10; y < 300; y += gridSize) {
const dx = goalPos.x - x;
const dy = goalPos.y - y;
const distToGoal = Math.sqrt(dx * dx + dy * dy);
let vx = dx / (distToGoal || 1);
let vy = dy / (distToGoal || 1);
let inObstacle = false;
obstacles.forEach(obs => {
if (x > obs.x && x < obs.x+obs.w && y > obs.y && y < obs.y+obs.h) inObstacle = true;
const closestX = Math.max(obs.x, Math.min(x, obs.x + obs.w));
const closestY = Math.max(obs.y, Math.min(y, obs.y + obs.h));
const distX = x - closestX;
const distY = y - closestY;
const dist = Math.sqrt(distX * distX + distY * distY);
if (dist < params.lookahead) {
let repX = distX / (dist || 1);
let repY = distY / (dist || 1);
const repulsionStrength = (params.lookahead - dist) / params.lookahead;
vx += repX * repulsionStrength * params.steerStrength;
vy += repY * repulsionStrength * params.steerStrength;
}
});
if (!inObstacle) {
if (params.noise > 0) {
vx += (Math.random() - 0.5) * params.noise * 0.8;
vy += (Math.random() - 0.5) * params.noise * 0.8;
}
const mag = Math.sqrt(vx*vx + vy*vy);
vx = (vx/mag) * arrowLen;
vy = (vy/mag) * arrowLen;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + vx, y + vy);
let color = 'rgba(217, 70, 239, 0.3)';
if (modelType === 'adversarial') color = 'rgba(34, 211, 238, 0.4)';
if (modelType === 'online') color = 'rgba(59, 130, 246, 0.4)';
ctx.strokeStyle = color;
ctx.lineWidth = 1;
ctx.stroke();
ctx.beginPath();
const headLen = 3;
const angle = Math.atan2(vy, vx);
ctx.moveTo(x + vx, y + vy);
ctx.lineTo(x + vx - headLen * Math.cos(angle - Math.PI / 6), y + vy - headLen * Math.sin(angle - Math.PI / 6));
ctx.lineTo(x + vx - headLen * Math.cos(angle + Math.PI / 6), y + vy - headLen * Math.sin(angle + Math.PI / 6));
ctx.fillStyle = color;
ctx.fill();
}
}
}
}
if (isRunning || status === 'Ready') {
ctx.beginPath();
ctx.arc(agentPos.x, agentPos.y, params.lookahead, 0, Math.PI * 2);
let color = 'rgba(217, 70, 239, 0.1)';
if (modelType === 'adversarial') color = 'rgba(34, 211, 238, 0.1)';
if (modelType === 'online') color = 'rgba(59, 130, 246, 0.1)';
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = color.replace('0.1', '0.3');
ctx.lineWidth = 1;
ctx.setLineDash([5, 5]);
ctx.stroke();
ctx.setLineDash([]);
}
obstacles.forEach(obs => {
ctx.fillStyle = '#1e293b';
ctx.fillRect(obs.x, obs.y, obs.w, obs.h);
ctx.shadowBlur = 10;
ctx.shadowColor = '#94a3b8';
ctx.strokeStyle = '#64748b';
ctx.lineWidth = 2;
ctx.strokeRect(obs.x, obs.y, obs.w, obs.h);
ctx.shadowBlur = 0;
});
ctx.beginPath();
let trajColor = '#d946ef';
if (modelType === 'adversarial') trajColor = '#22d3ee';
if (modelType === 'online') trajColor = '#3b82f6';
ctx.strokeStyle = trajColor;
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.shadowBlur = 10;
ctx.shadowColor = trajColor;
trajectory.forEach((p, i) => {
if (i === 0) ctx.moveTo(p.x, p.y);
else ctx.lineTo(p.x, p.y);
});
ctx.stroke();
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.arc(goalPos.x, goalPos.y, 8, 0, Math.PI * 2);
ctx.fillStyle = '#f472b6';
ctx.shadowBlur = 15;
ctx.shadowColor = '#f472b6';
ctx.fill();
ctx.shadowBlur = 0;
ctx.fillStyle = '#000';
ctx.font = 'bold 10px monospace';
ctx.fillText("G", goalPos.x - 4, goalPos.y + 3);
ctx.beginPath();
ctx.arc(agentPos.x, agentPos.y, 6, 0, Math.PI * 2);
ctx.fillStyle = '#fff';
ctx.shadowBlur = 10;
ctx.shadowColor = '#fff';
ctx.fill();
ctx.shadowBlur = 0;
}, [agentPos, goalPos, obstacles, trajectory, modelType, params.lookahead, isRunning, status, showVectors, params.steerStrength, params.noise]);
return (
<div className="flex flex-col h-screen bg-slate-950 text-cyan-50 font-mono">
{/* Header */}
<header className="bg-slate-900/80 backdrop-blur-md border-b border-slate-800 p-4 flex items-center justify-between z-10">
<div className="flex items-center space-x-3">
<div className="bg-cyan-500/10 border border-cyan-500/50 text-cyan-400 p-2 rounded shadow-[0_0_15px_rgba(6,182,212,0.3)]">
<Zap />
</div>
<div>
<h1 className="text-xl font-bold tracking-tight text-cyan-100 uppercase">Robust_World_Planner_v2.0</h1>
<p className="text-xs text-slate-400 font-medium">System: Online // Paper: "Closing the Train-Test Gap"</p>
</div>
</div>
<div className="flex items-center space-x-4">
{status === 'Crashed' && <span className="text-red-400 font-bold flex items-center bg-red-900/20 px-3 py-1 rounded border border-red-500/30"><AlertTriangle /><span className="ml-2">SYSTEM_FAILURE</span></span>}
{status === 'Success' && <span className="text-emerald-400 font-bold flex items-center bg-emerald-900/20 px-3 py-1 rounded border border-emerald-500/30"><ZapSmall /><span className="ml-2">TARGET_ACQUIRED</span></span>}
<a href="https://github.com/qw3rtman/robust-world-model-planning" target="_blank" rel="noreferrer" className="text-xs text-slate-500 hover:text-cyan-400 transition-colors uppercase tracking-widest">
Source_Code
</a>
</div>
</header>
{/* Main Content */}
<div className="flex flex-1 overflow-hidden">
{/* Sidebar Controls */}
<aside className="w-80 bg-slate-900 border-r border-slate-800 p-5 flex flex-col overflow-y-auto">
<div className="mb-6">
<h3 className="text-xs font-bold text-cyan-700 mb-3 uppercase tracking-widest flex items-center">
<Terminal /> <span className="ml-2">System_Config</span>
</h3>
<div className="space-y-4">
<div>
<label className="text-xs font-semibold text-slate-400 mb-2 block uppercase">Core Model</label>
<div className="grid grid-cols-1 gap-2">
<button
onClick={() => { setModelType('dino'); resetSim(); }}
className={`p-3 text-sm font-medium rounded border flex items-center transition-all ${modelType === 'dino' ? 'bg-fuchsia-900/20 border-fuchsia-500 text-fuchsia-400 shadow-[0_0_10px_rgba(217,70,239,0.2)]' : 'bg-slate-800 border-slate-700 text-slate-500 hover:bg-slate-800/80'}`}
>
<AlertTriangle /> <span className="ml-3">STANDARD_DINO</span>
</button>
<button
onClick={() => { setModelType('online'); resetSim(); }}
className={`p-3 text-sm font-medium rounded border flex items-center transition-all ${modelType === 'online' ? 'bg-blue-900/20 border-blue-500 text-blue-400 shadow-[0_0_10px_rgba(59,130,246,0.2)]' : 'bg-slate-800 border-slate-700 text-slate-500 hover:bg-slate-800/80'}`}
>
<Database /> <span className="ml-3">ONLINE_WM</span>
</button>
<button
onClick={() => { setModelType('adversarial'); resetSim(); }}
className={`p-3 text-sm font-medium rounded border flex items-center transition-all ${modelType === 'adversarial' ? 'bg-cyan-900/20 border-cyan-500 text-cyan-400 shadow-[0_0_10px_rgba(34,211,238,0.2)]' : 'bg-slate-800 border-slate-700 text-slate-500 hover:bg-slate-800/80'}`}
>
<ZapSmall /> <span className="ml-3">ADVERSARIAL_WM</span>
</button>
</div>
</div>
<div>
<label className="text-xs font-semibold text-slate-400 mb-2 block uppercase">Environment</label>
<select
className="w-full p-2.5 bg-slate-800 border border-slate-700 rounded text-sm text-cyan-50 focus:border-cyan-500 outline-none transition-all uppercase"
value={mapType}
onChange={(e) => setMapType(e.target.value)}
>
<option value="simple">Sector_A (Simple)</option>
<option value="wall">Sector_B (Wall)</option>
<option value="maze">Sector_C (Complex)</option>
</select>
</div>
<div>
<label className="text-xs font-semibold text-slate-400 mb-2 block uppercase">Planner Logic</label>
<select
className="w-full p-2.5 bg-slate-800 border border-slate-700 rounded text-sm text-cyan-50 focus:border-cyan-500 outline-none transition-all uppercase"
value={plannerType}
onChange={(e) => { setPlannerType(e.target.value); resetSim(); }}
>
<option value="gd">Gradient_Descent (Fast)</option>
<option value="cem">Cross_Entropy (Slow)</option>
</select>
</div>
</div>
</div>
<div className="mb-6 bg-slate-900/50 p-4 rounded border border-slate-800 relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-cyan-500/50"></div>
<h3 className="text-xs font-bold text-slate-500 mb-3 uppercase tracking-widest flex items-center">
<Sliders /> <span className="ml-2">Telemetry</span>
</h3>
<div className="space-y-4">
<div>
<div className="flex justify-between mb-1">
<label className="text-xs font-semibold text-slate-400">STEERING_GAIN</label>
<span className="text-xs font-mono text-cyan-400">{params.steerStrength.toFixed(1)}</span>
</div>
<input
type="range" min="0" max="6" step="0.1"
value={params.steerStrength}
onChange={(e) => setParams({...params, steerStrength: parseFloat(e.target.value)})}
className="w-full h-1 bg-slate-700 rounded appearance-none cursor-pointer accent-cyan-500"
/>
</div>
<div>
<div className="flex justify-between mb-1">
<label className="text-xs font-semibold text-slate-400">SCAN_HORIZON</label>
<span className="text-xs font-mono text-cyan-400">{params.lookahead}PX</span>
</div>
<input
type="range" min="10" max="150" step="5"
value={params.lookahead}
onChange={(e) => setParams({...params, lookahead: parseInt(e.target.value)})}
className="w-full h-1 bg-slate-700 rounded appearance-none cursor-pointer accent-cyan-500"
/>
</div>
</div>
</div>
<div className="mb-6">
<div className="flex space-x-2">
<button
onClick={() => { setIsRunning(!isRunning); if(status !== 'Planning' && !isRunning) setStatus('Planning'); }}
disabled={status === 'Crashed' || status === 'Success'}
className={`flex-1 flex items-center justify-center p-3 rounded text-slate-900 font-bold uppercase tracking-wide transition-all ${
status === 'Crashed' || status === 'Success' ? 'bg-slate-700 cursor-not-allowed opacity-50' :
isRunning ? 'bg-fuchsia-500 hover:bg-fuchsia-400 shadow-[0_0_15px_rgba(217,70,239,0.5)]' : 'bg-cyan-500 hover:bg-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.5)]'}`}
>
{isRunning ? <><Pause /> <span className="ml-2">Halt</span></> : <><Play /> <span className="ml-2">Execute</span></>}
</button>
<button
onClick={resetSim}
className="p-3 border border-slate-700 rounded text-slate-400 hover:bg-slate-800 hover:text-cyan-400 transition-colors"
>
<RefreshCw />
</button>
</div>
</div>
</aside>
{/* Visualization Area */}
<main className="flex-1 flex flex-col bg-slate-950 overflow-y-auto">
{/* Tabs */}
<div className="bg-slate-900 border-b border-slate-800 px-8 pt-4 flex space-x-8 sticky top-0 z-10">
<button
onClick={() => setActiveTab('simulation')}
className={`pb-4 text-xs font-bold border-b-2 transition-colors uppercase tracking-widest ${activeTab === 'simulation' ? 'border-cyan-500 text-cyan-400' : 'border-transparent text-slate-500 hover:text-slate-300'}`}
>
<span className="flex items-center"><MapIcon /> <span className="ml-2">Live_Feed</span></span>
</button>
<button
onClick={() => setActiveTab('landscape')}
className={`pb-4 text-xs font-bold border-b-2 transition-colors uppercase tracking-widest ${activeTab === 'landscape' ? 'border-cyan-500 text-cyan-400' : 'border-transparent text-slate-500 hover:text-slate-300'}`}
>
<span className="flex items-center"><Layers /> <span className="ml-2">Data_Surface</span></span>
</button>
<button
onClick={() => setActiveTab('benchmarks')}
className={`pb-4 text-xs font-bold border-b-2 transition-colors uppercase tracking-widest ${activeTab === 'benchmarks' ? 'border-cyan-500 text-cyan-400' : 'border-transparent text-slate-500 hover:text-slate-300'}`}
>
<span className="flex items-center"><BarChart2 /> <span className="ml-2">Analytics</span></span>
</button>
</div>
<div className="p-8 max-w-5xl mx-auto w-full">
{activeTab === 'simulation' && (
<div className="flex flex-col items-center animate-fade-in">
<div className="bg-slate-900 p-1 rounded-lg border border-slate-800 shadow-[0_0_30px_rgba(0,0,0,0.5)] mb-6 relative group">
<div className="absolute inset-0 bg-cyan-500/5 rounded-lg pointer-events-none"></div>
<canvas
ref={canvasRef}
width={400}
height={300}
className="rounded cursor-crosshair block"
onClick={(e) => {
const rect = canvasRef.current.getBoundingClientRect();
setGoalPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });
resetSim();
}}
/>
<div className="absolute top-4 left-4 flex flex-col space-y-2">
<button
onClick={() => setShowVectors(!showVectors)}
className={`px-3 py-1.5 rounded border text-[10px] font-bold uppercase tracking-wider backdrop-blur-md transition-all ${showVectors ? 'bg-cyan-500/20 border-cyan-500 text-cyan-400' : 'bg-slate-900/80 border-slate-700 text-slate-400 hover:bg-slate-800'}`}
>
<span className="flex items-center"><Grid /> <span className="ml-2">Matrix_View {showVectors ? '[ON]' : '[OFF]'}</span></span>
</button>
</div>
<div className="absolute top-4 right-4 bg-slate-950/80 backdrop-blur px-3 py-1.5 rounded border border-slate-700 text-[10px] font-medium text-cyan-400 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none uppercase">
Target_Designation_Mode
</div>
</div>
<div className="flex space-x-6 text-xs font-medium text-slate-400 bg-slate-900 px-6 py-3 rounded border border-slate-800 uppercase tracking-wide">
<div className="flex items-center"><span className="w-2 h-2 bg-white rounded-full mr-2 shadow-[0_0_8px_white]"></span> Agent</div>
<div className="flex items-center"><span className="w-2 h-2 bg-pink-500 rounded-full mr-2 shadow-[0_0_8px_magenta]"></span> Target</div>
<div className="flex items-center">
<span className={`w-2 h-2 rounded-full mr-2 shadow-[0_0_8px_currentColor] ${modelType === 'adversarial' ? 'text-cyan-400 bg-cyan-400' : modelType === 'online' ? 'text-blue-400 bg-blue-400' : 'text-fuchsia-400 bg-fuchsia-400'}`}></span> Path
</div>
<div className="flex items-center"><span className="w-2 h-2 border border-slate-500 border-dashed rounded-full mr-2"></span> Scanner</div>
</div>
</div>
)}
{activeTab === 'landscape' && (
<div className="bg-slate-900 p-6 rounded border border-slate-800 animate-fade-in">
<div className="mb-6">
<h3 className="text-lg font-bold text-cyan-100 uppercase tracking-wide">Loss Surface Topology</h3>
<p className="text-xs text-slate-500 mt-1">
Adversarial training (Cyan) smooths the optimization landscape. Standard DINO (Fuchsia) is volatile and non-convex.
</p>
</div>
<div className="flex justify-center">
<SimpleLineChart data={lossLandscapeData} width={550} height={280} />
</div>
</div>
)}
{activeTab === 'benchmarks' && (
<div className="bg-slate-900 p-6 rounded border border-slate-800 animate-fade-in">
<div className="mb-6">
<h3 className="text-lg font-bold text-cyan-100 uppercase tracking-wide">Performance Metrics</h3>
<p className="text-xs text-slate-500 mt-1">
Success rates (%) across control environments. Adversarial optimization provides superior stability.
</p>
</div>
<div className="flex justify-center">
<SimpleBarChart data={performanceData} width={550} height={280} />
</div>
</div>
)}
</div>
</main>
</div>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(<WorldModelDashboard />);
</script>
</body>
</html>