Spaces:
Running
Running
| <html> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width" /> | |
| <title>Reachy Mini Coding Lab</title> | |
| <link rel="stylesheet" href="style.css" /> | |
| </head> | |
| <body> | |
| <div class="hero"> | |
| <div class="hero-content"> | |
| <div class="app-icon">📝🤖</div> | |
| <h1>Reachy Mini Coding Lab</h1> | |
| <p class="tagline">Web app for programming Reachy Mini movements</p> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <div class="main-card"> | |
| <div class="app-preview"> | |
| <div class="preview-image"> | |
| <div class="camera-feed">💻</div> | |
| <pre class="code-preview">"Wave hello" | |
| look left | |
| antenna both up | |
| wait 1s | |
| look right</pre> | |
| </div> | |
| </div> | |
| <div class="app-details"> | |
| <h2>About RMScript</h2> | |
| <div class="template-info"> | |
| <div class="info-box"> | |
| <h3>Kid-Friendly Language</h3> | |
| <p>Write scripts using natural language commands like <code>look left</code>, <code>turn right</code>, and <code>antenna up</code>.</p> | |
| </div> | |
| <div class="info-box"> | |
| <h3>Real-Time Execution</h3> | |
| <p>Execute scripts directly on your robot with proper timing and smooth movements.</p> | |
| </div> | |
| </div> | |
| <div class="how-to-use"> | |
| <h3>Features</h3> | |
| <div class="steps"> | |
| <div class="step"> | |
| <div class="step-number">1</div> | |
| <div> | |
| <h4>Syntax Highlighting</h4> | |
| <p>Real-time verification and error detection as you type</p> | |
| </div> | |
| </div> | |
| <div class="step"> | |
| <div class="step-number">2</div> | |
| <div> | |
| <h4>Live Compilation</h4> | |
| <p>Instant compilation to robot commands with visual intermediate representation preview</p> | |
| </div> | |
| </div> | |
| <div class="step"> | |
| <div class="step-number">3</div> | |
| <div> | |
| <h4>Movements & Sounds</h4> | |
| <p>Support for head movements, antennas, waits, (sounds and pictures TBD !) </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="download-section"> | |
| <div class="download-card"> | |
| <h2>Install This App</h2> | |
| <div class="dashboard-config"> | |
| <label for="dashboardUrl">Your Reachy Dashboard URL (when using the desktop Reachy Mini app):</label> | |
| <input type="url" id="dashboardUrl" value="http://localhost:8042" | |
| placeholder="http://your-reachy-ip:8000" /> | |
| </div> | |
| <button id="installBtn" class="install-btn primary"> | |
| <span class="btn-icon">📥</span> | |
| Install Reachy Mini Script App to Reachy Mini | |
| </button> | |
| <div id="installStatus" class="install-status"></div> | |
| </div> | |
| </div> | |
| <div class="footer"> | |
| <p> | |
| 📝 RMScript App • | |
| <a href="https://github.com/pollen-robotics" target="_blank">Pollen Robotics</a> • | |
| <a href="https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Apps" target="_blank">Browse More Apps</a> | |
| </p> | |
| </div> | |
| <script> | |
| function getCurrentSpaceUrl() { | |
| const url = window.location.href; | |
| // Match huggingface.co/spaces/user/repo format | |
| const match = url.match(/https:\/\/huggingface\.co\/spaces\/([^\/]+\/[^\/]+)/); | |
| if (match) { | |
| return `https://huggingface.co/spaces/${match[1]}`; | |
| } | |
| // Match user-repo.hf.space format | |
| if (url.includes('.hf.space')) { | |
| const hostMatch = url.match(/https?:\/\/([^\.]+)\.hf\.space/); | |
| if (hostMatch) { | |
| const spaceName = hostMatch[1].replace(/-/g, '/'); | |
| return `https://huggingface.co/spaces/${spaceName}`; | |
| } | |
| } | |
| return url; | |
| } | |
| async function parseTomlProjectName(tomlContent) { | |
| const nameMatch = tomlContent.match(/^name\s*=\s*["']([^"']+)["']/m); | |
| return nameMatch ? nameMatch[1] : null; | |
| } | |
| async function getAppNameFromCurrentSpace() { | |
| try { | |
| const spaceUrl = getCurrentSpaceUrl(); | |
| const tomlUrl = spaceUrl + '/raw/main/pyproject.toml'; | |
| const response = await fetch(tomlUrl); | |
| if (response.ok) { | |
| const tomlContent = await response.text(); | |
| return await parseTomlProjectName(tomlContent); | |
| } | |
| } catch (e) { | |
| console.error('Error fetching app name:', e); | |
| } | |
| return 'coding_lab'; | |
| } | |
| function showStatus(type, message) { | |
| const statusEl = document.getElementById('installStatus'); | |
| statusEl.className = `install-status ${type}`; | |
| statusEl.textContent = message; | |
| statusEl.style.display = 'block'; | |
| } | |
| async function installToReachy() { | |
| const btn = document.getElementById('installBtn'); | |
| const dashboardUrl = document.getElementById('dashboardUrl').value.replace(/\/$/, ''); | |
| btn.disabled = true; | |
| showStatus('loading', 'Connecting to Reachy dashboard...'); | |
| try { | |
| // Test connection using the correct endpoint | |
| const testResponse = await fetch(`${dashboardUrl}/api/apps/list-available`, { | |
| method: 'GET', | |
| mode: 'cors', | |
| }); | |
| if (!testResponse.ok) { | |
| throw new Error('Cannot connect to dashboard'); | |
| } | |
| showStatus('loading', 'Installing app...'); | |
| const spaceUrl = getCurrentSpaceUrl(); | |
| const appName = await getAppNameFromCurrentSpace(); | |
| // Use correct API format: AppInfo schema with name, source_kind, url | |
| const installResponse = await fetch(`${dashboardUrl}/api/apps/install`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| name: appName || 'coding_lab', | |
| source_kind: 'hf_space', | |
| url: spaceUrl, | |
| description: 'Coding Lab for programming Reachy Mini with RMScript language', | |
| }), | |
| mode: 'cors', | |
| }); | |
| if (installResponse.ok) { | |
| const result = await installResponse.json(); | |
| if (result.job_id) { | |
| showStatus('success', `Installation started! Job ID: ${result.job_id}. Check the dashboard for progress.`); | |
| } else { | |
| showStatus('success', `Successfully installed! Open the dashboard to run ${appName}.`); | |
| } | |
| } else { | |
| const error = await installResponse.text(); | |
| throw new Error(error || 'Installation failed'); | |
| } | |
| } catch (e) { | |
| showStatus('error', `Error: ${e.message}. Make sure the Reachy daemon is running.`); | |
| } finally { | |
| btn.disabled = false; | |
| } | |
| } | |
| document.getElementById('installBtn').addEventListener('click', installToReachy); | |
| </script> | |
| </body> | |
| </html> | |