|
<!doctype html> |
|
<html> |
|
|
|
<head> |
|
<meta charset="utf-8" /> |
|
<meta name="viewport" content="width=device-width" /> |
|
<title>Reachy Mini Hand Tracking App</title> |
|
<link rel="stylesheet" href="style.css" /> |
|
</head> |
|
|
|
<body> |
|
<div class="hero"> |
|
<div class="hero-content"> |
|
<div class="app-icon">π€β‘</div> |
|
<h1>Reachy Mini Hand Tracking App</h1> |
|
<p class="tagline">Make Reachy Mini follow your hand!</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> |