8bitkick
Update install button text and enhance installation process with start app functionality
67364a3
<!DOCTYPE html>
<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>