Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>OpenArm 3D Visualizer - xoq</title> | |
| <script type="module" crossorigin src="./assets/openarm-BF3lx1-l.js"></script> | |
| <link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-B5Qt9EMX.js"> | |
| <link rel="modulepreload" crossorigin href="./assets/openarm-settings-Cing9rdF.js"> | |
| <link rel="modulepreload" crossorigin href="./assets/connect-C3lO3qk6.js"> | |
| <link rel="modulepreload" crossorigin href="./assets/URDFLoader-BpNv9rVq.js"> | |
| <link rel="stylesheet" crossorigin href="./assets/openarm-settings-CsiNG4Tl.css"> | |
| <link rel="stylesheet" crossorigin href="./assets/openarm-B5atNJ-X.css"> | |
| </head> | |
| <body> | |
| <script> | |
| (function() { | |
| var hasMSE = typeof MediaSource !== 'undefined' || typeof ManagedMediaSource !== 'undefined'; | |
| var hasWebCodecs = typeof VideoDecoder !== 'undefined'; | |
| if (!hasMSE) { | |
| document.addEventListener('DOMContentLoaded', function() { | |
| var b = document.createElement('div'); | |
| b.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;background:#b91c1c;color:#fff;padding:8px 16px;text-align:center;font:14px/1.4 system-ui,sans-serif;'; | |
| b.innerHTML = 'This browser does not support <b>MediaSource Extensions</b>. Color video playback requires MSE.'; | |
| document.body.prepend(b); | |
| }); | |
| } else if (!hasWebCodecs) { | |
| document.addEventListener('DOMContentLoaded', function() { | |
| var b = document.createElement('div'); | |
| b.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;background:#d97706;color:#fff;padding:8px 16px;text-align:center;font:14px/1.4 system-ui,sans-serif;'; | |
| b.innerHTML = 'Depth and point cloud features are unavailable (WebCodecs not supported). Color video will work normally.'; | |
| document.body.prepend(b); | |
| }); | |
| } | |
| })(); | |
| </script> | |
| <div class="top-bar"> | |
| <div class="top-bar-main"> | |
| <h1>OpenArm 3D</h1> | |
| <button id="startBtn">Connect</button> | |
| <button id="stopBtn" disabled>Disconnect</button> | |
| <button id="audioBtn" disabled title="Toggle audio playback">🔇 Audio</button> | |
| <button id="recordBtn" disabled title="Record live streams to MP4 files"><span class="rec-dot"></span> Record</button> | |
| <button id="replayBtn" title="Replay a recorded .mp4 file">Replay</button> | |
| <input type="file" id="replayFile" accept=".mp4,.bin,.log" multiple style="display:none" /> | |
| <span id="audioLevel" style="color:#555; font-size:0.65rem; font-family:monospace; letter-spacing:-1px; white-space:nowrap;" title="Audio level (RMS)">--</span> | |
| <span id="statusText" style="color:#888; font-size:0.8rem;">Idle</span> | |
| <span id="viewerCount" style="color:#888; font-size:0.8rem; margin-left:0.25rem;">0 viewers</span> | |
| <span style="font-size:0.8rem; margin-left:0.25rem;" title="Video stream latency and FPS"><span id="streamPing" style="color:#555; display:inline-block; width:5.5em; text-align:right;">--</span> <span id="streamFps" style="color:#555; display:inline-block; width:3.5em; text-align:right;">--</span></span> | |
| <button id="settingsToggle" class="settings-toggle" aria-label="Settings">⚙</button> | |
| </div> | |
| </div> | |
| <div class="replay-bar" id="replayBar"> | |
| <button id="replayPlayPause">Play</button> | |
| <span class="replay-time" id="replayCurrentTime">0:00</span> | |
| <input type="range" id="replayTimeline" min="0" max="1000" value="0" step="1" /> | |
| <span class="replay-time" id="replayDuration">0:00</span> | |
| <select id="replaySpeed"> | |
| <option value="0.25">0.25x</option> | |
| <option value="0.5">0.5x</option> | |
| <option value="1" selected>1x</option> | |
| <option value="2">2x</option> | |
| <option value="4">4x</option> | |
| </select> | |
| <button id="replayClose">X</button> | |
| </div> | |
| <div class="main"> | |
| <div class="canvas-container" id="view3d"> | |
| <canvas id="threeCanvas"></canvas> | |
| <div class="canvas-overlay"> | |
| <span>Drag to orbit / Scroll to zoom / Right-drag to pan</span> | |
| </div> | |
| </div> | |
| <div class="camera-split" id="cameraSplit"></div> | |
| <div class="settings-panel" id="settingsPanel"></div> | |
| <div class="side-panel"> | |
| <div id="armPanelsContainer"></div> | |
| <div class="panel-section" id="panelStats"> | |
| <h3>Stats</h3> | |
| <div class="stats-grid"> | |
| <div class="stat"> | |
| <div class="stat-label">Frames</div> | |
| <div class="stat-value" id="frameCount">0</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Bytes</div> | |
| <div class="stat-value" id="bytesReceived">0 B</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">FPS</div> | |
| <div class="stat-value" id="canFps">0</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Last</div> | |
| <div class="stat-value" id="lastUpdate">-</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="tab-bar" id="tabBar"> | |
| <button class="tab-btn active" data-tab="3d">3D</button> | |
| <button class="tab-btn" data-tab="arms">Arms</button> | |
| <button class="tab-btn" data-tab="camera">Cam</button> | |
| <button class="tab-btn" data-tab="stats">Stats</button> | |
| <button class="tab-btn" data-tab="log">Log</button> | |
| <button class="tab-btn" data-tab="settings">⚙</button> | |
| </div> | |
| <div class="log-panel" id="log"></div> | |
| <div class="chat-bar"> | |
| <input type="text" id="chatUsername" placeholder="Name" /> | |
| <input type="text" id="chatInput" placeholder="@robot pick up the yellow cube and put it in the green box" value="@robot pick up the yellow cube and put it in the green box" /> | |
| </div> | |
| <div class="toast-container" id="toasts"></div> | |
| </body> | |
| </html> | |