| <!doctype html> |
| <html> |
|
|
| <head> |
| <meta charset="utf-8" /> |
| <meta name="viewport" content="width=device-width" /> |
| <title>Example App - Reachy Mini Template</title> |
| <link rel="stylesheet" href="style.css" /> |
| </head> |
|
|
| <body> |
| <div class="hero"> |
| <div class="hero-content"> |
| <div class="app-icon">π€β‘</div> |
| <h1>Example Reachy Mini App</h1> |
| <p class="tagline">Template for creating your own Reachy Mini applications</p> |
| </div> |
| </div> |
|
|
| <div class="container"> |
| <div class="main-card"> |
| <div class="app-preview"> |
| <div class="preview-image"> |
| <div class="camera-feed">π οΈ</div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="app-details"> |
| <h2>Example Template App</h2> |
| <div class="template-info"> |
| <div class="info-box"> |
| <h3>π¨ Template Purpose</h3> |
| <p>This is an example landing page for Reachy Mini apps. Feel free to duplicate this template and |
| customize it for your own applications!</p> |
| </div> |
| <div class="info-box"> |
| <h3>π Getting Started</h3> |
| <p>Use this template to showcase your Reachy Mini app with a landing page. Simply modify the |
| content, add your app's repository URL, and deploy!</p> |
| </div> |
| </div> |
|
|
|
|
| <div class="how-to-use"> |
| <h3>How to Use This Template</h3> |
| <div class="steps"> |
| <div class="step"> |
| <span class="step-number">1</span> |
| <div> |
| <h4>Duplicate & Customize</h4> |
| <p>Copy this template and modify the content for your app</p> |
| </div> |
| </div> |
| <div class="step"> |
| <span class="step-number">2</span> |
| <div> |
| <h4>Update Repository URL</h4> |
| <p>Change the JavaScript to point to your app's Git repository</p> |
| </div> |
| </div> |
| <div class="step"> |
| <span class="step-number">3</span> |
| <div> |
| <h4>Deploy to HF Spaces</h4> |
| <p>Upload your customized version to Hugging Face Spaces</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="download-section"> |
| <div class="download-card"> |
| <h2>Install This Example App</h2> |
| <p>Try out the installation process with this template app</p> |
|
|
| <div class="dashboard-config"> |
| <label for="dashboardUrl">Your Reachy Dashboard URL:</label> |
| <input type="url" id="dashboardUrl" value="http://localhost:8000" |
| placeholder="http://your-reachy-ip:8000" /> |
| </div> |
|
|
| <button id="installBtn" class="install-btn primary"> |
| <span class="btn-icon">π₯</span> |
| Install Example App to Reachy |
| </button> |
|
|
| <div id="installStatus" class="install-status"></div> |
|
|
| </div> |
| </div> |
|
|
| <div class="footer"> |
| <p> |
| π€ Template for Reachy Mini Apps β’ |
| <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> |
| </div> |
|
|
| <script> |
| |
| function getCurrentSpaceUrl() { |
| |
| const currentUrl = window.location.href; |
| |
| |
| const cleanUrl = currentUrl.split('?')[0].replace(/\/$/, ''); |
| |
| return cleanUrl; |
| } |
| |
| |
| function parseTomlProjectName(tomlContent) { |
| try { |
| const lines = tomlContent.split('\n'); |
| let inProjectSection = false; |
| |
| for (const line of lines) { |
| const trimmedLine = line.trim(); |
| |
| |
| if (trimmedLine === '[project]') { |
| inProjectSection = true; |
| continue; |
| } |
| |
| |
| if (trimmedLine.startsWith('[') && trimmedLine !== '[project]') { |
| inProjectSection = false; |
| continue; |
| } |
| |
| |
| if (inProjectSection && trimmedLine.startsWith('name')) { |
| const match = trimmedLine.match(/name\s*=\s*["']([^"']+)["']/); |
| if (match) { |
| |
| return match[1].toLowerCase().replace(/[^a-z0-9-_]/g, '-'); |
| } |
| } |
| } |
| |
| throw new Error('Project name not found in pyproject.toml'); |
| } catch (error) { |
| console.error('Error parsing pyproject.toml:', error); |
| return 'unknown-app'; |
| } |
| } |
| |
| |
| async function getAppNameFromCurrentSpace() { |
| try { |
| |
| const response = await fetch('./pyproject.toml'); |
| if (!response.ok) { |
| throw new Error(`Failed to fetch pyproject.toml: ${response.status}`); |
| } |
| |
| const tomlContent = await response.text(); |
| return parseTomlProjectName(tomlContent); |
| } catch (error) { |
| console.error('Error fetching app name from current space:', error); |
| |
| const url = getCurrentSpaceUrl(); |
| const parts = url.split('/'); |
| const spaceName = parts[parts.length - 1]; |
| return spaceName.toLowerCase().replace(/[^a-z0-9-_]/g, '-'); |
| } |
| } |
| |
| async function installToReachy() { |
| const dashboardUrl = document.getElementById('dashboardUrl').value.trim(); |
| const statusDiv = document.getElementById('installStatus'); |
| const installBtn = document.getElementById('installBtn'); |
| |
| if (!dashboardUrl) { |
| showStatus('error', 'Please enter your Reachy dashboard URL'); |
| return; |
| } |
| |
| try { |
| installBtn.disabled = true; |
| installBtn.innerHTML = '<span class="btn-icon">β³</span>Installing...'; |
| showStatus('loading', 'Connecting to your Reachy dashboard...'); |
| |
| |
| const testResponse = await fetch(`${dashboardUrl}/api/status`, { |
| method: 'GET', |
| mode: 'cors', |
| }); |
| |
| if (!testResponse.ok) { |
| throw new Error('Cannot connect to dashboard. Make sure the URL is correct and the dashboard is running.'); |
| } |
| |
| showStatus('loading', 'Reading app configuration...'); |
| |
| |
| const appName = await getAppNameFromCurrentSpace(); |
| |
| |
| const repoUrl = getCurrentSpaceUrl(); |
| |
| showStatus('loading', `Starting installation of "${appName}"...`); |
| |
| |
| const installResponse = await fetch(`${dashboardUrl}/api/install`, { |
| method: 'POST', |
| mode: 'cors', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| url: repoUrl, |
| name: appName |
| }) |
| }); |
| |
| const result = await installResponse.json(); |
| |
| if (installResponse.ok) { |
| showStatus('success', `β
Installation started for "${appName}"! Check your dashboard for progress.`); |
| setTimeout(() => { |
| showStatus('info', `Open your dashboard at ${dashboardUrl} to see the installed app.`); |
| }, 3000); |
| } else { |
| throw new Error(result.detail || 'Installation failed'); |
| } |
| |
| } catch (error) { |
| console.error('Installation error:', error); |
| showStatus('error', `β ${error.message}`); |
| } finally { |
| installBtn.disabled = false; |
| installBtn.innerHTML = '<span class="btn-icon">π₯</span>Install App to Reachy'; |
| } |
| } |
| |
| function showStatus(type, message) { |
| const statusDiv = document.getElementById('installStatus'); |
| statusDiv.className = `install-status ${type}`; |
| statusDiv.textContent = message; |
| statusDiv.style.display = 'block'; |
| } |
| |
| function copyToClipboard() { |
| const repoUrl = document.getElementById('repoUrl').textContent; |
| navigator.clipboard.writeText(repoUrl).then(() => { |
| showStatus('success', 'π Repository URL copied to clipboard!'); |
| }).catch(() => { |
| showStatus('error', 'Failed to copy URL. Please copy manually.'); |
| }); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| |
| const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'; |
| if (isLocalhost) { |
| document.getElementById('dashboardUrl').value = 'http://localhost:8000'; |
| } |
| |
| |
| const repoUrlElement = document.getElementById('repoUrl'); |
| if (repoUrlElement) { |
| repoUrlElement.textContent = getCurrentSpaceUrl(); |
| } |
| }); |
| |
| |
| document.getElementById('installBtn').addEventListener('click', installToReachy); |
| </script> |
| </body> |
|
|
| </html> |