| | <!doctype html> |
| | <html> |
| |
|
| | <head> |
| | <meta charset="utf-8" /> |
| | <meta name="viewport" content="width=device-width" /> |
| | <title> Reachy Mini Controller </title> |
| | <link rel="stylesheet" href="style.css" /> |
| | </head> |
| |
|
| | <body> |
| | <div class="hero"> |
| | <div class="hero-content"> |
| | <div class="app-icon">π€β‘</div> |
| | <h1> Reachy Mini Controller </h1> |
| | <p class="tagline">Enter your tagline here</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> |
| |
|
| | <div class="download-section"> |
| | <div class="download-card"> |
| | <h2>Install This App</h2> |
| |
|
| | <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 Reachy Mini Controller to Reachy Mini |
| | </button> |
| |
|
| | <div id="installStatus" class="install-status"></div> |
| |
|
| | </div> |
| | </div> |
| |
|
| | <div class="footer"> |
| | <p> |
| | π€ Reachy Mini Controller β’ |
| | <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> |