Spaces:
Running
Running
8bitkick
Update install button text and enhance installation process with start app functionality
67364a3
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>Reachy Mini 3D Visualization</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <style> | |
| body { | |
| margin: 0; | |
| font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; | |
| overflow: hidden; | |
| } | |
| #container { | |
| width: 100vw; | |
| height: 100vh; | |
| } | |
| #status { | |
| background: rgba(0, 0, 0, 0.7); | |
| padding: 15px; | |
| border-radius: 5px; | |
| font-size: 16px; | |
| color: white; | |
| width: 270px; | |
| box-sizing: border-box; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-family: 'VT323', monospace; | |
| } | |
| .connection-led { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| display: inline-block; | |
| } | |
| .ok .connection-led { background-color: #00ff00; box-shadow: 0 0 8px #00ff00; } | |
| .err .connection-led { background-color: #ff0000; box-shadow: 0 0 8px #ff0000; } | |
| #data-panel { | |
| position: absolute; | |
| top: 10px; | |
| left: 10px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| z-index: 100; | |
| } | |
| #joints { | |
| background: rgba(0, 0, 0, 0.7); | |
| padding: 15px; | |
| border-radius: 5px; | |
| font-size: 16px; | |
| width: 270px; | |
| height: 200px; | |
| display: flex; | |
| flex-direction: column; | |
| font-family: 'VT323', monospace; | |
| box-sizing: border-box; | |
| } | |
| #joints h3 { | |
| margin: 0 0 10px 0; | |
| font-size: 11px; | |
| font-family: 'VT323', monospace; | |
| color: #ccc; | |
| font-weight: normal; | |
| } | |
| .joint-item { | |
| margin: 3px 0; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| gap: 15px; | |
| } | |
| .joint-name { | |
| color: #999; | |
| flex: 0 0 140px; | |
| text-align: left; | |
| white-space: nowrap; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .joint-color-dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| display: inline-block; | |
| } | |
| .joint-value { | |
| font-weight: bold; | |
| color: #ccc; | |
| flex: 0 0 80px; | |
| text-align: right; | |
| } | |
| #chart-container { | |
| background: rgba(0, 0, 0, 0.7); | |
| padding: 15px; | |
| border-radius: 5px; | |
| width: 270px; | |
| height: 200px; | |
| box-sizing: border-box; | |
| } | |
| #chart { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| /* Install panel (reuses joint panel aesthetics) */ | |
| #install-panel { | |
| background: rgba(0,0,0,0.7); | |
| padding: 15px; | |
| border-radius: 5px; | |
| font-size: 16px; | |
| width: 270px; | |
| box-sizing: border-box; | |
| font-family: 'VT323', monospace; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| #install-panel h3 { | |
| margin: 0 0 6px 0; | |
| font-size: 12px; | |
| font-weight: normal; | |
| color: #ccc; | |
| letter-spacing: 1px; | |
| } | |
| #dashboardUrl { | |
| width: 100%; | |
| background: #111; | |
| border: 1px solid #333; | |
| color: #ddd; | |
| padding: 6px 8px; | |
| border-radius: 4px; | |
| font-family: 'VT323', monospace; | |
| font-size: 14px; | |
| box-sizing: border-box; | |
| } | |
| #dashboardUrl:focus { outline: 1px solid #555; } | |
| .install-btn { | |
| background: linear-gradient(90deg,#4b136f,#8e2a6a); | |
| border: none; | |
| color: #fff; | |
| padding: 8px 10px; | |
| font-family: 'VT323', monospace; | |
| font-size: 16px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| justify-content: center; | |
| transition: background 0.2s; | |
| } | |
| .install-btn:hover { background: linear-gradient(90deg,#8e2a6a,#4b136f); } | |
| .install-btn:disabled { opacity: 0.5; cursor: not-allowed; } | |
| .install-status { | |
| font-size: 13px; | |
| min-height: 18px; | |
| line-height: 1.3; | |
| padding: 4px 6px; | |
| border-radius: 4px; | |
| background: #111; | |
| border: 1px solid #222; | |
| display: none; | |
| white-space: pre-line; | |
| } | |
| .install-status.info { color: #ccc; } | |
| .install-status.success { color: #00ff9d; } | |
| .install-status.error { color: #ff5f5f; } | |
| </style> | |
| <script> | |
| // Global debug flag (default false). Enable by appending ?debug=1 to URL. | |
| (function(){ | |
| const params = new URLSearchParams(location.search); | |
| window.REACHY_DEBUG = params.get('debug') === '1'; | |
| if(!window.REACHY_DEBUG){ | |
| ['log','info','warn','debug'].forEach(level => { | |
| const orig = console[level]; | |
| console[level] = function(){ /* suppressed */ }; | |
| }); | |
| } | |
| })(); | |
| </script> | |
| </head> | |
| <body> | |
| <div id="data-panel"> | |
| <div id="status">Loading...</div> | |
| <div id="joints"> | |
| <div id="joints-list">Waiting for data...</div> | |
| </div> | |
| <div id="chart-container"> | |
| <canvas id="chart"></canvas> | |
| </div> | |
| <!-- New install panel --> | |
| <div id="install-panel"> | |
| <h3>Install to Reachy Mini</h3> | |
| <input type="url" id="dashboardUrl" placeholder="http://reachy-dashboard:8000" value="http://localhost:8000" /> | |
| <button id="installBtn" class="install-btn"><span>📥</span><span>Install On Robot</span></button> | |
| <div id="installStatus" class="install-status"></div> | |
| </div> | |
| </div> | |
| <div id="container"></div> | |
| <!-- Chart.js --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> | |
| <!-- Three.js and URDFLoader --> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "three": "https://cdn.jsdelivr.net/npm/three@0.169.0/build/three.module.js", | |
| "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.169.0/examples/jsm/" | |
| } | |
| } | |
| </script> | |
| <script type="module"> | |
| import { App } from './reachy_mini_app_example/src/App.js'; | |
| // Initialize the application | |
| new App(); | |
| </script> | |
| <script> | |
| (function(){ | |
| const dashInput = document.getElementById('dashboardUrl'); | |
| const btn = document.getElementById('installBtn'); | |
| const statusEl = document.getElementById('installStatus'); | |
| function showStatus(type,msg){ | |
| statusEl.className='install-status '+type; | |
| statusEl.textContent=msg; | |
| statusEl.style.display='block'; | |
| } | |
| async function install(){ | |
| const dashURL = dashInput.value.trim(); | |
| if(!dashURL){ showStatus('error','Enter dashboard URL'); return; } | |
| btn.disabled=true; btn.innerHTML='⏳ Installing...'; showStatus('info','Sending install request...'); | |
| try { | |
| // Step 1: Install the app | |
| const payload = { | |
| name: '8bitkick/reachy_mini_3d_web_viz', | |
| source_kind: 'hf_space', | |
| description: '', | |
| url: 'https://huggingface.co/spaces/8bitkick/reachy_mini_3d_web_viz', | |
| extra: { additionalProp1: {} } | |
| }; | |
| const installResp = await fetch(dashURL+'/api/apps/install',{ | |
| method:'POST', | |
| headers:{'Content-Type':'application/json','accept':'application/json'}, | |
| body: JSON.stringify(payload) | |
| }); | |
| const installResult = await installResp.json().catch(()=>({})); | |
| if(!installResp.ok) { | |
| // If already installed, that's ok, we'll try to start it anyway | |
| if(installResult.detail && installResult.detail.includes('already installed')) { | |
| showStatus('info','App already installed, starting...'); | |
| } else { | |
| throw new Error(installResult.detail||'Install failed'); | |
| } | |
| } else { | |
| showStatus('success','Install complete! Starting app...'); | |
| } | |
| // Step 2: Start the app | |
| btn.innerHTML='⏳ Starting...'; | |
| const startResp = await fetch(dashURL+'/api/apps/start-app/reachy_mini_web_viz',{ | |
| method:'POST', | |
| headers:{'accept':'application/json'}, | |
| body: '' | |
| }); | |
| const startResult = await startResp.json().catch(()=>({})); | |
| if(!startResp.ok) throw new Error(startResult.detail||'Failed to start app'); | |
| if(startResult.state === 'running') { | |
| showStatus('success','App is now running on Reachy Mini!'); | |
| } else { | |
| showStatus('info',`App state: ${startResult.state}`); | |
| } | |
| } catch(err){ | |
| showStatus('error','Failed: '+err.message); | |
| } finally { | |
| btn.disabled=false; btn.innerHTML='<span>📥</span><span>Install & Start</span>'; | |
| } | |
| } | |
| btn.addEventListener('click', install); | |
| if(['localhost','127.0.0.1'].includes(window.location.hostname)){ | |
| dashInput.value='http://localhost:8000'; | |
| } | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |