openx-viewer / templates /index.html
rudraksh
Add OpenX Rerun Viewer: 6 robot types, 18 episodes
9468cdd
Raw
History Blame Contribute Delete
5.19 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OpenX Rerun Viewer</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #0d1117;
color: #c9d1d9;
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
padding: 12px 20px;
background: #161b22;
border-bottom: 1px solid #30363d;
display: flex;
align-items: center;
gap: 16px;
flex-shrink: 0;
}
.header h1 {
font-size: 18px;
font-weight: 600;
color: #f0f6fc;
white-space: nowrap;
}
.controls {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.controls select, .controls input, .controls button {
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #30363d;
background: #21262d;
color: #c9d1d9;
font-size: 13px;
}
.controls select:focus, .controls button:focus {
outline: none;
border-color: #58a6ff;
}
.controls button {
cursor: pointer;
background: #238636;
border-color: #2ea043;
font-weight: 600;
}
.controls button:hover { background: #2ea043; }
.meta {
font-size: 12px;
color: #8b949e;
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.meta span { white-space: nowrap; }
.viewer-container {
flex: 1;
overflow: hidden;
position: relative;
}
.viewer-container iframe {
width: 100%;
height: 100%;
border: none;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #484f58;
font-size: 16px;
}
.placeholder p { text-align: center; line-height: 1.6; }
</style>
</head>
<body>
<div class="header">
<h1>OpenX Rerun Viewer</h1>
<div class="controls">
<select id="dataset-select">
<option value="">-- Select Dataset --</option>
</select>
<select id="episode-select" disabled>
<option value="">-- Select Episode --</option>
</select>
<button id="view-btn" disabled>View in Rerun</button>
</div>
<div class="meta" id="meta-display"></div>
</div>
<div class="viewer-container">
<div class="placeholder" id="placeholder">
<p>Select a dataset and episode above to visualize in Rerun.<br>
<small>6 robot types · 18 episodes · OpenX-Embodiment</small></p>
</div>
<iframe id="rerun-frame" style="display:none"
allow="cross-origin-isolated"
allowfullscreen>
</iframe>
</div>
<script>
const BASE = document.querySelector('meta[content]')?.content || window.location.origin;
let datasets = [];
let currentSlug = null;
async function init() {
try {
const resp = await fetch('/api/datasets');
datasets = await resp.json();
const sel = document.getElementById('dataset-select');
datasets.forEach(ds => {
const opt = document.createElement('option');
opt.value = ds.slug;
opt.textContent = `${ds.robot_type} (${ds.episodes_available.length} eps, ${ds.fps}fps, ${ds.camera_labels.length} cam)`;
sel.appendChild(opt);
});
} catch (e) {
console.error('Failed to load datasets:', e);
}
}
document.getElementById('dataset-select').addEventListener('change', async (e) => {
const slug = e.target.value;
currentSlug = slug;
const epSel = document.getElementById('episode-select');
const viewBtn = document.getElementById('view-btn');
epSel.innerHTML = '<option value="">-- Select Episode --</option>';
epSel.disabled = !slug;
viewBtn.disabled = true;
if (!slug) {
document.getElementById('meta-display').innerHTML = '';
return;
}
const ds = datasets.find(d => d.slug === slug);
if (!ds) return;
document.getElementById('meta-display').innerHTML =
`<span>Robot: <strong>${ds.robot_type}</strong></span>` +
`<span>FPS: ${ds.fps}</span>` +
`<span>Cameras: ${ds.camera_labels.join(', ')}</span>` +
`<span>State dim: ${ds.state_dim}</span>` +
`<span>Action dim: ${ds.action_dim}</span>`;
ds.episodes_available.forEach(ep => {
const opt = document.createElement('option');
opt.value = ep.index;
opt.textContent = `Episode ${ep.index} (${ep.frames} frames)`;
epSel.appendChild(opt);
});
epSel.disabled = false;
});
document.getElementById('episode-select').addEventListener('change', () => {
document.getElementById('view-btn').disabled =
!document.getElementById('episode-select').value;
});
document.getElementById('view-btn').addEventListener('click', () => {
const slug = currentSlug;
const epIdx = document.getElementById('episode-select').value;
if (!slug || epIdx === '') return;
const rrdUrl = `${window.location.origin}/data/${slug}/episode_${String(epIdx).padStart(6, '0')}.rrd`;
const viewerBase = '{{VIEWER_BASE}}';
const viewerUrl = `${viewerBase}/index.html?url=${encodeURIComponent(rrdUrl)}`;
document.getElementById('placeholder').style.display = 'none';
const frame = document.getElementById('rerun-frame');
frame.src = viewerUrl;
frame.style.display = 'block';
});
init();
</script>
</body>
</html>